@topogram/cli 0.3.78 → 0.3.80

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 (100) 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-parser.js +2 -0
  14. package/src/cli/command-parsers/core.js +9 -5
  15. package/src/cli/command-parsers/extractor.js +40 -0
  16. package/src/cli/command-parsers/import.js +11 -17
  17. package/src/cli/command-parsers/project.js +0 -3
  18. package/src/cli/commands/catalog/copy.js +3 -3
  19. package/src/cli/commands/catalog/help.js +1 -2
  20. package/src/cli/commands/catalog/list.js +7 -4
  21. package/src/cli/commands/catalog/show.js +4 -4
  22. package/src/cli/commands/copy.js +356 -0
  23. package/src/cli/commands/doctor.js +1 -1
  24. package/src/cli/commands/extractor.js +451 -0
  25. package/src/cli/commands/import/adopt.js +9 -9
  26. package/src/cli/commands/import/check.js +15 -15
  27. package/src/cli/commands/import/diff.js +6 -6
  28. package/src/cli/commands/import/help.js +45 -34
  29. package/src/cli/commands/import/paths.js +3 -3
  30. package/src/cli/commands/import/plan.js +8 -8
  31. package/src/cli/commands/import/refresh.js +25 -24
  32. package/src/cli/commands/import/status-history.js +4 -4
  33. package/src/cli/commands/import/workspace.js +24 -18
  34. package/src/cli/commands/import-runner.js +10 -7
  35. package/src/cli/commands/import.js +4 -1
  36. package/src/cli/commands/init.js +67 -0
  37. package/src/cli/commands/query/{import-adopt.js → extract-adopt.js} +2 -2
  38. package/src/cli/commands/query/runner/change.js +2 -2
  39. package/src/cli/commands/query/runner/{import-adopt.js → extract-adopt.js} +9 -9
  40. package/src/cli/commands/query/runner/index.js +1 -1
  41. package/src/cli/commands/query/runner/workflow.js +7 -7
  42. package/src/cli/commands/query/workspace.js +4 -4
  43. package/src/cli/commands/release-status.js +2 -2
  44. package/src/cli/commands/source.js +2 -2
  45. package/src/cli/commands/template/check.js +2 -2
  46. package/src/cli/commands/template/list-show.js +4 -4
  47. package/src/cli/dispatcher.js +32 -3
  48. package/src/cli/help-dispatch.js +33 -8
  49. package/src/cli/help.js +79 -52
  50. package/src/cli/migration-guidance.js +9 -0
  51. package/src/cli/options.js +17 -0
  52. package/src/extractor/check.js +155 -0
  53. package/src/extractor/packages.js +295 -0
  54. package/src/extractor/registry.js +196 -0
  55. package/src/extractor-policy.js +249 -0
  56. package/src/generator/check.js +24 -87
  57. package/src/generator/context/bundle.js +14 -7
  58. package/src/generator/context/diff.js +8 -1
  59. package/src/generator/context/digest.js +10 -1
  60. package/src/generator/context/shared/domain-sdlc.js +5 -1
  61. package/src/generator/context/shared/relationships.js +20 -5
  62. package/src/generator/context/shared/summaries.js +26 -0
  63. package/src/generator/context/shared.d.ts +1 -0
  64. package/src/generator/context/shared.js +1 -0
  65. package/src/generator/context/slice/core.js +9 -5
  66. package/src/generator/context/slice/sdlc.js +31 -2
  67. package/src/generator/context/task-mode.js +3 -3
  68. package/src/generator/registry/index.js +16 -75
  69. package/src/generator-policy.js +9 -57
  70. package/src/import/core/registry.d.ts +3 -0
  71. package/src/import/core/registry.js +82 -8
  72. package/src/import/core/runner/reports.js +4 -4
  73. package/src/import/core/runner/run.js +2 -0
  74. package/src/import/core/runner/tracks.js +66 -4
  75. package/src/import/provenance.js +18 -17
  76. package/src/init-project.js +215 -0
  77. package/src/new-project/constants.js +1 -1
  78. package/src/new-project/create.js +2 -2
  79. package/src/new-project/project-files.js +7 -7
  80. package/src/package-adapters/adapter.js +64 -0
  81. package/src/package-adapters/file-map.js +30 -0
  82. package/src/package-adapters/index.js +27 -0
  83. package/src/package-adapters/manifest.js +108 -0
  84. package/src/package-adapters/policy.js +81 -0
  85. package/src/package-adapters/spec.js +51 -0
  86. package/src/reconcile/journeys.js +8 -3
  87. package/src/record-blocks.js +125 -0
  88. package/src/resolver/index.js +3 -0
  89. package/src/resolver/journeys.js +74 -0
  90. package/src/resolver/normalize.js +25 -0
  91. package/src/sdlc/adopt.js +1 -1
  92. package/src/validator/common.js +34 -1
  93. package/src/validator/index.js +4 -0
  94. package/src/validator/kinds.d.ts +2 -0
  95. package/src/validator/kinds.js +34 -1
  96. package/src/validator/per-kind/journey.js +233 -0
  97. package/src/workflows/docs-generate.js +4 -1
  98. package/src/workflows/reconcile/bundle-core/index.js +4 -2
  99. package/src/workflows/reconcile/canonical-surface.js +4 -1
  100. 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");