From product
Orchestrates PostgreSQL to ScalarDB migration: schema extraction, analysis, and PL/pgSQL to Java conversion. Activate with /architect:migrate-postgresql.
How this skill is triggered — by the user, by Claude, or both
Slash command
/product:migrate-postgresqlsonnetThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Orchestrates the complete PostgreSQL to ScalarDB migration workflow through an interactive chat interface. Collects database connection parameters from the user via questions, updates the configuration file, then runs the analysis and migration skills.
analyze-postgresql-schema/analyze-postgresql-dbms_report.mdanalyze-postgresql-schema/scripts/postgresql_db_extractor.pymigrate-postgresql-sp-trigger-to-scalardb/reference/migration-strategy-guide-sp-triggers-to-scalardb.mdmigrate-postgresql-sp-trigger-to-scalardb/templates/scalardb_sp_migration_report.mdmigrate-postgresql-to-scalardb/reference/scalardb_reference.mdmigrate-postgresql-to-scalardb/templates/scalardb_migration_analysis.mdmigrate-postgresql-to-scalardb/templates/scalardb_migration_steps.mdOrchestrates the complete PostgreSQL to ScalarDB migration workflow through an interactive chat interface. Collects database connection parameters from the user via questions, updates the configuration file, then runs the analysis and migration skills.
You MUST follow these steps exactly in order. Do NOT skip any step.
Run the following Bash command to get the plugin installation directory:
echo "$CLAUDE_PLUGIN_ROOT"
PLUGIN_ROOT to that value.find ~/.claude/plugins -name "plugin.json" -path "*/architect/*" 2>/dev/null | head -1 | xargs -I{} dirname {} | xargs -I{} dirname {}
Store the result as PLUGIN_ROOT. All subagent template paths in Steps 7–11 use this variable (e.g., PLUGIN_ROOT/skills/common/subagents/postgresql/0-test-connection.md).
First, attempt to read the current configuration file:
Read file: .claude/configuration/databases.env
Two possible outcomes:
A) File EXISTS → Set CONFIG_EXISTS = true
B) File DOES NOT EXIST → Set CONFIG_EXISTS = false
mkdir -p .claude/configurationBuild the questions based on CONFIG_EXISTS:
If CONFIG_EXISTS = true: Include "Keep current" options with actual values in descriptions (e.g., "Keep current" with description "Keep: localhost").
If CONFIG_EXISTS = false: Replace "Keep current" options with additional useful defaults instead. Do NOT offer "Keep current" since there is nothing to keep.
Use the AskUserQuestion tool:
{
"questions": [
{
"question": "What is the PostgreSQL database host?",
"header": "Host",
"options": [
{"label": "localhost", "description": "Database running on local machine"},
CONFIG_EXISTS ? {"label": "Keep current", "description": "Keep: <CURRENT_POSTGRES_HOST>"}
: {"label": "127.0.0.1", "description": "Loopback IP address"}
],
"multiSelect": false
},
{
"question": "What is the PostgreSQL port?",
"header": "Port",
"options": [
{"label": "5432 (Default)", "description": "Standard PostgreSQL port"},
CONFIG_EXISTS ? {"label": "Keep current", "description": "Keep: <CURRENT_POSTGRES_PORT>"}
: {"label": "5433", "description": "Alternative PostgreSQL port"}
],
"multiSelect": false
},
{
"question": "What is the PostgreSQL database name to analyze?",
"header": "Database",
"options": [
{"label": "postgres", "description": "Default PostgreSQL database"},
CONFIG_EXISTS ? {"label": "Keep current", "description": "Keep: <CURRENT_POSTGRES_DATABASE>"}
: {"label": "template1", "description": "PostgreSQL template database"}
],
"multiSelect": false
},
{
"question": "What is the PostgreSQL username?",
"header": "Username",
"options": [
{"label": "postgres", "description": "Default PostgreSQL superuser"},
CONFIG_EXISTS ? {"label": "Keep current", "description": "Keep: <CURRENT_POSTGRES_USER>"}
: {"label": "admin", "description": "Common admin username"}
],
"multiSelect": false
}
]
}
Note: The pseudo-code CONFIG_EXISTS ? ... : ... means you must construct the actual JSON dynamically based on whether the config file existed. Replace <CURRENT_*> placeholders with actual values read from the file.
Save all responses. For "Keep current" responses, use the existing values from the config file. For "Other" responses, use the custom text the user typed.
Use the AskUserQuestion tool, again adapting based on CONFIG_EXISTS:
{
"questions": [
{
"question": "What is the database password? (Select 'Other' to type your password)",
"header": "Password",
"options": [
CONFIG_EXISTS ? {"label": "Keep current", "description": "Use the password already in config"}
: {"label": "No password", "description": "Connect without a password (empty)"},
CONFIG_EXISTS ? {"label": "No password", "description": "Connect without a password (empty)"}
: {"label": "Type below", "description": "Select 'Other' to enter your password"}
],
"multiSelect": false
},
{
"question": "Which PostgreSQL schema to analyze?",
"header": "Schema",
"options": [
{"label": "public (Default)", "description": "Default PostgreSQL schema"},
CONFIG_EXISTS ? {"label": "Keep current", "description": "Keep: <CURRENT_POSTGRES_SCHEMA or '(empty)'>"}
: {"label": "pg_catalog", "description": "PostgreSQL system catalog schema"}
],
"multiSelect": false
},
{
"question": "Include PL/pgSQL function and procedure source code in the report?",
"header": "PL/pgSQL",
"options": [
{"label": "Yes (Recommended)", "description": "Include PL/pgSQL source code for migration analysis"},
{"label": "No", "description": "Skip source code - faster analysis, smaller report"}
],
"multiSelect": false
},
{
"question": "Where should output files be saved? (Must be an absolute path)",
"header": "Output Dir",
"options": [
{"label": "Default (.claude/output)", "description": "Use the project's .claude/output directory"},
CONFIG_EXISTS ? {"label": "Keep current", "description": "Keep: <CURRENT_OUTPUT_DIR>"}
: {"label": "Custom path", "description": "Select 'Other' to type a custom absolute path"}
],
"multiSelect": false
}
]
}
Save all responses.
Process the collected answers into configuration values using these rules:
| Parameter | Response Mapping |
|---|---|
| POSTGRES_HOST | "localhost" -> localhost, "127.0.0.1" -> 127.0.0.1, "Keep current" -> keep existing, "Other" -> user's typed value |
| POSTGRES_PORT | "5432 (Default)" -> 5432, "5433" -> 5433, "Keep current" -> keep existing, "Other" -> user's typed value |
| POSTGRES_DATABASE | "postgres" -> postgres, "template1" -> template1, "Keep current" -> keep existing, "Other" -> user's typed value |
| POSTGRES_USER | "postgres" -> postgres, "admin" -> admin, "Keep current" -> keep existing, "Other" -> user's typed value |
| POSTGRES_PASSWORD | "Keep current" -> keep existing, "No password" -> empty string, "Type below" -> user must use "Other", "Other" -> user's typed value |
| POSTGRES_SCHEMA | "public (Default)" -> public, "pg_catalog" -> pg_catalog, "Keep current" -> keep existing, "Other" -> user's typed value |
| POSTGRES_INCLUDE_PLPGSQL_SOURCE | "Yes (Recommended)" -> true, "No" -> false |
| OUTPUT_DIR | "Default (.claude/output)" -> absolute path to project's .claude/output directory, "Keep current" -> keep existing, "Custom path" -> user must use "Other", "Other" -> user's typed value |
Also set: ACTIVE_DATABASE=postgresql
If CONFIG_EXISTS = false (first run):
Use the Write tool to create .claude/configuration/databases.env with the complete template populated with collected values:
# =============================================================================
# CONSOLIDATED DATABASE CONFIGURATION
# =============================================================================
# Single configuration file for all database migration skills
# =============================================================================
# ACTIVE DATABASE SELECTION
ACTIVE_DATABASE=postgresql
# SHARED OUTPUT CONFIGURATION (ABSOLUTE PATH REQUIRED)
OUTPUT_DIR=<collected_output_dir>
# ScalarDB target version
SCALARDB_TARGET_VERSION=3.17
# =============================================================================
# POSTGRESQL CONFIGURATION
# =============================================================================
POSTGRES_HOST=<collected_host>
POSTGRES_PORT=<collected_port>
POSTGRES_DATABASE=<collected_database>
POSTGRES_USER=<collected_user>
POSTGRES_PASSWORD=<collected_password>
POSTGRES_SCHEMA=<collected_schema>
POSTGRES_REPORT_FILENAME=postgresql_schema_report.md
POSTGRES_SCALARDB_NAMESPACE=<lowercase of collected_database or collected_schema>
POSTGRES_INCLUDE_PLPGSQL_SOURCE=<collected_plpgsql_source>
POSTGRES_PSQL_PATH=
POSTGRES_CONNECTION_TIMEOUT=30
POSTGRES_QUERY_TIMEOUT=300
# =============================================================================
# MYSQL CONFIGURATION (defaults - not yet configured)
# =============================================================================
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_DATABASE=your_database
MYSQL_USER=your_username
MYSQL_PASSWORD=your_password
MYSQL_REPORT_FILENAME=mysql_schema_report.md
MYSQL_SCALARDB_NAMESPACE=
MYSQL_INCLUDE_SOURCE=false
MYSQL_CHARSET=utf8mb4
MYSQL_CONNECTION_TIMEOUT=30
# =============================================================================
# ORACLE CONFIGURATION (defaults - not yet configured)
# =============================================================================
ORACLE_HOST=localhost
ORACLE_PORT=1521
ORACLE_SERVICE=ORCL
ORACLE_USER=your_username
ORACLE_PASSWORD=your_password
ORACLE_SCHEMA=
ORACLE_REPORT_FILENAME=oracle_schema_report.md
ORACLE_SCALARDB_NAMESPACE=
ORACLE_INCLUDE_PLSQL_SOURCE=false
ORACLE_SQLPLUS_PATH=
ORACLE_HOME=
ORACLE_TNS_ADMIN=
# =============================================================================
# END OF CONFIGURATION
# =============================================================================
If CONFIG_EXISTS = true (updating existing):
Use the Edit tool to update .claude/configuration/databases.env with the collected values:
ACTIVE_DATABASE=postgresqlOUTPUT_DIR if changedPOSTGRES_* parameters with the mapped values from Step 4After writing/updating, display a confirmation summary to the user:
Configuration <Created / Updated>:
Host: <value>
Port: <value>
Database: <value>
User: <value>
Password: ******** (hidden)
Schema: <value>
PL/pgSQL: <true/false>
Output: <path>
Use Bash to create the output directory if it doesn't exist:
mkdir -p <OUTPUT_DIR>
Spawn a Bash subagent using the Task tool to test the PostgreSQL database connection via the external API.
${PLUGIN_ROOT}/skills/common/subagents/postgresql/0-test-connection.md<POSTGRES_HOST>, <POSTGRES_PORT>, <POSTGRES_DATABASE>, <POSTGRES_USER>, <POSTGRES_PASSWORD>, and <OUTPUT_DIR> with the actual values from Steps 4-5subagent_type: "Bash", description: "Test PostgreSQL connection", and the substituted promptAfter the subagent completes:
DURATION_SECONDS from the subagent's response → store as S0_DURATIONtotal_tokens from the <usage> block in the Task result (if present) → store as S0_TOKENSCheck the subagent result:
pg_hba.conf allows connections, ensure PostgreSQL server is running). STOP HERE — do NOT proceed to Step 8, 9, or 10.Spawn a Bash subagent using the Task tool to run the Python extractor script.
${PLUGIN_ROOT}/skills/common/subagents/postgresql/1-extract-schema.md<INCLUDE_SOURCE_FLAG> based on POSTGRES_INCLUDE_PLPGSQL_SOURCE from Step 4)subagent_type: "Bash", description: "Extract PostgreSQL schema", and the substituted promptAfter the subagent completes:
DURATION_SECONDS from the subagent's response → store as S1_DURATIONtotal_tokens from the <usage> block in the Task result (if present) → store as S1_TOKENSCheck the subagent result:
Spawn a general-purpose subagent using the Task tool to generate the schema report from the extracted JSON.
${PLUGIN_ROOT}/skills/common/subagents/postgresql/2-generate-report.md<OUTPUT_DIR> with the actual absolute output directory path from Step 4)subagent_type: "general-purpose", description: "Generate PostgreSQL schema report", and the substituted promptAfter the subagent completes:
DURATION_SECONDS from the subagent's response → store as S2_DURATIONtotal_tokens from the <usage> block in the Task result (if present) → store as S2_TOKENSCheck the subagent result:
raw_schema_data.json is still available for manual inspection. STOP HERE — do NOT proceed to Step 10.Both subagents run simultaneously in a single message — send both Task tool calls together in one response. They share the same inputs (postgresql_schema_report.md and raw_schema_data.json) and have no dependency on each other's output.
Preparation:
${PLUGIN_ROOT}/skills/common/subagents/postgresql/3-migration-analysis.md
<OUTPUT_DIR> with the actual absolute output directory path from Step 4${PLUGIN_ROOT}/skills/common/subagents/postgresql/4-sp-trigger-migration.md
<OUTPUT_DIR> with the actual absolute output directory path from Step 4Spawn both in one message:
subagent_type: "general-purpose", description: "Generate PostgreSQL migration docs", substituted prompt from 3-migration-analysis.mdsubagent_type: "general-purpose", description: "Generate SP & trigger migration code", substituted prompt from 4-sp-trigger-migration.mdAfter both subagents complete:
DURATION_SECONDS → store as S3_DURATION; extract total_tokens from <usage> block → store as S3_TOKENSDURATION_SECONDS → store as S4_DURATION; extract total_tokens from <usage> block → store as S4_TOKENSS34_WALL = max(S3_DURATION, S4_DURATION)Error cascading rules:
Check results and proceed to Step 11.
Display the combined results from all five subagents, then a timing and token usage table.
Compute totals before rendering:
TOTAL_DURATION = S0_DURATION + S1_DURATION + S2_DURATION + S34_WALL
(Phases 3 & 4 ran in parallel, so only the longer one adds to wall-clock time)TOTAL_TOKENS = S0_TOKENS + S1_TOKENS + S2_TOKENS + S3_TOKENS + S4_TOKENS
(If any token value is unavailable, mark it as "N/A" and omit it from the total)Display to the user:
PostgreSQL to ScalarDB Migration — Complete
Phase 0: Connection Test
- Connection method: Python psycopg2 (direct database connection)
- <Subagent 0 SUMMARY line (database product and version)>
Phase 1: Schema Extraction
- Connected to PostgreSQL at <host>:<port>/<database>
- <Subagent 1 SUMMARY line>
Phase 2: Schema Report
- Generated: postgresql_schema_report.md
- <Subagent 2 SUMMARY lines>
Phase 3 + 4 (Parallel):
Migration Analysis:
- Generated: scalardb_migration_analysis.md
- Generated: scalardb_migration_steps.md
- Migration Complexity: <Subagent 3 COMPLEXITY_SCORE>
- <Subagent 3 SUMMARY lines>
SP & Trigger Migration:
- Generated: scalardb_sp_migration_report.md
- Java files: <OUTPUT_DIR>/generated-java/
- Files generated: <Subagent 4 FILES_GENERATED>
- <Subagent 4 SUMMARY lines>
Output Directory: <OUTPUT_DIR>
Next Steps:
1. Review scalardb_migration_analysis.md for compatibility details
2. Follow scalardb_migration_steps.md for implementation guide
3. Review generated-java/ for migrated stored procedure code
4. Review scalardb_sp_migration_report.md for SP & trigger migration details
Then display the metrics table:
Execution Summary
─────────────────────────────────────────────────────────────────────
Phase │ Subagent Type │ Tokens │ Time
─────────────────────────────────────────────────────────────────────
Phase 0: Connection Test │ Bash │ S0_TOK │ S0s
Phase 1: Schema Extraction │ Bash │ S1_TOK │ S1s
Phase 2: Schema Report │ General-purpose │ S2_TOK │ S2s
Phase 3: Migration Analysis ┐ │ General-purpose │ S3_TOK │ S3s
Phase 4: SP/Trigger Migration┘ │ General-purpose │ S4_TOK │ S4s
(parallel wall-clock) │ S34s
─────────────────────────────────────────────────────────────────────
TOTAL │ 5 subagents │ TOT_TOK │ TOTs
─────────────────────────────────────────────────────────────────────
Note: Phases 3 & 4 ran in parallel. Total time reflects wall-clock
(sequential sum of Phases 0–2 plus the longer of Phases 3/4).
Token counts extracted from <usage> blocks; N/A if unavailable.
pg_hba.conf, ensure PostgreSQL server is running), STOP (do not spawn Subagent 1, 2, 3, or 4)raw_schema_data.json is available on disk, STOP (do not spawn Subagent 3 or 4 — both need postgresql_schema_report.md)Partial outputs are always preserved — if extraction succeeds but later steps fail, earlier output files remain on disk for manual inspection.
This command uses 5 subagents — 3 sequential then 2 in parallel — to isolate heavy processing from the main conversation:
Step 7 ──► Subagent 0 (Bash) Connection Test
Step 8 ──► Subagent 1 (Bash) Schema Extraction
Step 9 ──► Subagent 2 (General-purpose) Schema Report
│
Step 10 ──► ─┼─► Subagent 3 (General-purpose) Migration Analysis ─┐
└─► Subagent 4 (General-purpose) SP & Trigger Migration ─┤ (parallel)
│
Step 11 ◄──────────────────────────────────────────────────────────── ┘
Display final summary + metrics table
| Subagent | Step | Type | Purpose | Returns |
|---|---|---|---|---|
| 0 | 7 | Bash | POST to test-postgres-connection API to verify DB is reachable | SUCCESS/FAILURE + DB version + DURATION_SECONDS |
| 1 | 8 | Bash | Run postgresql_db_extractor.py | SUCCESS/FAILURE + file path + DURATION_SECONDS |
| 2 | 9 | General-purpose | Generate postgresql_schema_report.md from JSON + template | Executive summary + DURATION_SECONDS |
| 3 | 10 ┐ | General-purpose | Generate migration analysis + steps from schema report + reference docs | Complexity score + findings + DURATION_SECONDS |
| 4 | 10 ┘ | General-purpose | Generate Java code from PL/pgSQL + SP & trigger migration report | Files generated + complexity + DURATION_SECONDS |
Parallelism: Subagents 3 and 4 are spawned simultaneously in Step 10 (single message, two Task calls). Both read from postgresql_schema_report.md and raw_schema_data.json; neither depends on the other.
Metrics tracking: After each Task call, extract total_tokens and duration_ms from the <usage> block in the result, and DURATION_SECONDS from the subagent's self-reported output. Use these to populate the Step 11 summary table.
The SKILL.md files are read directly by subagents as instruction documents (the Skill tool is not invoked).
${PLUGIN_ROOT}/skills/common/subagents/postgresql/ (5 prompt templates)${PLUGIN_ROOT}/skills/migrate-postgresql/analyze-postgresql-schema/SKILL.md${PLUGIN_ROOT}/skills/migrate-postgresql/analyze-postgresql-schema/analyze-postgresql-dbms_report.md${PLUGIN_ROOT}/skills/migrate-postgresql/analyze-postgresql-schema/scripts/postgresql_db_extractor.py${PLUGIN_ROOT}/skills/migrate-postgresql/migrate-postgresql-to-scalardb/SKILL.md${PLUGIN_ROOT}/skills/migrate-postgresql/migrate-postgresql-to-scalardb/templates/${PLUGIN_ROOT}/skills/migrate-postgresql/migrate-postgresql-to-scalardb/reference/scalardb_reference.md${PLUGIN_ROOT}/skills/migrate-postgresql/migrate-postgresql-sp-trigger-to-scalardb/SKILL.md${PLUGIN_ROOT}/skills/migrate-postgresql/migrate-postgresql-sp-trigger-to-scalardb/reference/migration-strategy-guide-sp-triggers-to-scalardb.md${PLUGIN_ROOT}/skills/migrate-postgresql/migrate-postgresql-sp-trigger-to-scalardb/templates/scalardb_sp_migration_report.md.claude/configuration/databases.envnpx claudepluginhub wfukatsu/nexus-architect --plugin scalardbOrchestrates MySQL to ScalarDB migration: schema extraction, migration analysis, and stored procedure/trigger to Java conversion. Interactive chat collects DB connection params, updates config, then runs analysis and migration skills.
Creates production-ready SQL migration scripts with zero-downtime strategies for PostgreSQL, MySQL, and SQL Server. Includes rollback, validation, and monitoring.
Creates zero-downtime SQL migration scripts with rollback procedures, validation checks, and performance optimization for PostgreSQL, MySQL, and SQL Server.