@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
@@ -0,0 +1,356 @@
1
+ // @ts-check
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ import {
7
+ catalogEntryPackageSpec,
8
+ copyCatalogTopogramEntry,
9
+ findCatalogEntry,
10
+ loadCatalog,
11
+ TOPOGRAM_SOURCE_FILE
12
+ } from "../../catalog.js";
13
+ import { GENERATOR_POLICY_FILE } from "../../generator-policy.js";
14
+ import { createNewProject } from "../../new-project.js";
15
+ import { copyPath, ensureEmptyDirectory } from "../../catalog/files.js";
16
+ import { writeTopogramSourceRecord } from "../../catalog/provenance.js";
17
+ import { DEFAULT_TOPO_FOLDER_NAME, DEFAULT_WORKSPACE_PATH, resolvePackageWorkspace } from "../../workspace-paths.js";
18
+ import { formatCatalogTemplateAliasError, suggestCatalogTemplateIds } from "../catalog-alias.js";
19
+ import {
20
+ buildCatalogListPayload,
21
+ printCatalogList
22
+ } from "./catalog/list.js";
23
+ import { shellCommandArg } from "./catalog/shared.js";
24
+ import { stableStringify } from "../../format.js";
25
+
26
+ const ENGINE_ROOT = path.resolve(decodeURIComponent(new URL("../../../", import.meta.url).pathname));
27
+ const TEMPLATES_ROOT = path.join(ENGINE_ROOT, "templates");
28
+
29
+ /**
30
+ * @param {string} source
31
+ * @returns {boolean}
32
+ */
33
+ function isCatalogIdCandidate(source) {
34
+ return Boolean(source) &&
35
+ !source.startsWith("@") &&
36
+ !source.startsWith("./") &&
37
+ !source.startsWith("../") &&
38
+ !path.isAbsolute(source) &&
39
+ !source.includes("/") &&
40
+ !source.endsWith(".tgz");
41
+ }
42
+
43
+ /**
44
+ * @param {ReturnType<typeof createNewProject>} result
45
+ * @param {string} cwd
46
+ * @returns {string}
47
+ */
48
+ function displayProjectRootForCopy(result, cwd) {
49
+ const relativeProjectRoot = path.relative(cwd, result.projectRoot);
50
+ return !relativeProjectRoot || relativeProjectRoot.startsWith("..")
51
+ ? result.projectRoot
52
+ : relativeProjectRoot;
53
+ }
54
+
55
+ /**
56
+ * @param {string} targetPath
57
+ * @returns {void}
58
+ */
59
+ function assertProjectTargetOutsideEngine(targetPath) {
60
+ const projectRoot = path.resolve(targetPath);
61
+ const relativeToEngine = path.relative(ENGINE_ROOT, projectRoot);
62
+ if (relativeToEngine === "" || (!relativeToEngine.startsWith("..") && !path.isAbsolute(relativeToEngine))) {
63
+ throw new Error(
64
+ `Refusing to copy a project inside the engine directory. Use a path outside engine, for example '../${path.basename(projectRoot)}'.`
65
+ );
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @param {string} source
71
+ * @param {string} targetPath
72
+ * @param {{ catalogSource?: string|null, version?: string|null }} options
73
+ * @returns {{ ok: boolean, action: "copy_topogram", source: string, id: string, kind: "topogram", packageSpec: string, targetPath: string, provenancePath: string, files: string[], diagnostics: any[], errors: string[] }|null}
74
+ */
75
+ function tryCopyCatalogTopogram(source, targetPath, options) {
76
+ if (!isCatalogIdCandidate(source)) {
77
+ return null;
78
+ }
79
+ const loaded = loadCatalog(options.catalogSource || null);
80
+ const entry = findCatalogEntry(loaded.catalog, source, "topogram");
81
+ if (!entry) {
82
+ return null;
83
+ }
84
+ const copied = copyCatalogTopogramEntry(entry, targetPath, {
85
+ catalogSource: loaded.source,
86
+ version: options.version || null
87
+ });
88
+ return {
89
+ action: "copy_topogram",
90
+ source: loaded.source,
91
+ ...copied,
92
+ diagnostics: [],
93
+ errors: []
94
+ };
95
+ }
96
+
97
+ /**
98
+ * @param {string} source
99
+ * @param {string} targetPath
100
+ * @param {{ catalogSource?: string|null, version?: string|null }} options
101
+ * @returns {{ templateName: string, provenance: any|null }}
102
+ */
103
+ function resolveCopyTemplateSource(source, targetPath, options) {
104
+ void targetPath;
105
+ if (!isCatalogIdCandidate(source)) {
106
+ return { templateName: source, provenance: null };
107
+ }
108
+ const loaded = loadCatalog(options.catalogSource || null);
109
+ const entry = findCatalogEntry(loaded.catalog, source, "template");
110
+ if (!entry) {
111
+ throw new Error(formatCatalogTemplateAliasError(source, loaded.source, null, {
112
+ suggestions: suggestCatalogTemplateIds(loaded.catalog, source)
113
+ }));
114
+ }
115
+ const packageSpec = catalogEntryPackageSpec(entry, options.version || null);
116
+ return {
117
+ templateName: packageSpec,
118
+ provenance: {
119
+ id: entry.id,
120
+ source: loaded.source,
121
+ package: entry.package,
122
+ version: options.version || entry.defaultVersion,
123
+ packageSpec,
124
+ includesExecutableImplementation: Boolean(entry.trust?.includesExecutableImplementation)
125
+ }
126
+ };
127
+ }
128
+
129
+ /**
130
+ * @param {string} sourcePath
131
+ * @param {string} targetPath
132
+ * @returns {{ ok: boolean, action: "copy_topogram", source: string, id: string, kind: "topogram", packageSpec: string, targetPath: string, provenancePath: string, files: string[], diagnostics: any[], errors: string[] }}
133
+ */
134
+ function copyLocalTopogramSource(sourcePath, targetPath) {
135
+ const packageRoot = path.resolve(sourcePath);
136
+ if (!fs.existsSync(packageRoot) || !fs.statSync(packageRoot).isDirectory()) {
137
+ throw new Error(`Copy source '${sourcePath}' was not found.`);
138
+ }
139
+ if (fs.existsSync(path.join(packageRoot, "implementation"))) {
140
+ throw new Error(`Topogram source '${sourcePath}' contains implementation/, which is not allowed for pure topogram copy.`);
141
+ }
142
+ const packageWorkspace = resolvePackageWorkspace(packageRoot);
143
+ const resolvedTarget = path.resolve(targetPath);
144
+ ensureEmptyDirectory(resolvedTarget);
145
+ /** @type {string[]} */
146
+ const files = [];
147
+ copyPath(packageWorkspace.root, path.join(resolvedTarget, DEFAULT_TOPO_FOLDER_NAME), DEFAULT_TOPO_FOLDER_NAME, files);
148
+ for (const fileName of ["topogram.project.json", "README.md"]) {
149
+ const sourceFile = path.join(packageRoot, fileName);
150
+ if (!fs.existsSync(sourceFile) || !fs.statSync(sourceFile).isFile()) {
151
+ continue;
152
+ }
153
+ if (fileName === "topogram.project.json") {
154
+ const projectConfig = JSON.parse(fs.readFileSync(sourceFile, "utf8"));
155
+ projectConfig.workspace = DEFAULT_WORKSPACE_PATH;
156
+ fs.writeFileSync(path.join(resolvedTarget, fileName), `${JSON.stringify(projectConfig, null, 2)}\n`, "utf8");
157
+ files.push(fileName);
158
+ } else {
159
+ copyPath(sourceFile, path.join(resolvedTarget, fileName), fileName, files);
160
+ }
161
+ }
162
+ const sourceId = path.basename(packageRoot);
163
+ const provenance = writeTopogramSourceRecord(resolvedTarget, {
164
+ catalogSource: null,
165
+ entry: {
166
+ id: sourceId,
167
+ package: sourcePath,
168
+ defaultVersion: "local"
169
+ },
170
+ packageSpec: sourcePath,
171
+ version: "local"
172
+ });
173
+ return {
174
+ ok: true,
175
+ action: "copy_topogram",
176
+ source: sourcePath,
177
+ id: sourceId,
178
+ kind: "topogram",
179
+ packageSpec: sourcePath,
180
+ targetPath: resolvedTarget,
181
+ provenancePath: provenance.path,
182
+ files: files.sort((a, b) => a.localeCompare(b)),
183
+ diagnostics: [],
184
+ errors: []
185
+ };
186
+ }
187
+
188
+ /**
189
+ * @param {ReturnType<typeof createNewProject>} result
190
+ * @returns {Record<string, any>}
191
+ */
192
+ function templateCopyPayload(result) {
193
+ return {
194
+ ok: true,
195
+ action: "copy_template",
196
+ kind: "template",
197
+ projectRoot: result.projectRoot,
198
+ templateName: result.templateName,
199
+ template: result.template,
200
+ topogramPath: result.topogramPath,
201
+ appPath: result.appPath,
202
+ warnings: result.warnings,
203
+ diagnostics: [],
204
+ errors: []
205
+ };
206
+ }
207
+
208
+ /**
209
+ * @param {ReturnType<typeof createNewProject>} result
210
+ * @param {string} cwd
211
+ * @returns {void}
212
+ */
213
+ export function printTemplateCopyResult(result, cwd) {
214
+ const template = result.template || {};
215
+ console.log(`Copied Topogram template to ${result.projectRoot}.`);
216
+ console.log(`Template: ${result.templateName}`);
217
+ console.log(`Source: ${template.source || "unknown"}`);
218
+ if (template.sourceSpec) {
219
+ console.log(`Source spec: ${template.sourceSpec}`);
220
+ }
221
+ if (template.catalog) {
222
+ console.log(`Catalog: ${template.catalog.id} from ${template.catalog.source}`);
223
+ console.log(`Package: ${template.catalog.packageSpec}`);
224
+ }
225
+ console.log(`Executable implementation: ${template.includesExecutableImplementation ? "yes" : "no"}`);
226
+ console.log("Policy: topogram.template-policy.json");
227
+ console.log(`Generator policy: ${GENERATOR_POLICY_FILE}`);
228
+ console.log("Template files: .topogram-template-files.json");
229
+ if (template.includesExecutableImplementation) {
230
+ console.log("Trust: .topogram-template-trust.json");
231
+ }
232
+ for (const warning of result.warnings) {
233
+ console.warn(`Warning: ${warning}`);
234
+ }
235
+ console.log("");
236
+ console.log("Next steps:");
237
+ console.log(` cd ${displayProjectRootForCopy(result, cwd)}`);
238
+ console.log(" npm install");
239
+ console.log(" npm run agent:brief");
240
+ console.log(" npm run doctor");
241
+ console.log(" npm run source:status");
242
+ console.log(" npm run template:explain");
243
+ console.log(" npm run check");
244
+ console.log(" npm run generator:policy:status");
245
+ console.log(" npm run generator:policy:check");
246
+ if (template.includesExecutableImplementation) {
247
+ console.log(" npm run template:policy:explain");
248
+ console.log(" npm run trust:status");
249
+ }
250
+ console.log(" npm run generate");
251
+ console.log(" npm run verify");
252
+ }
253
+
254
+ /**
255
+ * @param {any} payload
256
+ * @returns {void}
257
+ */
258
+ export function printTopogramCopy(payload) {
259
+ console.log(`Copied topogram '${payload.id}' to ${payload.targetPath}.`);
260
+ console.log(`Package: ${payload.packageSpec}`);
261
+ console.log(`Source provenance: ${payload.provenancePath}`);
262
+ console.log(`Files: ${payload.files.length}`);
263
+ console.log(`${TOPOGRAM_SOURCE_FILE} records copy provenance only. Local edits are allowed.`);
264
+ console.log("");
265
+ console.log("Next steps:");
266
+ console.log(` cd ${shellCommandArg(path.relative(process.cwd(), payload.targetPath) || ".")}`);
267
+ console.log(" topogram source status --local");
268
+ console.log(" topogram check");
269
+ console.log(" topogram generate");
270
+ }
271
+
272
+ /**
273
+ * @param {{ commandArgs: Record<string, any>, catalogSource?: string|null, requestedVersion?: string|null, json?: boolean, cwd?: string }} context
274
+ * @returns {number}
275
+ */
276
+ export function runCopyCommand(context) {
277
+ const {
278
+ commandArgs,
279
+ catalogSource = null,
280
+ requestedVersion = null,
281
+ json = false,
282
+ cwd = process.cwd()
283
+ } = context;
284
+
285
+ if (commandArgs.copyCommand === "list") {
286
+ const payload = buildCatalogListPayload(catalogSource || null);
287
+ if (json) {
288
+ console.log(stableStringify(payload));
289
+ } else {
290
+ printCatalogList(payload);
291
+ }
292
+ return 0;
293
+ }
294
+
295
+ const source = commandArgs.copySource;
296
+ const targetPath = commandArgs.inputPath;
297
+ if (!source || String(source).startsWith("-")) {
298
+ throw new Error("topogram copy requires <source>.");
299
+ }
300
+ if (!targetPath || String(targetPath).startsWith("-")) {
301
+ throw new Error("topogram copy requires <target>.");
302
+ }
303
+ assertProjectTargetOutsideEngine(targetPath);
304
+
305
+ let catalogTopogram;
306
+ try {
307
+ catalogTopogram = tryCopyCatalogTopogram(source, targetPath, {
308
+ catalogSource,
309
+ version: requestedVersion
310
+ });
311
+ } catch (error) {
312
+ if (isCatalogIdCandidate(source)) {
313
+ throw new Error(formatCatalogTemplateAliasError(source, catalogSource || null, error));
314
+ }
315
+ throw error;
316
+ }
317
+ if (catalogTopogram) {
318
+ if (json) {
319
+ console.log(stableStringify(catalogTopogram));
320
+ } else {
321
+ printTopogramCopy(catalogTopogram);
322
+ }
323
+ return catalogTopogram.ok ? 0 : 1;
324
+ }
325
+
326
+ if (source === "." || source.startsWith("./") || source.startsWith("../") || path.isAbsolute(source)) {
327
+ const localRoot = path.resolve(source);
328
+ if (fs.existsSync(path.join(localRoot, DEFAULT_TOPO_FOLDER_NAME)) && !fs.existsSync(path.join(localRoot, "topogram-template.json"))) {
329
+ const payload = copyLocalTopogramSource(source, targetPath);
330
+ if (json) {
331
+ console.log(stableStringify(payload));
332
+ } else {
333
+ printTopogramCopy(payload);
334
+ }
335
+ return payload.ok ? 0 : 1;
336
+ }
337
+ }
338
+
339
+ const resolvedTemplate = resolveCopyTemplateSource(source, targetPath, {
340
+ catalogSource,
341
+ version: requestedVersion
342
+ });
343
+ const result = createNewProject({
344
+ targetPath,
345
+ templateName: resolvedTemplate.templateName,
346
+ templateProvenance: resolvedTemplate.provenance,
347
+ engineRoot: ENGINE_ROOT,
348
+ templatesRoot: TEMPLATES_ROOT
349
+ });
350
+ if (json) {
351
+ console.log(stableStringify(templateCopyPayload(result)));
352
+ } else {
353
+ printTemplateCopyResult(result, cwd);
354
+ }
355
+ return 0;
356
+ }
@@ -37,7 +37,7 @@ export function printDoctorHelp() {
37
37
  console.log(" npm install --save-dev @topogram/cli");
38
38
  console.log(" npx topogram doctor");
39
39
  console.log(" npx topogram template list");
40
- console.log(" npx topogram new ./my-app --template hello-web");
40
+ console.log(" npx topogram copy hello-web ./my-app");
41
41
  console.log("");
42
42
  console.log("Related setup commands:");
43
43
  console.log(" topogram setup package-auth");
@@ -42,7 +42,7 @@ export function writtenFileHashesForReceipt(outputRoot, writtenFiles) {
42
42
  */
43
43
  export function buildImportAdoptionReceipt({ artifacts, selector, options, importStatus, summary, writtenFiles, outputRoot }) {
44
44
  return {
45
- type: "topogram_import_adoption_receipt",
45
+ type: "topogram_adoption_receipt",
46
46
  version: "0.1",
47
47
  timestamp: new Date().toISOString(),
48
48
  cli: {
@@ -87,18 +87,18 @@ export function buildImportAdoptionReceipt({ artifacts, selector, options, impor
87
87
  */
88
88
  export function buildBrownfieldImportAdoptPayload(selector, inputPath, options = {}) {
89
89
  if (!selector) {
90
- throw new Error("Missing required <selector>. Example: topogram import adopt bundle:task --dry-run");
90
+ throw new Error("Missing required <selector>. Example: topogram adopt bundle:task --dry-run");
91
91
  }
92
92
  if (options.write && options.dryRun) {
93
93
  throw new Error("Use either --dry-run or --write, not both.");
94
94
  }
95
95
  if (options.write && options.force && !options.reason) {
96
- throw new Error("Forced import adoption writes require --reason <text>.");
96
+ throw new Error("Forced adoption writes require --reason <text>.");
97
97
  }
98
98
  const artifacts = readImportAdoptionArtifacts(inputPath);
99
99
  const importStatus = buildTopogramImportStatus(artifacts.projectRoot);
100
100
  if (options.write && !options.force && !importStatus.ok) {
101
- throw new Error(`Refusing to write import adoption because brownfield source provenance is ${importStatus.status}. Run 'topogram import check ${importProjectCommandPath(artifacts.projectRoot)}', review the changed source evidence, rerun import, or pass --force --reason <text> after review.`);
101
+ throw new Error(`Refusing to write adoption because brownfield source provenance is ${importStatus.status}. Run 'topogram extract check ${importProjectCommandPath(artifacts.projectRoot)}', review the changed source evidence, rerun extract, or pass --force --reason <text> after review.`);
102
102
  }
103
103
  const result = runWorkflow("reconcile", artifacts.projectRoot, {
104
104
  adopt: selector,
@@ -129,19 +129,19 @@ export function buildBrownfieldImportAdoptPayload(selector, inputPath, options =
129
129
  receipt,
130
130
  receiptPath,
131
131
  adoption: summary,
132
- import: importStatus,
132
+ extract: importStatus,
133
133
  warnings: options.write && options.force && !importStatus.ok
134
134
  ? [`Brownfield source provenance is ${importStatus.status}; adoption write was forced with reason: ${options.reason}.`]
135
135
  : [],
136
136
  nextCommands: options.write
137
137
  ? [
138
- `topogram import history ${importProjectCommandPath(artifacts.projectRoot)}`,
139
- `topogram import status ${importProjectCommandPath(artifacts.projectRoot)}`,
138
+ `topogram extract history ${importProjectCommandPath(artifacts.projectRoot)}`,
139
+ `topogram extract status ${importProjectCommandPath(artifacts.projectRoot)}`,
140
140
  `topogram check ${importProjectCommandPath(artifacts.projectRoot)}`
141
141
  ]
142
142
  : [
143
143
  importAdoptCommand(artifacts.projectRoot, selector, true),
144
- `topogram import status ${importProjectCommandPath(artifacts.projectRoot)}`
144
+ `topogram extract status ${importProjectCommandPath(artifacts.projectRoot)}`
145
145
  ]
146
146
  };
147
147
  }
@@ -151,7 +151,7 @@ export function buildBrownfieldImportAdoptPayload(selector, inputPath, options =
151
151
  * @returns {void}
152
152
  */
153
153
  export function printBrownfieldImportAdopt(payload) {
154
- console.log(`${payload.dryRun ? "Previewed" : "Applied"} import adoption for ${payload.selector}.`);
154
+ console.log(`${payload.dryRun ? "Previewed" : "Applied"} adoption for ${payload.selector}.`);
155
155
  console.log(`Project: ${payload.projectRoot}`);
156
156
  console.log(`Promoted canonical items: ${payload.promotedCanonicalItemCount}`);
157
157
  console.log(`Written files: ${payload.writtenFiles.length}`);
@@ -40,7 +40,7 @@ export function buildTopogramCheckPayloadForPath(inputPath) {
40
40
 
41
41
  /**
42
42
  * @param {string} projectRoot
43
- * @returns {{ ok: boolean, projectRoot: string, workspaceRoot: string, import: ReturnType<typeof buildTopogramImportStatus>, topogram: ReturnType<typeof buildTopogramCheckPayloadForPath>, errors: any[] }}
43
+ * @returns {{ ok: boolean, projectRoot: string, workspaceRoot: string, extract: ReturnType<typeof buildTopogramImportStatus>, topogram: ReturnType<typeof buildTopogramCheckPayloadForPath>, errors: any[] }}
44
44
  */
45
45
  export function buildBrownfieldImportCheckPayload(projectRoot) {
46
46
  const resolvedRoot = normalizeProjectRoot(projectRoot);
@@ -50,10 +50,10 @@ export function buildBrownfieldImportCheckPayload(projectRoot) {
50
50
  ok: importStatus.ok && topogramCheck.ok,
51
51
  projectRoot: resolvedRoot,
52
52
  workspaceRoot: normalizeTopogramPath(resolvedRoot),
53
- import: importStatus,
53
+ extract: importStatus,
54
54
  topogram: topogramCheck,
55
55
  errors: [
56
- ...(importStatus.errors || []).map((/** @type {string} */ message) => ({ source: "import", message })),
56
+ ...(importStatus.errors || []).map((/** @type {string} */ message) => ({ source: "extract", message })),
57
57
  ...(topogramCheck.errors || [])
58
58
  ]
59
59
  };
@@ -64,23 +64,23 @@ export function buildBrownfieldImportCheckPayload(projectRoot) {
64
64
  * @returns {void}
65
65
  */
66
66
  export function printBrownfieldImportCheck(payload) {
67
- console.log(`Topogram import check: ${payload.import.status}`);
67
+ console.log(`Topogram extract check: ${payload.extract.status}`);
68
68
  console.log(`Project: ${payload.projectRoot}`);
69
- if (payload.import.source?.source?.path) {
70
- console.log(`Imported source: ${payload.import.source.source.path}`);
69
+ if (payload.extract.source?.source?.path) {
70
+ console.log(`Extracted source: ${payload.extract.source.source.path}`);
71
71
  }
72
- console.log(`Provenance: ${payload.import.path}`);
73
- if (payload.import.source?.files) {
74
- console.log(`Trusted source files: ${payload.import.source.files.length}`);
72
+ console.log(`Provenance: ${payload.extract.path}`);
73
+ if (payload.extract.source?.files) {
74
+ console.log(`Trusted source files: ${payload.extract.source.files.length}`);
75
75
  }
76
- if (payload.import.status === "changed") {
77
- console.log(`Changed source files: ${payload.import.content.changed.length}`);
78
- console.log(`Added source files: ${payload.import.content.added.length}`);
79
- console.log(`Removed source files: ${payload.import.content.removed.length}`);
76
+ if (payload.extract.status === "changed") {
77
+ console.log(`Changed source files: ${payload.extract.content.changed.length}`);
78
+ console.log(`Added source files: ${payload.extract.content.added.length}`);
79
+ console.log(`Removed source files: ${payload.extract.content.removed.length}`);
80
80
  }
81
81
  console.log(`Topogram check: ${payload.topogram.ok ? "passed" : "failed"}`);
82
- console.log("Imported Topogram artifacts are project-owned; import check compares only the brownfield source hashes trusted at import time plus normal Topogram validity.");
83
- for (const diagnostic of payload.import.diagnostics || []) {
82
+ console.log("Extracted Topogram artifacts are project-owned; extract check compares only the brownfield source hashes trusted at extraction time plus normal Topogram validity.");
83
+ for (const diagnostic of payload.extract.diagnostics || []) {
84
84
  const label = diagnostic.severity === "warning" ? "Warning" : "Error";
85
85
  console.log(`${label}: ${diagnostic.message}`);
86
86
  if (diagnostic.suggestedFix) {
@@ -21,7 +21,7 @@ export function buildBrownfieldImportDiffPayload(inputPath, options = {}) {
21
21
  topogramRoot: analysis.topogramRoot,
22
22
  sourcePath: analysis.sourcePath,
23
23
  provenancePath: analysis.provenancePath,
24
- importStatus: analysis.previousImportStatus,
24
+ extractStatus: analysis.previousImportStatus,
25
25
  sourceDiff: analysis.sourceDiff,
26
26
  tracks: analysis.tracks,
27
27
  sourceFiles: analysis.sourceFiles,
@@ -31,9 +31,9 @@ export function buildBrownfieldImportDiffPayload(inputPath, options = {}) {
31
31
  receiptVerification: analysis.receiptVerification,
32
32
  plannedFiles: analysis.plannedFiles,
33
33
  nextCommands: [
34
- `topogram import refresh ${importProjectCommandPath(analysis.projectRoot)} --dry-run`,
35
- `topogram import refresh ${importProjectCommandPath(analysis.projectRoot)}`,
36
- `topogram import plan ${importProjectCommandPath(analysis.projectRoot)}`
34
+ `topogram extract refresh ${importProjectCommandPath(analysis.projectRoot)} --dry-run`,
35
+ `topogram extract refresh ${importProjectCommandPath(analysis.projectRoot)}`,
36
+ `topogram extract plan ${importProjectCommandPath(analysis.projectRoot)}`
37
37
  ]
38
38
  };
39
39
  }
@@ -43,9 +43,9 @@ export function buildBrownfieldImportDiffPayload(inputPath, options = {}) {
43
43
  * @returns {void}
44
44
  */
45
45
  export function printBrownfieldImportDiff(payload) {
46
- console.log(`Import diff for ${payload.projectRoot}`);
46
+ console.log(`Extraction diff for ${payload.projectRoot}`);
47
47
  console.log(`Source: ${payload.sourcePath}`);
48
- console.log(`Source status: ${payload.importStatus}`);
48
+ console.log(`Source status: ${payload.extractStatus}`);
49
49
  console.log(`Source diff: changed=${payload.sourceDiff.counts.changed}, added=${payload.sourceDiff.counts.added}, removed=${payload.sourceDiff.counts.removed}`);
50
50
  for (const filePath of [...payload.sourceDiff.changed, ...payload.sourceDiff.added, ...payload.sourceDiff.removed].slice(0, 12)) {
51
51
  const status = payload.sourceDiff.changed.includes(filePath)
@@ -3,46 +3,55 @@
3
3
  import { TOPOGRAM_IMPORT_FILE } from "../../../import/provenance.js";
4
4
  import { TOPOGRAM_IMPORT_ADOPTIONS_FILE } from "./paths.js";
5
5
 
6
- export function printImportHelp() {
7
- console.log("Usage: topogram import <app-path> --out <target> [--from <track[,track]>] [--json]");
8
- console.log(" or: topogram import refresh [path] [--from <app-path>] [--dry-run] [--json]");
9
- console.log(" or: topogram import diff [path] [--json]");
10
- console.log(" or: topogram import check [path] [--json]");
11
- console.log(" or: topogram import plan [path] [--json]");
12
- console.log(" or: topogram import adopt --list [path] [--json]");
13
- console.log(" or: topogram import adopt <selector> [path] [--dry-run|--write] [--force --reason <text>] [--json]");
14
- console.log(" or: topogram import status [path] [--json]");
15
- console.log(" or: topogram import history [path] [--verify] [--json]");
6
+ export function printExtractHelp() {
7
+ console.log("Usage: topogram extract <app-path> --out <target> [--from <track[,track]>] [--json]");
8
+ console.log(" or: topogram extract refresh [path] [--from <app-path>] [--dry-run] [--json]");
9
+ console.log(" or: topogram extract diff [path] [--json]");
10
+ console.log(" or: topogram extract check [path] [--json]");
11
+ console.log(" or: topogram extract plan [path] [--json]");
12
+ console.log(" or: topogram extract status [path] [--json]");
13
+ console.log(" or: topogram extract history [path] [--verify] [--json]");
16
14
  console.log("");
17
- console.log("Creates an editable Topogram workspace from a brownfield app without modifying the app.");
15
+ console.log("Extracts reviewable Topogram candidates from a brownfield app without modifying the app.");
18
16
  console.log("");
19
17
  console.log("Behavior:");
20
- console.log(" - writes raw import candidates under topo/candidates/app");
18
+ console.log(" - writes raw extraction candidates under topo/candidates/app");
21
19
  console.log(" - writes reconcile proposal bundles under topo/candidates/reconcile");
22
20
  console.log(" - writes topogram.project.json with maintained ownership and no generated stack binding");
23
- console.log(` - writes ${TOPOGRAM_IMPORT_FILE} with source file hashes from import time`);
24
- console.log(" - imported Topogram artifacts are project-owned after creation");
21
+ console.log(` - writes ${TOPOGRAM_IMPORT_FILE} with source file hashes from extraction time`);
22
+ console.log(" - extracted Topogram artifacts are project-owned after creation");
25
23
  console.log(" - refresh rewrites only candidate/reconcile artifacts and source provenance");
26
- console.log(" - adoption previews never write canonical Topogram files unless --write is passed");
27
- console.log(" - adoption writes refuse dirty brownfield source provenance unless --force is passed");
28
- console.log(` - adoption writes append audit receipts to ${TOPOGRAM_IMPORT_ADOPTIONS_FILE}`);
29
- console.log(" - forced adoption writes require --reason <text>");
30
24
  console.log("");
31
25
  console.log("Examples:");
32
- console.log(" topogram import ./existing-app --out ./imported-topogram");
33
- console.log(" topogram import ./existing-app --out ./imported-topogram --from db,api,ui");
34
- console.log(" topogram import ./existing-cli --out ./imported-topogram --from cli");
35
- console.log(" topogram import diff ./imported-topogram");
36
- console.log(" topogram import refresh ./imported-topogram --from ./existing-app --dry-run");
37
- console.log(" topogram import refresh ./imported-topogram --from ./existing-app");
38
- console.log(" topogram import check ./imported-topogram");
39
- console.log(" topogram import plan ./imported-topogram");
40
- console.log(" topogram import adopt --list ./imported-topogram");
41
- console.log(" topogram import adopt bundle:task ./imported-topogram --dry-run");
42
- console.log(" topogram import adopt bundle:task ./imported-topogram --write");
43
- console.log(" topogram import adopt bundle:task ./imported-topogram --write --force --reason \"Reviewed source drift\"");
44
- console.log(" topogram import status ./imported-topogram");
45
- console.log(" topogram import history ./imported-topogram");
46
- console.log(" topogram import history ./imported-topogram --verify");
47
- console.log(" topogram import check --json");
26
+ console.log(" topogram extract ./existing-app --out ./extracted-topogram");
27
+ console.log(" topogram extract ./existing-app --out ./extracted-topogram --from db,api,ui");
28
+ console.log(" topogram extract ./existing-cli --out ./extracted-topogram --from cli");
29
+ console.log(" topogram extract diff ./extracted-topogram");
30
+ console.log(" topogram extract refresh ./extracted-topogram --from ./existing-app --dry-run");
31
+ console.log(" topogram extract refresh ./extracted-topogram --from ./existing-app");
32
+ console.log(" topogram extract check ./extracted-topogram");
33
+ console.log(" topogram extract plan ./extracted-topogram");
34
+ console.log(" topogram extract status ./extracted-topogram");
35
+ console.log(" topogram extract history ./extracted-topogram");
36
+ console.log(" topogram extract history ./extracted-topogram --verify");
37
+ console.log(" topogram extract check --json");
38
+ }
39
+
40
+ export function printAdoptHelp() {
41
+ console.log("Usage: topogram adopt --list [path] [--json]");
42
+ console.log(" or: topogram adopt <selector> [path] [--dry-run|--write] [--force --reason <text>] [--json]");
43
+ console.log("");
44
+ console.log("Promotes reviewed extraction candidates into canonical topo/ files.");
45
+ console.log("");
46
+ console.log("Behavior:");
47
+ console.log(" - previews never write canonical Topogram files unless --write is passed");
48
+ console.log(" - writes refuse dirty brownfield source provenance unless --force is passed");
49
+ console.log(` - writes append audit receipts to ${TOPOGRAM_IMPORT_ADOPTIONS_FILE}`);
50
+ console.log(" - forced writes require --reason <text>");
51
+ console.log("");
52
+ console.log("Examples:");
53
+ console.log(" topogram adopt --list ./extracted-topogram");
54
+ console.log(" topogram adopt bundle:task ./extracted-topogram --dry-run");
55
+ console.log(" topogram adopt bundle:task ./extracted-topogram --write");
56
+ console.log(" topogram adopt bundle:task ./extracted-topogram --write --force --reason \"Reviewed source drift\"");
48
57
  }
@@ -9,7 +9,7 @@ import { TOPOGRAM_IMPORT_FILE } from "../../../import/provenance.js";
9
9
  import { shellCommandArg } from "../catalog.js";
10
10
  import { resolveTopoRoot, resolveWorkspaceContext } from "../../../workspace-paths.js";
11
11
 
12
- export const TOPOGRAM_IMPORT_ADOPTIONS_FILE = ".topogram-import-adoptions.jsonl";
12
+ export const TOPOGRAM_IMPORT_ADOPTIONS_FILE = ".topogram-adoptions.jsonl";
13
13
 
14
14
  /**
15
15
  * @typedef {Record<string, any>} AnyRecord
@@ -219,7 +219,7 @@ export function readImportAdoptionReceipts(projectRoot) {
219
219
  try {
220
220
  return JSON.parse(line);
221
221
  } catch (error) {
222
- throw new Error(`Invalid import adoption receipt JSON at ${historyPath}:${index + 1}.`);
222
+ throw new Error(`Invalid adoption receipt JSON at ${historyPath}:${index + 1}.`);
223
223
  }
224
224
  });
225
225
  }
@@ -265,5 +265,5 @@ export function importProjectCommandPath(projectRoot) {
265
265
  * @returns {string}
266
266
  */
267
267
  export function importAdoptCommand(projectRoot, selector, write = false) {
268
- return `topogram import adopt ${selector} ${importProjectCommandPath(projectRoot)} ${write ? "--write" : "--dry-run"}`;
268
+ return `topogram adopt ${selector} ${importProjectCommandPath(projectRoot)} ${write ? "--write" : "--dry-run"}`;
269
269
  }