@topogram/cli 0.3.78 → 0.3.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/package.json +2 -2
  3. package/src/agent-brief.js +29 -23
  4. package/src/agent-ops/query-builders/change-risk/{import-plan.js → extract-plan.js} +1 -1
  5. package/src/agent-ops/query-builders/change-risk/review-packets.js +5 -5
  6. package/src/agent-ops/query-builders/change-risk.js +1 -1
  7. package/src/agent-ops/query-builders/common.js +2 -2
  8. package/src/agent-ops/query-builders/multi-agent.js +1 -1
  9. package/src/agent-ops/query-builders/workflow-context-shared.js +4 -4
  10. package/src/catalog/provenance.js +1 -1
  11. package/src/cli/catalog-alias.d.ts +2 -0
  12. package/src/cli/catalog-alias.js +2 -2
  13. package/src/cli/command-parsers/core.js +9 -5
  14. package/src/cli/command-parsers/import.js +11 -17
  15. package/src/cli/command-parsers/project.js +0 -3
  16. package/src/cli/commands/catalog/copy.js +3 -3
  17. package/src/cli/commands/catalog/help.js +1 -2
  18. package/src/cli/commands/catalog/list.js +7 -4
  19. package/src/cli/commands/catalog/show.js +4 -4
  20. package/src/cli/commands/copy.js +356 -0
  21. package/src/cli/commands/doctor.js +1 -1
  22. package/src/cli/commands/import/adopt.js +9 -9
  23. package/src/cli/commands/import/check.js +15 -15
  24. package/src/cli/commands/import/diff.js +6 -6
  25. package/src/cli/commands/import/help.js +43 -34
  26. package/src/cli/commands/import/paths.js +3 -3
  27. package/src/cli/commands/import/plan.js +8 -8
  28. package/src/cli/commands/import/refresh.js +25 -24
  29. package/src/cli/commands/import/status-history.js +4 -4
  30. package/src/cli/commands/import/workspace.js +16 -16
  31. package/src/cli/commands/import-runner.js +6 -5
  32. package/src/cli/commands/import.js +4 -1
  33. package/src/cli/commands/init.js +67 -0
  34. package/src/cli/commands/query/{import-adopt.js → extract-adopt.js} +2 -2
  35. package/src/cli/commands/query/runner/change.js +2 -2
  36. package/src/cli/commands/query/runner/{import-adopt.js → extract-adopt.js} +9 -9
  37. package/src/cli/commands/query/runner/index.js +1 -1
  38. package/src/cli/commands/query/runner/workflow.js +7 -7
  39. package/src/cli/commands/query/workspace.js +4 -4
  40. package/src/cli/commands/release-status.js +2 -2
  41. package/src/cli/commands/source.js +2 -2
  42. package/src/cli/commands/template/check.js +2 -2
  43. package/src/cli/commands/template/list-show.js +4 -4
  44. package/src/cli/dispatcher.js +18 -3
  45. package/src/cli/help-dispatch.js +22 -8
  46. package/src/cli/help.js +68 -52
  47. package/src/cli/migration-guidance.js +9 -0
  48. package/src/generator/context/bundle.js +14 -7
  49. package/src/generator/context/diff.js +8 -1
  50. package/src/generator/context/digest.js +10 -1
  51. package/src/generator/context/shared/domain-sdlc.js +5 -1
  52. package/src/generator/context/shared/relationships.js +20 -5
  53. package/src/generator/context/shared/summaries.js +26 -0
  54. package/src/generator/context/shared.d.ts +1 -0
  55. package/src/generator/context/shared.js +1 -0
  56. package/src/generator/context/slice/core.js +9 -5
  57. package/src/generator/context/slice/sdlc.js +31 -2
  58. package/src/generator/context/task-mode.js +3 -3
  59. package/src/import/core/runner/reports.js +4 -4
  60. package/src/import/provenance.js +16 -16
  61. package/src/init-project.js +215 -0
  62. package/src/new-project/constants.js +1 -1
  63. package/src/new-project/create.js +2 -2
  64. package/src/new-project/project-files.js +7 -7
  65. package/src/reconcile/journeys.js +8 -3
  66. package/src/record-blocks.js +125 -0
  67. package/src/resolver/index.js +3 -0
  68. package/src/resolver/journeys.js +74 -0
  69. package/src/resolver/normalize.js +25 -0
  70. package/src/sdlc/adopt.js +1 -1
  71. package/src/validator/common.js +34 -1
  72. package/src/validator/index.js +4 -0
  73. package/src/validator/kinds.d.ts +2 -0
  74. package/src/validator/kinds.js +34 -1
  75. package/src/validator/per-kind/journey.js +233 -0
  76. package/src/workflows/docs-generate.js +4 -1
  77. package/src/workflows/reconcile/bundle-core/index.js +4 -2
  78. package/src/workflows/reconcile/canonical-surface.js +4 -1
  79. package/src/cli/commands/new.js +0 -94
@@ -76,7 +76,7 @@ export function readImportAdoptionArtifacts(inputPath) {
76
76
  reconcileReport: path.join(reconcileRoot, "report.json")
77
77
  };
78
78
  if (!fs.existsSync(paths.adoptionPlanAgent)) {
79
- throw new Error(`No import adoption plan found under '${reconcileRoot}'. Run 'topogram import <app-path> --out <target>' first.`);
79
+ throw new Error(`No extraction adoption plan found under '${reconcileRoot}'. Run 'topogram extract <app-path> --out <target>' first.`);
80
80
  }
81
81
  return {
82
82
  projectRoot,
@@ -182,10 +182,10 @@ export function summarizeImportAdoption(adoptionPlan, adoptionStatus, projectRoo
182
182
  risks: [
183
183
  ...(blockedCount > 0 ? [`${blockedCount} adoption item(s) are blocked.`] : []),
184
184
  ...(((adoptionPlan.requires_human_review || []).length || surfaces.some((/** @type {AnyRecord} */ item) => item.human_review_required))
185
- ? ["Imported proposal items require human review before adoption."]
185
+ ? ["Extracted proposal items require human review before adoption."]
186
186
  : [])
187
187
  ],
188
- nextCommand: nextBundle ? nextBundle.nextCommand : `topogram import status ${importProjectCommandPath(projectRoot)}`
188
+ nextCommand: nextBundle ? nextBundle.nextCommand : `topogram extract status ${importProjectCommandPath(projectRoot)}`
189
189
  };
190
190
  }
191
191
 
@@ -209,8 +209,8 @@ export function buildBrownfieldImportPlanPayload(inputPath) {
209
209
  },
210
210
  ...adoption,
211
211
  commands: {
212
- check: `topogram import check ${importProjectCommandPath(artifacts.projectRoot)}`,
213
- status: `topogram import status ${importProjectCommandPath(artifacts.projectRoot)}`,
212
+ check: `topogram extract check ${importProjectCommandPath(artifacts.projectRoot)}`,
213
+ status: `topogram extract status ${importProjectCommandPath(artifacts.projectRoot)}`,
214
214
  next: adoption.nextCommand
215
215
  }
216
216
  };
@@ -221,7 +221,7 @@ export function buildBrownfieldImportPlanPayload(inputPath) {
221
221
  * @returns {void}
222
222
  */
223
223
  export function printBrownfieldImportPlan(payload) {
224
- console.log(`Import adoption plan for ${payload.projectRoot}`);
224
+ console.log(`Extraction adoption plan for ${payload.projectRoot}`);
225
225
  console.log(`Proposal items: ${payload.summary.proposalItemCount}`);
226
226
  console.log(`Bundles: ${payload.summary.bundleCount}`);
227
227
  for (const bundle of payload.bundles) {
@@ -279,9 +279,9 @@ export function buildBrownfieldImportAdoptListPayload(inputPath) {
279
279
  * @returns {void}
280
280
  */
281
281
  export function printBrownfieldImportAdoptList(payload) {
282
- console.log(`Import adoption selectors for ${payload.projectRoot}`);
282
+ console.log(`Adoption selectors for ${payload.projectRoot}`);
283
283
  if (payload.selectors.length === 0) {
284
- console.log("No adoption selectors are available. Run `topogram import plan` to inspect reconcile artifacts.");
284
+ console.log("No adoption selectors are available. Run `topogram extract plan` to inspect reconcile artifacts.");
285
285
  return;
286
286
  }
287
287
  for (const selector of payload.selectors) {
@@ -34,12 +34,12 @@ import { verifyImportAdoptionReceipts } from "./status-history.js";
34
34
  export function readTopogramImportRecord(projectRoot) {
35
35
  const importPath = path.join(normalizeProjectRoot(projectRoot), TOPOGRAM_IMPORT_FILE);
36
36
  if (!fs.existsSync(importPath)) {
37
- throw new Error(`No brownfield import provenance found at '${importPath}'. Run 'topogram import <app-path> --out <target>' first.`);
37
+ throw new Error(`No brownfield extraction provenance found at '${importPath}'. Run 'topogram extract <app-path> --out <target>' first.`);
38
38
  }
39
39
  try {
40
40
  return { path: importPath, record: JSON.parse(fs.readFileSync(importPath, "utf8")) };
41
41
  } catch (error) {
42
- throw new Error(`Invalid brownfield import provenance JSON at '${importPath}'.`);
42
+ throw new Error(`Invalid brownfield extraction provenance JSON at '${importPath}'.`);
43
43
  }
44
44
  }
45
45
 
@@ -48,8 +48,9 @@ export function readTopogramImportRecord(projectRoot) {
48
48
  * @returns {string|null}
49
49
  */
50
50
  export function importTrackValueFromRecord(importRecord) {
51
- const tracks = Array.isArray(importRecord.import?.tracks)
52
- ? importRecord.import.tracks.map((/** @type {any} */ track) => String(track).trim()).filter(Boolean)
51
+ const source = importRecord.extract || importRecord.import || {};
52
+ const tracks = Array.isArray(source.tracks)
53
+ ? source.tracks.map((/** @type {any} */ track) => String(track).trim()).filter(Boolean)
53
54
  : [];
54
55
  return tracks.length ? [...new Set(tracks)].join(",") : null;
55
56
  }
@@ -283,7 +284,7 @@ export function buildBrownfieldImportRefreshAnalysis(inputPath, options = {}) {
283
284
  const projectRoot = normalizeProjectRoot(inputPath);
284
285
  const topogramRoot = normalizeTopogramPath(projectRoot);
285
286
  if (!fs.existsSync(topogramRoot) || !fs.statSync(topogramRoot).isDirectory()) {
286
- throw new Error(`No workspace folder found for imported workspace '${inputPath}'.`);
287
+ throw new Error(`No workspace folder found for extracted workspace '${inputPath}'.`);
287
288
  }
288
289
 
289
290
  const { record: importRecord } = readTopogramImportRecord(projectRoot);
@@ -291,21 +292,21 @@ export function buildBrownfieldImportRefreshAnalysis(inputPath, options = {}) {
291
292
  ? options.sourcePath
292
293
  : importRecord.source?.path;
293
294
  if (!sourcePath) {
294
- throw new Error("No brownfield source path was provided or recorded. Use 'topogram import refresh <workspace> --from <app-path>'.");
295
+ throw new Error("No brownfield source path was provided or recorded. Use 'topogram extract refresh <workspace> --from <app-path>'.");
295
296
  }
296
297
  const sourceRoot = path.resolve(sourcePath);
297
298
  if (!fs.existsSync(sourceRoot) || !fs.statSync(sourceRoot).isDirectory()) {
298
299
  throw new Error(`Cannot refresh from missing app directory '${sourcePath}'.`);
299
300
  }
300
301
  if (sourceRoot === projectRoot) {
301
- throw new Error("Refusing to refresh import from the imported Topogram workspace itself.");
302
+ throw new Error("Refusing to refresh extraction from the extracted Topogram workspace itself.");
302
303
  }
303
304
 
304
305
  const sourceComparison = compareImportRecordToSource(projectRoot, importRecord, sourceRoot);
305
306
  const trackValue = importTrackValueFromRecord(importRecord);
306
307
  const importResult = runWorkflow("import-app", sourceRoot, { from: trackValue });
307
308
  const candidateCounts = importCandidateCounts(importResult.summary);
308
- const candidateCountDeltas = buildCountDeltas(importRecord.import?.candidateCounts || {}, candidateCounts);
309
+ const candidateCountDeltas = buildCountDeltas((importRecord.extract || importRecord.import)?.candidateCounts || {}, candidateCounts);
309
310
  const removedCandidateFiles = {
310
311
  rawCandidateFiles: countFilesRecursive(path.join(topogramRoot, "candidates", "app")),
311
312
  reconcileFiles: countFilesRecursive(path.join(topogramRoot, "candidates", "reconcile"))
@@ -324,7 +325,7 @@ export function buildBrownfieldImportRefreshAnalysis(inputPath, options = {}) {
324
325
  topogramRoot,
325
326
  sourcePath: sourceRoot,
326
327
  provenancePath: path.join(projectRoot, TOPOGRAM_IMPORT_FILE),
327
- importedAt: importRecord.importedAt || null,
328
+ importedAt: importRecord.extractedAt || importRecord.importedAt || null,
328
329
  previousImportStatus: sourceComparison.status,
329
330
  sourceDiff: {
330
331
  status: sourceComparison.status,
@@ -354,13 +355,13 @@ export function buildBrownfieldImportRefreshAnalysis(inputPath, options = {}) {
354
355
  /**
355
356
  * @param {string} inputPath
356
357
  * @param {{ sourcePath?: string|null, dryRun?: boolean }} [options]
357
- * @returns {{ ok: boolean, dryRun: boolean, projectRoot: string, workspaceRoot: string, topogramRoot: string, sourcePath: string, provenancePath: string, previousImportStatus: string, currentImportStatus: string, tracks: string[], sourceFiles: number, sourceDiff: Record<string, any>, removedCandidateFiles: Record<string, number>, rawCandidateFiles: number, reconcileFiles: number, writtenFiles: string[], plannedFiles: string[], candidateCounts: Record<string, number>, candidateCountDeltas: Record<string, any>, adoptionPlanDeltas: Record<string, any>, receiptVerification: Record<string, any>, refreshMetadata: Record<string, any>|null, nextCommands: string[] }}
358
+ * @returns {{ ok: boolean, dryRun: boolean, projectRoot: string, workspaceRoot: string, topogramRoot: string, sourcePath: string, provenancePath: string, previousExtractStatus: string, currentExtractStatus: string, tracks: string[], sourceFiles: number, sourceDiff: Record<string, any>, removedCandidateFiles: Record<string, number>, rawCandidateFiles: number, reconcileFiles: number, writtenFiles: string[], plannedFiles: string[], candidateCounts: Record<string, number>, candidateCountDeltas: Record<string, any>, adoptionPlanDeltas: Record<string, any>, receiptVerification: Record<string, any>, refreshMetadata: Record<string, any>|null, nextCommands: string[] }}
358
359
  */
359
360
  export function buildBrownfieldImportRefreshPayload(inputPath, options = {}) {
360
361
  const analysis = buildBrownfieldImportRefreshAnalysis(inputPath, options);
361
362
  const dryRun = Boolean(options.dryRun);
362
363
  let provenancePath = analysis.provenancePath;
363
- let currentImportStatus = dryRun ? analysis.previousImportStatus : "unknown";
364
+ let currentExtractStatus = dryRun ? analysis.previousImportStatus : "unknown";
364
365
  /** @type {string[]} */
365
366
  let writtenFiles = [];
366
367
  /** @type {AnyRecord|null} */
@@ -391,7 +392,7 @@ export function buildBrownfieldImportRefreshPayload(inputPath, options = {}) {
391
392
  files: collectImportSourceFileRecords(analysis.sourcePath, { excludeRoots: [analysis.projectRoot] })
392
393
  });
393
394
  provenancePath = provenance.path;
394
- currentImportStatus = buildTopogramImportStatus(analysis.projectRoot).status;
395
+ currentExtractStatus = buildTopogramImportStatus(analysis.projectRoot).status;
395
396
  writtenFiles = [
396
397
  TOPOGRAM_IMPORT_FILE,
397
398
  ...rawCandidateFiles.map((filePath) => `${DEFAULT_TOPO_FOLDER_NAME}/${filePath}`),
@@ -402,15 +403,15 @@ export function buildBrownfieldImportRefreshPayload(inputPath, options = {}) {
402
403
  analysis.reconcileFiles = reconcileFiles.length;
403
404
  }
404
405
  return {
405
- ok: dryRun || currentImportStatus === "clean",
406
+ ok: dryRun || currentExtractStatus === "clean",
406
407
  dryRun,
407
408
  projectRoot: analysis.projectRoot,
408
409
  workspaceRoot: analysis.topogramRoot,
409
410
  topogramRoot: analysis.topogramRoot,
410
411
  sourcePath: analysis.sourcePath,
411
412
  provenancePath,
412
- previousImportStatus: analysis.previousImportStatus,
413
- currentImportStatus,
413
+ previousExtractStatus: analysis.previousImportStatus,
414
+ currentExtractStatus,
414
415
  tracks: analysis.tracks,
415
416
  sourceFiles: analysis.sourceFiles,
416
417
  sourceDiff: analysis.sourceDiff,
@@ -426,11 +427,11 @@ export function buildBrownfieldImportRefreshPayload(inputPath, options = {}) {
426
427
  refreshMetadata,
427
428
  nextCommands: [
428
429
  dryRun
429
- ? `topogram import refresh ${importProjectCommandPath(analysis.projectRoot)}`
430
- : `topogram import check ${importProjectCommandPath(analysis.projectRoot)}`,
431
- `topogram import plan ${importProjectCommandPath(analysis.projectRoot)}`,
432
- `topogram import status ${importProjectCommandPath(analysis.projectRoot)}`,
433
- `topogram import history ${importProjectCommandPath(analysis.projectRoot)} --verify`
430
+ ? `topogram extract refresh ${importProjectCommandPath(analysis.projectRoot)}`
431
+ : `topogram extract check ${importProjectCommandPath(analysis.projectRoot)}`,
432
+ `topogram extract plan ${importProjectCommandPath(analysis.projectRoot)}`,
433
+ `topogram extract status ${importProjectCommandPath(analysis.projectRoot)}`,
434
+ `topogram extract history ${importProjectCommandPath(analysis.projectRoot)} --verify`
434
435
  ]
435
436
  };
436
437
  }
@@ -440,12 +441,12 @@ export function buildBrownfieldImportRefreshPayload(inputPath, options = {}) {
440
441
  * @returns {void}
441
442
  */
442
443
  export function printBrownfieldImportRefresh(payload) {
443
- console.log(`${payload.dryRun ? "Previewed" : "Refreshed"} brownfield import candidates for ${payload.projectRoot}.`);
444
+ console.log(`${payload.dryRun ? "Previewed" : "Refreshed"} brownfield extraction candidates for ${payload.projectRoot}.`);
444
445
  console.log(`Source: ${payload.sourcePath}`);
445
446
  console.log(`Topogram: ${payload.topogramRoot}`);
446
- console.log(`Import provenance: ${payload.provenancePath}`);
447
- console.log(`Previous source status: ${payload.previousImportStatus}`);
448
- console.log(`Current source status: ${payload.currentImportStatus}`);
447
+ console.log(`Extraction provenance: ${payload.provenancePath}`);
448
+ console.log(`Previous source status: ${payload.previousExtractStatus}`);
449
+ console.log(`Current source status: ${payload.currentExtractStatus}`);
449
450
  console.log(`Source diff: changed=${payload.sourceDiff.counts.changed}, added=${payload.sourceDiff.counts.added}, removed=${payload.sourceDiff.counts.removed}`);
450
451
  console.log(`Tracked source files: ${payload.sourceFiles}`);
451
452
  console.log(`Raw candidate files: ${payload.rawCandidateFiles}`);
@@ -33,7 +33,7 @@ export function buildBrownfieldImportStatusPayload(inputPath) {
33
33
  projectRoot: artifacts.projectRoot,
34
34
  workspaceRoot: artifacts.topogramRoot,
35
35
  topogramRoot: artifacts.topogramRoot,
36
- import: importCheck.import,
36
+ extract: importCheck.extract,
37
37
  topogram: importCheck.topogram,
38
38
  adoption: {
39
39
  status: adoptionStatus,
@@ -52,7 +52,7 @@ export function buildBrownfieldImportStatusPayload(inputPath) {
52
52
  * @returns {void}
53
53
  */
54
54
  export function printBrownfieldImportStatus(payload) {
55
- console.log(`Import status: ${payload.import.status}`);
55
+ console.log(`Extraction status: ${payload.extract.status}`);
56
56
  console.log(`Topogram check: ${payload.topogram.ok ? "passed" : "failed"}`);
57
57
  console.log(`Adoption: ${payload.adoption.summary.appliedItemCount} applied, ${payload.adoption.summary.pendingItemCount} pending, ${payload.adoption.summary.blockedItemCount} blocked`);
58
58
  const next = payload.adoption.nextCommand;
@@ -134,7 +134,7 @@ export function verifyImportAdoptionReceipts(projectRoot, receipts) {
134
134
  summary,
135
135
  files,
136
136
  auditOnly: true,
137
- note: "History verification is audit-only. Imported/adopted Topogram files are project-owned, and edits do not make the workspace invalid."
137
+ note: "History verification is audit-only. Extracted/adopted Topogram files are project-owned, and edits do not make the workspace invalid."
138
138
  };
139
139
  }
140
140
 
@@ -175,7 +175,7 @@ export function buildBrownfieldImportHistoryPayload(inputPath, options = {}) {
175
175
  * @returns {void}
176
176
  */
177
177
  export function printBrownfieldImportHistory(payload) {
178
- console.log(`Import adoption history for ${payload.projectRoot}`);
178
+ console.log(`Adoption history for ${payload.projectRoot}`);
179
179
  console.log(`Receipts: ${payload.summary.receiptCount}`);
180
180
  console.log(`Forced writes: ${payload.summary.forcedWriteCount}`);
181
181
  if (!payload.exists) {
@@ -79,23 +79,23 @@ function importedProjectConfig() {
79
79
  */
80
80
  function importedWorkspaceReadme(sourceRoot, targetRoot, importSummary) {
81
81
  return [
82
- "# Imported Topogram Workspace",
82
+ "# Extracted Topogram Workspace",
83
83
  "",
84
- "This workspace was created from a brownfield app import.",
84
+ "This workspace was created from a brownfield app extraction.",
85
85
  "",
86
- `- Imported source: \`${sourceRoot}\``,
86
+ `- Extracted source: \`${sourceRoot}\``,
87
87
  `- Target workspace: \`${targetRoot}\``,
88
88
  `- Tracks: ${(importSummary.tracks || []).join(", ") || "none"}`,
89
89
  `- Provenance: \`${TOPOGRAM_IMPORT_FILE}\``,
90
90
  "",
91
- "Imported Topogram artifacts are project-owned after creation. Edit them directly, promote candidates deliberately, and run `topogram check` before generation or maintained-app work.",
91
+ "Extracted Topogram artifacts are project-owned after creation. Edit them directly, promote candidates deliberately, and run `topogram check` before generation or maintained-app work.",
92
92
  "",
93
93
  "Useful commands:",
94
94
  "",
95
95
  "```sh",
96
- "topogram import check",
96
+ "topogram extract check",
97
97
  "topogram check",
98
- `topogram query import-plan ./${DEFAULT_TOPO_FOLDER_NAME}`,
98
+ `topogram query extract-plan ./${DEFAULT_TOPO_FOLDER_NAME}`,
99
99
  "```",
100
100
  ""
101
101
  ].join("\n");
@@ -156,10 +156,10 @@ export function buildBrownfieldImportWorkspacePayload(sourcePath, targetPath, op
156
156
  const sourceRoot = path.resolve(sourcePath);
157
157
  const targetRoot = path.resolve(targetPath);
158
158
  if (!fs.existsSync(sourceRoot) || !fs.statSync(sourceRoot).isDirectory()) {
159
- throw new Error(`Cannot import missing app directory '${sourcePath}'.`);
159
+ throw new Error(`Cannot extract missing app directory '${sourcePath}'.`);
160
160
  }
161
161
  if (sourceRoot === targetRoot) {
162
- throw new Error("Refusing to import into the same directory as the brownfield app.");
162
+ throw new Error("Refusing to extract into the same directory as the brownfield app.");
163
163
  }
164
164
  ensureEmptyImportTarget(targetRoot);
165
165
 
@@ -206,12 +206,12 @@ export function buildBrownfieldImportWorkspacePayload(sourcePath, targetPath, op
206
206
  writtenFiles,
207
207
  candidateCounts,
208
208
  nextCommands: [
209
- "topogram import check",
210
- "topogram import plan",
211
- "topogram import adopt bundle:task --dry-run",
212
- "topogram import status",
209
+ "topogram extract check",
210
+ "topogram extract plan",
211
+ "topogram adopt bundle:task --dry-run",
212
+ "topogram extract status",
213
213
  "topogram check",
214
- `topogram query import-plan ./${DEFAULT_TOPO_FOLDER_NAME}`
214
+ `topogram query extract-plan ./${DEFAULT_TOPO_FOLDER_NAME}`
215
215
  ]
216
216
  };
217
217
  }
@@ -221,15 +221,15 @@ export function buildBrownfieldImportWorkspacePayload(sourcePath, targetPath, op
221
221
  * @returns {void}
222
222
  */
223
223
  export function printBrownfieldImportWorkspace(payload) {
224
- console.log(`Imported brownfield app to ${payload.targetPath}.`);
224
+ console.log(`Extracted brownfield app to ${payload.targetPath}.`);
225
225
  console.log(`Source: ${payload.sourcePath}`);
226
226
  console.log(`Topogram: ${payload.topogramRoot}`);
227
227
  console.log(`Project config: ${payload.projectConfigPath}`);
228
- console.log(`Import provenance: ${payload.provenancePath}`);
228
+ console.log(`Extraction provenance: ${payload.provenancePath}`);
229
229
  console.log(`Tracked source files: ${payload.sourceFiles}`);
230
230
  console.log(`Raw candidate files: ${payload.rawCandidateFiles}`);
231
231
  console.log(`Reconcile proposal files: ${payload.reconcileFiles}`);
232
- console.log("Imported Topogram artifacts are project-owned after creation; source hashes record the app evidence trusted at import time.");
232
+ console.log("Extracted Topogram artifacts are project-owned after creation; source hashes record the app evidence trusted at extraction time.");
233
233
  console.log("");
234
234
  console.log("Next steps:");
235
235
  console.log(` cd ${shellCommandArg(path.relative(process.cwd(), payload.targetPath) || ".")}`);
@@ -11,6 +11,7 @@ import {
11
11
  buildBrownfieldImportRefreshPayload,
12
12
  buildBrownfieldImportStatusPayload,
13
13
  buildBrownfieldImportWorkspacePayload,
14
+ printAdoptHelp,
14
15
  printBrownfieldImportAdopt,
15
16
  printBrownfieldImportAdoptList,
16
17
  printBrownfieldImportCheck,
@@ -20,7 +21,7 @@ import {
20
21
  printBrownfieldImportRefresh,
21
22
  printBrownfieldImportStatus,
22
23
  printBrownfieldImportWorkspace,
23
- printImportHelp
24
+ printExtractHelp
24
25
  } from "./import.js";
25
26
 
26
27
  /**
@@ -45,7 +46,7 @@ export function runImportCommand(context) {
45
46
  if (command === "workspace") {
46
47
  if (!outPath) {
47
48
  console.error("Missing required --out <target>.");
48
- printImportHelp();
49
+ printExtractHelp();
49
50
  return 1;
50
51
  }
51
52
  const payload = buildBrownfieldImportWorkspacePayload(inputPath || "", outPath, { from: fromValue });
@@ -110,12 +111,12 @@ export function runImportCommand(context) {
110
111
  if (command === "adopt") {
111
112
  if (!commandArgs.importAdoptSelector || commandArgs.importAdoptSelector.startsWith("-")) {
112
113
  console.error("Missing required <selector>.");
113
- printImportHelp();
114
+ printAdoptHelp();
114
115
  return 1;
115
116
  }
116
117
  if (write && dryRun) {
117
118
  console.error("Use either --dry-run or --write, not both.");
118
- printImportHelp();
119
+ printAdoptHelp();
119
120
  return 1;
120
121
  }
121
122
  const payload = buildBrownfieldImportAdoptPayload(commandArgs.importAdoptSelector, inputPath || ".", {
@@ -153,5 +154,5 @@ export function runImportCommand(context) {
153
154
  return payload.ok ? 0 : 1;
154
155
  }
155
156
 
156
- throw new Error(`Unknown import command '${command}'`);
157
+ throw new Error(`Unknown extract/adopt command '${command}'`);
157
158
  }
@@ -1,6 +1,9 @@
1
1
  // @ts-check
2
2
 
3
- export { printImportHelp } from "./import/help.js";
3
+ export {
4
+ printAdoptHelp,
5
+ printExtractHelp
6
+ } from "./import/help.js";
4
7
  export {
5
8
  buildBrownfieldImportWorkspacePayload,
6
9
  printBrownfieldImportWorkspace
@@ -0,0 +1,67 @@
1
+ // @ts-check
2
+
3
+ import path from "node:path";
4
+
5
+ import { stableStringify } from "../../format.js";
6
+ import { initTopogramProject } from "../../init-project.js";
7
+
8
+ /**
9
+ * @param {string} projectRoot
10
+ * @param {string} cwd
11
+ * @returns {string}
12
+ */
13
+ function displayProjectRoot(projectRoot, cwd) {
14
+ const relative = path.relative(cwd, projectRoot);
15
+ return !relative || relative.startsWith("..")
16
+ ? projectRoot
17
+ : relative.split(path.sep).join("/");
18
+ }
19
+
20
+ /**
21
+ * @param {ReturnType<typeof initTopogramProject>} result
22
+ * @param {string} cwd
23
+ * @returns {void}
24
+ */
25
+ export function printInitProjectResult(result, cwd) {
26
+ console.log(`Initialized Topogram workspace at ${result.projectRoot}.`);
27
+ console.log("Workspace: topo/");
28
+ console.log("Project config: topogram.project.json");
29
+ console.log("Output ownership: maintained (.)");
30
+ console.log("Template: none");
31
+ console.log("Generated app output: none");
32
+ console.log(`SDLC: ${result.sdlc.enabled ? "adopted/enforced" : "not adopted"}`);
33
+ if (result.created.length > 0) {
34
+ console.log(`Created: ${result.created.join(", ")}`);
35
+ }
36
+ if (result.skipped.length > 0) {
37
+ console.log(`Skipped existing files: ${result.skipped.join(", ")}`);
38
+ }
39
+ console.log("");
40
+ console.log("Next steps:");
41
+ console.log(` cd ${displayProjectRoot(result.projectRoot, cwd)}`);
42
+ console.log(" topogram agent brief --json");
43
+ console.log(" topogram check --json");
44
+ console.log(" topogram query list --json");
45
+ if (result.sdlc.enabled) {
46
+ console.log(" topogram sdlc policy explain --json");
47
+ console.log(" topogram sdlc prep commit . --json");
48
+ } else {
49
+ console.log(" topogram sdlc policy init .");
50
+ }
51
+ console.log(" topogram emit <target> ./topo --json");
52
+ }
53
+
54
+ /**
55
+ * @param {string} inputPath
56
+ * @param {{ json?: boolean, cwd?: string, withSdlc?: boolean }} [options]
57
+ * @returns {number}
58
+ */
59
+ export function runInitProjectCommand(inputPath, options = {}) {
60
+ const result = initTopogramProject({ targetPath: inputPath || ".", withSdlc: Boolean(options.withSdlc) });
61
+ if (options.json) {
62
+ console.log(stableStringify(result));
63
+ } else {
64
+ printInitProjectResult(result, options.cwd || process.cwd());
65
+ }
66
+ return 0;
67
+ }
@@ -38,7 +38,7 @@ export function buildImportPlanForContext(context, queryFamily) {
38
38
  }
39
39
  const adoptionPlan = readJson(planPath);
40
40
  const ast = parsePath(context.inputPath);
41
- const taskModeResult = buildTaskMode(ast, {}, "import-adopt");
41
+ const taskModeResult = buildTaskMode(ast, {}, "extract-adopt");
42
42
  if (!resultOk(taskModeResult)) {
43
43
  return taskModeResult;
44
44
  }
@@ -74,7 +74,7 @@ export function buildImportAdoptAgentContext(context, queryFamily) {
74
74
  const topogramRoot = normalizeTopogramPath(context.inputPath);
75
75
  const artifacts = requireReconcileArtifacts(topogramRoot, queryFamily);
76
76
  const ast = parsePath(context.inputPath);
77
- const taskModeResult = buildTaskMode(ast, {}, "import-adopt");
77
+ const taskModeResult = buildTaskMode(ast, {}, "extract-adopt");
78
78
  if (!resultOk(taskModeResult)) {
79
79
  return taskModeResult;
80
80
  }
@@ -14,7 +14,7 @@ import {
14
14
  } from "../../../../agent-ops/query-builders.js";
15
15
  import { parsePath } from "../../../../parser.js";
16
16
  import { buildChangePlanContext } from "../change-plan.js";
17
- import { buildImportPlanForContext } from "../import-adopt.js";
17
+ import { buildImportPlanForContext } from "../extract-adopt.js";
18
18
  import {
19
19
  adoptionPlanPath,
20
20
  buildTaskMode,
@@ -57,7 +57,7 @@ export function runChangeQuery(context) {
57
57
  maintainedRisk: built.importPlan.maintained_risk || null
58
58
  });
59
59
  return printJson(buildRiskSummaryPayload({
60
- source: "import-plan",
60
+ source: "extract-plan",
61
61
  risk,
62
62
  nextAction: built.importPlan.next_action || null,
63
63
  maintainedRisk: built.importPlan.maintained_risk || null
@@ -10,7 +10,7 @@ import {
10
10
  buildLaneStatusPayload,
11
11
  buildWorkPacketPayload
12
12
  } from "../../../../agent-ops/query-builders.js";
13
- import { buildImportAdoptAgentContext, buildImportPlanForContext } from "../import-adopt.js";
13
+ import { buildImportAdoptAgentContext, buildImportPlanForContext } from "../extract-adopt.js";
14
14
  import { normalizeTopogramPath, printValidationFailure, readJson, resultOk } from "../workspace.js";
15
15
  import { printJson } from "./output.js";
16
16
 
@@ -25,15 +25,15 @@ import { printJson } from "./output.js";
25
25
  export function runImportAdoptQuery(context) {
26
26
  const queryName = context.commandArgs?.queryName;
27
27
 
28
- if (queryName === "import-plan") {
29
- const built = buildImportPlanForContext(context, "import-plan");
28
+ if (queryName === "extract-plan") {
29
+ const built = buildImportPlanForContext(context, "extract-plan");
30
30
  if (!resultOk(built)) return printValidationFailure(built);
31
31
  return printJson(built.importPlan);
32
32
  }
33
33
 
34
34
  if (queryName === "multi-agent-plan") {
35
- if (context.modeId !== "import-adopt") {
36
- throw new Error("query multi-agent-plan currently supports only --mode import-adopt.");
35
+ if (context.modeId !== "extract-adopt") {
36
+ throw new Error("query multi-agent-plan currently supports only --mode extract-adopt.");
37
37
  }
38
38
  const built = buildImportAdoptAgentContext(context, "multi-agent-plan");
39
39
  if (!resultOk(built)) return printValidationFailure(built);
@@ -41,8 +41,8 @@ export function runImportAdoptQuery(context) {
41
41
  }
42
42
 
43
43
  if (queryName === "work-packet") {
44
- if (context.modeId !== "import-adopt") {
45
- throw new Error("query work-packet currently supports only --mode import-adopt.");
44
+ if (context.modeId !== "extract-adopt") {
45
+ throw new Error("query work-packet currently supports only --mode extract-adopt.");
46
46
  }
47
47
  if (!context.laneId) {
48
48
  throw new Error("query work-packet requires --lane <id>.");
@@ -57,8 +57,8 @@ export function runImportAdoptQuery(context) {
57
57
  }
58
58
 
59
59
  if (queryName === "lane-status" || queryName === "handoff-status") {
60
- if (context.modeId !== "import-adopt") {
61
- throw new Error(`query ${queryName} currently supports only --mode import-adopt.`);
60
+ if (context.modeId !== "extract-adopt") {
61
+ throw new Error(`query ${queryName} currently supports only --mode extract-adopt.`);
62
62
  }
63
63
  const built = buildImportAdoptAgentContext(context, queryName);
64
64
  if (!resultOk(built)) return printValidationFailure(built);
@@ -3,7 +3,7 @@
3
3
  import { runArtifactQuery } from "./artifacts.js";
4
4
  import { runBoundaryQuery } from "./boundaries.js";
5
5
  import { runChangeQuery } from "./change.js";
6
- import { runImportAdoptQuery } from "./import-adopt.js";
6
+ import { runImportAdoptQuery } from "./extract-adopt.js";
7
7
  import { runWorkflowQuery } from "./workflow.js";
8
8
 
9
9
  /**
@@ -46,7 +46,7 @@ export function runWorkflowQuery(context) {
46
46
  const selectors = selectorOptions(context);
47
47
 
48
48
  if (queryName === "next-action") {
49
- const result = buildTaskMode(parsePath(context.inputPath), selectors, context.modeId || "import-adopt", context.fromTopogramPath);
49
+ const result = buildTaskMode(parsePath(context.inputPath), selectors, context.modeId || "extract-adopt", context.fromTopogramPath);
50
50
  if (!resultOk(result)) return printValidationFailure(result);
51
51
  return printJson({
52
52
  type: "next_action_query",
@@ -72,13 +72,13 @@ export function runWorkflowQuery(context) {
72
72
 
73
73
  if (queryName === "workflow-preset-activation") {
74
74
  if (!context.modeId) {
75
- throw new Error("query workflow-preset-activation requires --mode <modeling|maintained-app-edit|import-adopt|diff-review|verification>.");
75
+ throw new Error("query workflow-preset-activation requires --mode <modeling|maintained-app-edit|extract-adopt|diff-review|verification>.");
76
76
  }
77
77
  const topogramRoot = normalizeTopogramPath(context.inputPath);
78
78
  const taskModeResult = buildTaskMode(parsePath(context.inputPath), selectors, context.modeId, context.fromTopogramPath);
79
79
  if (!resultOk(taskModeResult)) return printValidationFailure(taskModeResult);
80
80
  let importPlan = null;
81
- if (context.modeId === "import-adopt" && fs.existsSync(adoptionPlanPath(topogramRoot))) {
81
+ if (context.modeId === "extract-adopt" && fs.existsSync(adoptionPlanPath(topogramRoot))) {
82
82
  const workflowPresets = buildWorkflowPresetState({
83
83
  workspace: topogramRoot,
84
84
  selectors: workflowPresetSelectors(taskModeResult.artifact, context.providerId, context.presetId, "workflow-preset-activation")
@@ -118,7 +118,7 @@ export function runWorkflowQuery(context) {
118
118
  */
119
119
  function runSingleAgentPlan(context, selectors) {
120
120
  if (!context.modeId) {
121
- throw new Error("query single-agent-plan requires --mode <modeling|maintained-app-edit|import-adopt|diff-review|verification>.");
121
+ throw new Error("query single-agent-plan requires --mode <modeling|maintained-app-edit|extract-adopt|diff-review|verification>.");
122
122
  }
123
123
  const ast = parsePath(context.inputPath);
124
124
  const result = buildTaskMode(ast, selectors, context.modeId, context.fromTopogramPath);
@@ -134,7 +134,7 @@ function runSingleAgentPlan(context, selectors) {
134
134
  });
135
135
  const topogramRoot = normalizeTopogramPath(context.inputPath);
136
136
  let importPlan = null;
137
- if (context.modeId === "import-adopt" && fs.existsSync(adoptionPlanPath(topogramRoot))) {
137
+ if (context.modeId === "extract-adopt" && fs.existsSync(adoptionPlanPath(topogramRoot))) {
138
138
  const workflowPresets = buildWorkflowPresetState({
139
139
  workspace: topogramRoot,
140
140
  selectors: workflowPresetSelectors(result.artifact, context.providerId, context.presetId, "single-agent-plan")
@@ -163,7 +163,7 @@ function runSingleAgentPlan(context, selectors) {
163
163
  */
164
164
  function runResolvedWorkflowContext(context, selectors) {
165
165
  if (!context.modeId) {
166
- throw new Error("query resolved-workflow-context requires --mode <modeling|maintained-app-edit|import-adopt|diff-review|verification>.");
166
+ throw new Error("query resolved-workflow-context requires --mode <modeling|maintained-app-edit|extract-adopt|diff-review|verification>.");
167
167
  }
168
168
  const topogramRoot = normalizeTopogramPath(context.inputPath);
169
169
  const ast = parsePath(context.inputPath);
@@ -186,7 +186,7 @@ function runResolvedWorkflowContext(context, selectors) {
186
186
  maintainedBoundaryArtifact: maintainedBundleResult?.artifact?.maintained_boundary || null
187
187
  });
188
188
  let importPlan = null;
189
- if (context.modeId === "import-adopt" && fs.existsSync(adoptionPlanPath(topogramRoot))) {
189
+ if (context.modeId === "extract-adopt" && fs.existsSync(adoptionPlanPath(topogramRoot))) {
190
190
  const workflowPresets = buildWorkflowPresetState({
191
191
  workspace: topogramRoot,
192
192
  selectors: workflowPresetSelectors(taskModeResult.artifact, context.providerId, context.presetId, "resolved-workflow-context")
@@ -29,7 +29,7 @@ export function normalizeTopogramPath(inputPath) {
29
29
  */
30
30
  export function workflowPresetSelectors(taskModeArtifact, providerId = null, presetId = null, queryFamily = null) {
31
31
  const categories = [];
32
- if (taskModeArtifact?.mode === "import-adopt") categories.push("provider_adoption");
32
+ if (taskModeArtifact?.mode === "extract-adopt") categories.push("provider_adoption");
33
33
  if (taskModeArtifact?.mode === "maintained-app-edit") categories.push("maintained_app");
34
34
  if ((taskModeArtifact?.verification_targets?.maintained_app_checks || []).length > 0) categories.push("maintained_boundary");
35
35
  return {
@@ -65,7 +65,7 @@ export function generatorTargetsForWorkflowContext(options = {}) {
65
65
  * @returns {boolean}
66
66
  */
67
67
  export function importAdoptOnlyRequested(options = {}) {
68
- return options.modeId === "import-adopt" && !(
68
+ return options.modeId === "extract-adopt" && !(
69
69
  options.capabilityId ||
70
70
  options.workflowId ||
71
71
  options.projectionId ||
@@ -267,7 +267,7 @@ export function resolveRecommendedQueryFamily(nextAction, mode) {
267
267
  case "customize_workflow_preset":
268
268
  case "refresh_workflow_preset_customization":
269
269
  case "import_declared_workflow_preset":
270
- return "import-plan";
270
+ return "extract-plan";
271
271
  case "review_diff_impact":
272
272
  case "inspect_projection":
273
273
  case "inspect_diff":
@@ -284,7 +284,7 @@ export function resolveRecommendedQueryFamily(nextAction, mode) {
284
284
  default:
285
285
  break;
286
286
  }
287
- if (mode === "import-adopt") return "import-plan";
287
+ if (mode === "extract-adopt") return "extract-plan";
288
288
  if (mode === "maintained-app-edit") return "maintained-boundary";
289
289
  if (mode === "verification") return "verification-targets";
290
290
  return "change-plan";