@topogram/cli 0.3.64 → 0.3.66

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 (278) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +716 -0
  3. package/src/adoption/plan.js +12 -703
  4. package/src/adoption/reporting.js +1 -1
  5. package/src/agent-brief.js +7 -21
  6. package/src/agent-ops/query-builders/auth.js +375 -0
  7. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  8. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  9. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  10. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  11. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  12. package/src/agent-ops/query-builders/change-risk.js +25 -0
  13. package/src/agent-ops/query-builders/common.js +149 -0
  14. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  15. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  16. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  17. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  18. package/src/agent-ops/query-builders/work-packets.js +417 -0
  19. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  20. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  21. package/src/agent-ops/query-builders/workflow-presets-core.js +677 -0
  22. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  23. package/src/agent-ops/query-builders.d.ts +26 -26
  24. package/src/agent-ops/query-builders.js +42 -5021
  25. package/src/archive/jsonl.js +2 -2
  26. package/src/archive/resolver-bridge.js +1 -1
  27. package/src/archive/unarchive.js +2 -1
  28. package/src/catalog/constants.js +10 -0
  29. package/src/catalog/copy.js +65 -0
  30. package/src/catalog/diagnostics.js +15 -0
  31. package/src/catalog/entries.js +42 -0
  32. package/src/catalog/files.js +67 -0
  33. package/src/catalog/provenance.js +123 -0
  34. package/src/catalog/source.js +150 -0
  35. package/src/catalog/validation.js +252 -0
  36. package/src/catalog.d.ts +2 -0
  37. package/src/catalog.js +18 -746
  38. package/src/cli/command-parsers/project.js +3 -0
  39. package/src/cli/command-parsers/shared.js +1 -1
  40. package/src/cli/commands/agent.js +2 -2
  41. package/src/cli/commands/catalog/check.js +31 -0
  42. package/src/cli/commands/catalog/copy.js +59 -0
  43. package/src/cli/commands/catalog/doctor.js +248 -0
  44. package/src/cli/commands/catalog/help.js +21 -0
  45. package/src/cli/commands/catalog/list.js +52 -0
  46. package/src/cli/commands/catalog/runner.js +92 -0
  47. package/src/cli/commands/catalog/shared.js +17 -0
  48. package/src/cli/commands/catalog/show.js +134 -0
  49. package/src/cli/commands/catalog.js +30 -615
  50. package/src/cli/commands/check.js +3 -3
  51. package/src/cli/commands/doctor.js +2 -9
  52. package/src/cli/commands/generator-policy/package-info.js +162 -0
  53. package/src/cli/commands/generator-policy/payloads.js +372 -0
  54. package/src/cli/commands/generator-policy/printers.js +159 -0
  55. package/src/cli/commands/generator-policy/runner.js +81 -0
  56. package/src/cli/commands/generator-policy/shared.js +39 -0
  57. package/src/cli/commands/generator-policy.js +15 -783
  58. package/src/cli/commands/import/adopt.js +170 -0
  59. package/src/cli/commands/import/check.js +91 -0
  60. package/src/cli/commands/import/diff.js +84 -0
  61. package/src/cli/commands/import/help.js +47 -0
  62. package/src/cli/commands/import/paths.js +269 -0
  63. package/src/cli/commands/import/plan.js +292 -0
  64. package/src/cli/commands/import/refresh.js +471 -0
  65. package/src/cli/commands/import/status-history.js +196 -0
  66. package/src/cli/commands/import/workspace.js +233 -0
  67. package/src/cli/commands/import.js +33 -1732
  68. package/src/cli/commands/migrate.js +153 -0
  69. package/src/cli/commands/package/constants.js +17 -0
  70. package/src/cli/commands/package/doctor.js +240 -0
  71. package/src/cli/commands/package/help.js +27 -0
  72. package/src/cli/commands/package/lockfile.js +135 -0
  73. package/src/cli/commands/package/npm.js +97 -0
  74. package/src/cli/commands/package/reporting.js +35 -0
  75. package/src/cli/commands/package/runner.js +33 -0
  76. package/src/cli/commands/package/shared.js +9 -0
  77. package/src/cli/commands/package/update-cli.js +252 -0
  78. package/src/cli/commands/package/versions.js +35 -0
  79. package/src/cli/commands/package.js +29 -813
  80. package/src/cli/commands/query/change-plan.js +68 -0
  81. package/src/cli/commands/query/definitions.js +202 -0
  82. package/src/cli/commands/query/import-adopt.js +121 -0
  83. package/src/cli/commands/query/runner/artifacts.js +102 -0
  84. package/src/cli/commands/query/runner/boundaries.js +211 -0
  85. package/src/cli/commands/query/runner/change.js +182 -0
  86. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  87. package/src/cli/commands/query/runner/index.js +31 -0
  88. package/src/cli/commands/query/runner/output.js +12 -0
  89. package/src/cli/commands/query/runner/workflow.js +241 -0
  90. package/src/cli/commands/query/runner.js +3 -0
  91. package/src/cli/commands/query/workflow-context.js +5 -0
  92. package/src/cli/commands/query/workspace.js +270 -0
  93. package/src/cli/commands/query.js +9 -1300
  94. package/src/cli/commands/source.js +3 -12
  95. package/src/cli/commands/template/baseline.js +100 -0
  96. package/src/cli/commands/template/check.js +467 -0
  97. package/src/cli/commands/template/constants.js +8 -0
  98. package/src/cli/commands/template/diagnostics.js +26 -0
  99. package/src/cli/commands/template/help.js +28 -0
  100. package/src/cli/commands/template/lifecycle.js +404 -0
  101. package/src/cli/commands/template/list-show.js +287 -0
  102. package/src/cli/commands/template/policy.js +422 -0
  103. package/src/cli/commands/template/shared.js +127 -0
  104. package/src/cli/commands/template/updates.js +352 -0
  105. package/src/cli/commands/template-runner.js +6 -6
  106. package/src/cli/commands/template.js +41 -2143
  107. package/src/cli/commands/trust.js +1 -1
  108. package/src/cli/commands/workflow.js +6 -1
  109. package/src/cli/dispatcher.js +6 -1
  110. package/src/cli/help.js +15 -14
  111. package/src/cli/migration-guidance.js +1 -1
  112. package/src/cli/output-safety.js +2 -1
  113. package/src/cli/path-normalization.js +3 -13
  114. package/src/generator/api/contracts.js +497 -0
  115. package/src/generator/api/metadata.js +221 -0
  116. package/src/generator/api/openapi.js +559 -0
  117. package/src/generator/api/schema.js +124 -0
  118. package/src/generator/api/types.d.ts +98 -0
  119. package/src/generator/api.js +3 -1195
  120. package/src/generator/context/domain-page.js +1 -1
  121. package/src/generator/context/shared/domain-sdlc.js +282 -0
  122. package/src/generator/context/shared/maintained-boundary.js +665 -0
  123. package/src/generator/context/shared/metrics.js +85 -0
  124. package/src/generator/context/shared/primitives.js +64 -0
  125. package/src/generator/context/shared/relationships.js +453 -0
  126. package/src/generator/context/shared/summaries.js +263 -0
  127. package/src/generator/context/shared/types.d.ts +207 -0
  128. package/src/generator/context/shared.d.ts +42 -0
  129. package/src/generator/context/shared.js +80 -1390
  130. package/src/generator/context/slice/core.js +397 -0
  131. package/src/generator/context/slice/sdlc.js +417 -0
  132. package/src/generator/context/slice/ui-packets.js +183 -0
  133. package/src/generator/context/slice.js +2 -859
  134. package/src/generator/context/task-mode.js +2 -2
  135. package/src/generator/registry/index.js +507 -0
  136. package/src/generator/registry.js +18 -504
  137. package/src/generator/runtime/environment/index.js +666 -0
  138. package/src/generator/runtime/environment.js +4 -666
  139. package/src/generator/runtime/runtime-check/index.js +554 -0
  140. package/src/generator/runtime/runtime-check.js +4 -554
  141. package/src/generator/runtime/shared/index.js +572 -0
  142. package/src/generator/runtime/shared.js +19 -570
  143. package/src/generator/sdlc/doc-page.js +1 -1
  144. package/src/generator/shared.d.ts +2 -0
  145. package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
  146. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +1 -1
  147. package/src/generator/surfaces/shared.d.ts +3 -0
  148. package/src/generator/widget-conformance/behavior-report.js +258 -0
  149. package/src/generator/widget-conformance/checks.js +371 -0
  150. package/src/generator/widget-conformance/projection-context.js +200 -0
  151. package/src/generator/widget-conformance/report.js +166 -0
  152. package/src/generator/widget-conformance/types.d.ts +121 -0
  153. package/src/generator/widget-conformance.js +3 -824
  154. package/src/import/core/context.d.ts +3 -0
  155. package/src/import/core/context.js +5 -7
  156. package/src/import/core/contracts.d.ts +1 -0
  157. package/src/import/core/registry.d.ts +4 -0
  158. package/src/import/core/runner/candidates.js +337 -0
  159. package/src/import/core/runner/options.js +22 -0
  160. package/src/import/core/runner/reports.js +51 -0
  161. package/src/import/core/runner/run.js +79 -0
  162. package/src/import/core/runner/tracks.js +150 -0
  163. package/src/import/core/runner/ui-drafts.js +393 -0
  164. package/src/import/core/runner.js +3 -698
  165. package/src/import/core/shared/api-routes.js +221 -0
  166. package/src/import/core/shared/candidates.js +97 -0
  167. package/src/import/core/shared/files.js +177 -0
  168. package/src/import/core/shared/next-app.js +389 -0
  169. package/src/import/core/shared/types.d.ts +51 -0
  170. package/src/import/core/shared/ui-routes.js +230 -0
  171. package/src/import/core/shared.js +60 -861
  172. package/src/new-project/constants.js +128 -0
  173. package/src/new-project/create.js +90 -0
  174. package/src/new-project/json.js +28 -0
  175. package/src/new-project/metadata.js +96 -0
  176. package/src/new-project/package-spec.js +161 -0
  177. package/src/new-project/project-files.js +351 -0
  178. package/src/new-project/template-policy.js +269 -0
  179. package/src/new-project/template-resolution.js +370 -0
  180. package/src/new-project/template-snapshots.js +442 -0
  181. package/src/new-project/template-updates.js +512 -0
  182. package/src/new-project/types.d.ts +83 -0
  183. package/src/new-project.js +6 -2277
  184. package/src/parser.d.ts +87 -1
  185. package/src/parser.js +118 -0
  186. package/src/policy/review-boundaries.d.ts +15 -0
  187. package/src/project-config/index.js +591 -0
  188. package/src/project-config.js +19 -561
  189. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  190. package/src/resolver/enrich/bug.js +2 -0
  191. package/src/resolver/enrich/pitch.js +2 -0
  192. package/src/resolver/enrich/requirement.js +2 -0
  193. package/src/resolver/enrich/task.js +2 -0
  194. package/src/resolver/index.js +19 -2089
  195. package/src/resolver/normalize.js +384 -1
  196. package/src/resolver/plans.js +168 -0
  197. package/src/resolver/projections-api.js +494 -0
  198. package/src/resolver/projections-db.js +133 -0
  199. package/src/resolver/projections-ui.js +317 -0
  200. package/src/resolver/shapes.js +251 -0
  201. package/src/resolver/shared.js +278 -0
  202. package/src/resolver/widgets.js +132 -0
  203. package/src/sdlc/adopt.js +6 -5
  204. package/src/sdlc/paths.js +3 -5
  205. package/src/sdlc/scaffold.js +2 -1
  206. package/src/template-trust/constants.js +62 -0
  207. package/src/template-trust/content.js +258 -0
  208. package/src/template-trust/diff.js +92 -0
  209. package/src/template-trust/policy.js +61 -0
  210. package/src/template-trust/record.js +90 -0
  211. package/src/template-trust/status.js +182 -0
  212. package/src/template-trust.js +24 -687
  213. package/src/text-helpers.d.ts +1 -0
  214. package/src/topogram-types.d.ts +69 -0
  215. package/src/validator/common.js +488 -0
  216. package/src/validator/data-model.js +237 -0
  217. package/src/validator/docs.js +167 -0
  218. package/src/validator/expressions.js +146 -1
  219. package/src/validator/index.d.ts +23 -0
  220. package/src/validator/index.js +32 -3585
  221. package/src/validator/kinds.d.ts +41 -0
  222. package/src/validator/kinds.js +2 -0
  223. package/src/validator/model-helpers.js +46 -0
  224. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  225. package/src/validator/per-kind/bug.js +6 -0
  226. package/src/validator/per-kind/domain.js +15 -2
  227. package/src/validator/per-kind/pitch.js +7 -0
  228. package/src/validator/per-kind/requirement.js +5 -0
  229. package/src/validator/per-kind/task.js +7 -0
  230. package/src/validator/per-kind/widget.js +14 -0
  231. package/src/validator/projections/api-http-async.js +410 -0
  232. package/src/validator/projections/api-http-authz.js +88 -0
  233. package/src/validator/projections/api-http-core.js +205 -0
  234. package/src/validator/projections/api-http-policies.js +339 -0
  235. package/src/validator/projections/api-http-responses.js +233 -0
  236. package/src/validator/projections/api-http.js +44 -0
  237. package/src/validator/projections/db.js +353 -0
  238. package/src/validator/projections/generator-defaults.js +45 -0
  239. package/src/validator/projections/helpers.js +87 -0
  240. package/src/validator/projections/ui-helpers.js +214 -0
  241. package/src/validator/projections/ui-navigation.js +344 -0
  242. package/src/validator/projections/ui-structure.js +364 -0
  243. package/src/validator/projections/ui-widgets.js +493 -0
  244. package/src/validator/projections/ui.js +46 -0
  245. package/src/validator/registry.js +48 -1
  246. package/src/validator/utils.d.ts +20 -0
  247. package/src/validator/utils.js +115 -12
  248. package/src/widget-behavior.d.ts +1 -0
  249. package/src/workflows/import-app/api/collect.js +221 -0
  250. package/src/workflows/import-app/api/openapi.js +257 -0
  251. package/src/workflows/import-app/api/routes.js +327 -0
  252. package/src/workflows/import-app/api/sources.js +22 -0
  253. package/src/workflows/import-app/api.js +2 -797
  254. package/src/workflows/reconcile/adoption-plan/build.js +212 -0
  255. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  256. package/src/workflows/reconcile/adoption-plan/outputs.js +153 -0
  257. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  258. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  259. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  260. package/src/workflows/reconcile/adoption-plan.js +30 -740
  261. package/src/workflows/reconcile/auth/closures.js +115 -0
  262. package/src/workflows/reconcile/auth/formatters.js +142 -0
  263. package/src/workflows/reconcile/auth/inference.js +330 -0
  264. package/src/workflows/reconcile/auth/roles.js +122 -0
  265. package/src/workflows/reconcile/auth.js +35 -690
  266. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  267. package/src/workflows/reconcile/bundle-core.js +12 -598
  268. package/src/workflows/reconcile/candidate-model.js +18 -2
  269. package/src/workflows/reconcile/canonical-surface.js +1 -1
  270. package/src/workflows/reconcile/impacts/adoption-plan.js +196 -0
  271. package/src/workflows/reconcile/impacts/indexes.js +105 -0
  272. package/src/workflows/reconcile/impacts/patches.js +252 -0
  273. package/src/workflows/reconcile/impacts/reports.js +80 -0
  274. package/src/workflows/reconcile/impacts.js +14 -623
  275. package/src/workflows/reconcile/renderers.js +41 -6
  276. package/src/workflows/shared.js +5 -11
  277. package/src/workspace-docs.d.ts +29 -0
  278. package/src/workspace-paths.js +328 -0
@@ -43,5 +43,8 @@ export function parseProjectCommandArgs(args) {
43
43
  if (args[0] === "package" && args[1] === "update-cli") {
44
44
  return { packageCommand: "update-cli", inputPath: args.includes("--latest") ? "latest" : args[2] };
45
45
  }
46
+ if (args[0] === "migrate" && args[1] === "workspace-folder") {
47
+ return { migrateCommand: "workspace-folder", inputPath: commandPath(args, 2, ".") };
48
+ }
46
49
  return null;
47
50
  }
@@ -12,7 +12,7 @@
12
12
  * @param {string} [fallback]
13
13
  * @returns {string}
14
14
  */
15
- export function commandPath(args, index, fallback = "./topogram") {
15
+ export function commandPath(args, index, fallback = "./topo") {
16
16
  const value = args[index];
17
17
  return value && !value.startsWith("-") ? value : fallback;
18
18
  }
@@ -14,12 +14,12 @@ export function printAgentHelp() {
14
14
  console.log("");
15
15
  console.log("Prints read-only first-run guidance for humans and agents working in a Topogram project.");
16
16
  console.log("");
17
- console.log("Defaults: path is ./topogram. The command validates the Topogram and project config, but does not write files, generate apps, load generator adapters, or execute template implementation.");
17
+ console.log("Defaults: path is ./topo. The command validates the Topogram and project config, but does not write files, generate apps, load generator adapters, or execute template implementation.");
18
18
  console.log("");
19
19
  console.log("Examples:");
20
20
  console.log(" topogram agent brief");
21
21
  console.log(" topogram agent brief --json");
22
- console.log(" topogram agent brief ./topogram --json");
22
+ console.log(" topogram agent brief ./topo --json");
23
23
  }
24
24
 
25
25
  /**
@@ -0,0 +1,31 @@
1
+ // @ts-check
2
+
3
+ import { checkCatalogSource } from "../../../catalog.js";
4
+
5
+ /**
6
+ * @param {string} source
7
+ * @returns {ReturnType<typeof checkCatalogSource>}
8
+ */
9
+ export function buildCatalogCheckPayload(source) {
10
+ if (!source) {
11
+ throw new Error("topogram catalog check requires <path-or-url>.");
12
+ }
13
+ return checkCatalogSource(source);
14
+ }
15
+
16
+ /**
17
+ * @param {ReturnType<typeof checkCatalogSource>} payload
18
+ * @returns {void}
19
+ */
20
+ export function printCatalogCheck(payload) {
21
+ console.log(payload.ok ? "Catalog check passed." : "Catalog check failed.");
22
+ console.log(`Source: ${payload.source}`);
23
+ if (payload.catalog) {
24
+ console.log(`Version: ${payload.catalog.version}`);
25
+ console.log(`Entries: ${payload.catalog.entries.length}`);
26
+ }
27
+ for (const diagnostic of payload.diagnostics) {
28
+ const label = diagnostic.severity === "warning" ? "Warning" : "Error";
29
+ console.log(`${label}: ${diagnostic.message}`);
30
+ }
31
+ }
@@ -0,0 +1,59 @@
1
+ // @ts-check
2
+
3
+ import path from "node:path";
4
+
5
+ import {
6
+ copyCatalogTopogramEntry,
7
+ findCatalogEntry,
8
+ loadCatalog,
9
+ TOPOGRAM_SOURCE_FILE
10
+ } from "../../../catalog.js";
11
+ import { shellCommandArg } from "./shared.js";
12
+
13
+ /**
14
+ * @param {string} id
15
+ * @param {string} targetPath
16
+ * @param {{ source?: string|null, version?: string|null }} options
17
+ * @returns {{ ok: boolean, source: string, id: string, kind: "topogram", packageSpec: string, targetPath: string, provenancePath: string, files: string[], diagnostics: any[], errors: string[] }}
18
+ */
19
+ export function buildCatalogCopyPayload(id, targetPath, options) {
20
+ if (!id || id.startsWith("-")) {
21
+ throw new Error("topogram catalog copy requires <id>.");
22
+ }
23
+ if (!targetPath || targetPath.startsWith("-")) {
24
+ throw new Error("topogram catalog copy requires <target>.");
25
+ }
26
+ const loaded = loadCatalog(options.source || null);
27
+ const entry = findCatalogEntry(loaded.catalog, id, "topogram");
28
+ if (!entry) {
29
+ throw new Error(`Catalog topogram entry '${id}' was not found in ${loaded.source}.`);
30
+ }
31
+ const copied = copyCatalogTopogramEntry(entry, targetPath, {
32
+ catalogSource: loaded.source,
33
+ version: options.version || null
34
+ });
35
+ return {
36
+ source: loaded.source,
37
+ ...copied,
38
+ diagnostics: [],
39
+ errors: []
40
+ };
41
+ }
42
+
43
+ /**
44
+ * @param {ReturnType<typeof buildCatalogCopyPayload>} payload
45
+ * @returns {void}
46
+ */
47
+ export function printCatalogCopy(payload) {
48
+ console.log(`Copied catalog topogram '${payload.id}' to ${payload.targetPath}.`);
49
+ console.log(`Package: ${payload.packageSpec}`);
50
+ console.log(`Source provenance: ${payload.provenancePath}`);
51
+ console.log(`Files: ${payload.files.length}`);
52
+ console.log(`${TOPOGRAM_SOURCE_FILE} records catalog-copy provenance only. Local edits are allowed.`);
53
+ console.log("");
54
+ console.log("Next steps:");
55
+ console.log(` cd ${shellCommandArg(path.relative(process.cwd(), payload.targetPath) || ".")}`);
56
+ console.log(" topogram source status --local");
57
+ console.log(" topogram check");
58
+ console.log(" topogram generate");
59
+ }
@@ -0,0 +1,248 @@
1
+ // @ts-check
2
+
3
+ import childProcess from "node:child_process";
4
+
5
+ import {
6
+ catalogEntryPackageSpec,
7
+ catalogSourceOrDefault,
8
+ loadCatalog
9
+ } from "../../../catalog.js";
10
+ import { githubAuthStatus } from "../../../github-client.js";
11
+ import { assertSafeNpmSpec, localNpmrcEnv } from "../../../npm-safety.js";
12
+ import { messageFromError } from "./shared.js";
13
+
14
+ /**
15
+ * @param {string|null} source
16
+ * @returns {{ ok: boolean, source: string, auth: { githubTokenEnv: boolean, ghTokenEnv: boolean, ghCli: { checked: boolean, available: boolean, authenticated: boolean, reason: string|null } }, catalog: { reachable: boolean, version: string|null, entries: number }, packages: Array<{ id: string, kind: string, package: string, version: string, packageSpec: string, ok: boolean, checkedVersion: string|null, diagnostics: any[] }>, diagnostics: any[], errors: string[] }}
17
+ */
18
+ export function buildCatalogDoctorPayload(source) {
19
+ const resolvedSource = catalogSourceOrDefault(source || null);
20
+ const auth = buildCatalogDoctorAuth(resolvedSource);
21
+ const diagnostics = [];
22
+ /** @type {Array<{ id: string, kind: string, package: string, version: string, packageSpec: string, ok: boolean, checkedVersion: string|null, diagnostics: any[] }>} */
23
+ const packages = [];
24
+ let loaded = null;
25
+ try {
26
+ loaded = loadCatalog(source || null);
27
+ } catch (error) {
28
+ const diagnostic = {
29
+ code: "catalog_unreachable",
30
+ severity: "error",
31
+ message: messageFromError(error),
32
+ path: resolvedSource,
33
+ suggestedFix: catalogDoctorSourceFix(resolvedSource)
34
+ };
35
+ return {
36
+ ok: false,
37
+ source: resolvedSource,
38
+ auth,
39
+ catalog: {
40
+ reachable: false,
41
+ version: null,
42
+ entries: 0
43
+ },
44
+ packages,
45
+ diagnostics: [diagnostic],
46
+ errors: [diagnostic.message]
47
+ };
48
+ }
49
+ diagnostics.push(...loaded.diagnostics);
50
+ for (const entry of loaded.catalog.entries) {
51
+ packages.push(checkCatalogDoctorPackage(entry));
52
+ }
53
+ const packageDiagnostics = packages.flatMap((entry) => entry.diagnostics);
54
+ const allDiagnostics = [...diagnostics, ...packageDiagnostics];
55
+ const errors = allDiagnostics
56
+ .filter((diagnostic) => diagnostic.severity === "error")
57
+ .map((diagnostic) => diagnostic.message);
58
+ return {
59
+ ok: errors.length === 0,
60
+ source: loaded.source,
61
+ auth,
62
+ catalog: {
63
+ reachable: true,
64
+ version: loaded.catalog.version,
65
+ entries: loaded.catalog.entries.length
66
+ },
67
+ packages,
68
+ diagnostics: allDiagnostics,
69
+ errors
70
+ };
71
+ }
72
+
73
+ /**
74
+ * @param {string} source
75
+ * @returns {{ githubTokenEnv: boolean, ghTokenEnv: boolean, ghCli: { checked: boolean, available: boolean, authenticated: boolean, reason: string|null } }}
76
+ */
77
+ export function buildCatalogDoctorAuth(source) {
78
+ const shouldCheckGh = source.startsWith("github:");
79
+ return githubAuthStatus({
80
+ checkGh: shouldCheckGh && !process.env.GITHUB_TOKEN && !process.env.GH_TOKEN
81
+ });
82
+ }
83
+
84
+ /**
85
+ * @param {any} entry
86
+ * @returns {{ id: string, kind: string, package: string, version: string, packageSpec: string, ok: boolean, checkedVersion: string|null, diagnostics: any[] }}
87
+ */
88
+ function checkCatalogDoctorPackage(entry) {
89
+ const packageSpec = catalogEntryPackageSpec(entry);
90
+ const result = runNpmViewPackageSpec(packageSpec);
91
+ if (result.status === 0) {
92
+ const checkedVersion = String(result.stdout || "").trim().replace(/^"|"$/g, "");
93
+ return {
94
+ id: entry.id,
95
+ kind: entry.kind,
96
+ package: entry.package,
97
+ version: entry.defaultVersion,
98
+ packageSpec,
99
+ ok: checkedVersion === entry.defaultVersion,
100
+ checkedVersion: checkedVersion || null,
101
+ diagnostics: checkedVersion === entry.defaultVersion ? [] : [{
102
+ code: "catalog_package_version_mismatch",
103
+ severity: "error",
104
+ message: `Catalog entry '${entry.id}' expected ${packageSpec}, but npm returned '${checkedVersion || "(empty)"}'.`,
105
+ path: entry.id,
106
+ suggestedFix: "Check defaultVersion in the catalog, or publish the referenced package version."
107
+ }]
108
+ };
109
+ }
110
+ const diagnostic = catalogDoctorPackageDiagnostic(entry, packageSpec, result);
111
+ return {
112
+ id: entry.id,
113
+ kind: entry.kind,
114
+ package: entry.package,
115
+ version: entry.defaultVersion,
116
+ packageSpec,
117
+ ok: false,
118
+ checkedVersion: null,
119
+ diagnostics: [diagnostic]
120
+ };
121
+ }
122
+
123
+ /**
124
+ * @param {string} packageSpec
125
+ * @returns {{ status: number|null, stdout: string, stderr: string, error?: Error }}
126
+ */
127
+ export function runNpmViewPackageSpec(packageSpec) {
128
+ assertSafeNpmSpec(packageSpec);
129
+ const npmBin = process.platform === "win32" ? "npm.cmd" : "npm";
130
+ return childProcess.spawnSync(npmBin, ["view", "--json", "--", packageSpec, "version"], {
131
+ encoding: "utf8",
132
+ env: {
133
+ ...process.env,
134
+ ...localNpmrcEnv(process.cwd()),
135
+ PATH: process.env.PATH || ""
136
+ }
137
+ });
138
+ }
139
+
140
+ /**
141
+ * @param {any} entry
142
+ * @param {string} packageSpec
143
+ * @param {{ stdout?: string, stderr?: string, error?: Error }} result
144
+ * @returns {any}
145
+ */
146
+ export function catalogDoctorPackageDiagnostic(entry, packageSpec, result) {
147
+ const output = [result.error?.message, result.stderr, result.stdout].filter(Boolean).join("\n").trim();
148
+ const normalized = output.toLowerCase();
149
+ if (result.error?.message && result.error.message.includes("ENOENT")) {
150
+ return {
151
+ code: "npm_not_found",
152
+ severity: "error",
153
+ message: "npm is required to check catalog package access.",
154
+ path: entry.id,
155
+ suggestedFix: "Install npm with Node.js, then rerun `topogram catalog doctor`."
156
+ };
157
+ }
158
+ if (/\b(401|e401|authentication|auth token|login)\b/.test(normalized)) {
159
+ return {
160
+ code: "catalog_package_auth_required",
161
+ severity: "error",
162
+ message: `Authentication is required to inspect package '${packageSpec}'.`,
163
+ path: entry.id,
164
+ suggestedFix: "Configure registry-specific npm auth when using private packages."
165
+ };
166
+ }
167
+ if (/\b(403|e403|forbidden|permission|denied)\b/.test(normalized)) {
168
+ return {
169
+ code: "catalog_package_access_denied",
170
+ severity: "error",
171
+ message: `Package access was denied for '${packageSpec}'.`,
172
+ path: entry.id,
173
+ suggestedFix: "Check package visibility and registry-specific npm auth for the consumer environment."
174
+ };
175
+ }
176
+ if (/\b(404|e404|not found)\b/.test(normalized)) {
177
+ return {
178
+ code: "catalog_package_not_found",
179
+ severity: "error",
180
+ message: `Package '${packageSpec}' was not found, or the current npm token cannot see it.`,
181
+ path: entry.id,
182
+ suggestedFix: "Check the package name/version and npm package registry access."
183
+ };
184
+ }
185
+ return {
186
+ code: "catalog_package_check_failed",
187
+ severity: "error",
188
+ message: `Failed to inspect package '${packageSpec}'.${output ? `\n${output}` : ""}`,
189
+ path: entry.id,
190
+ suggestedFix: "Run `npm view <package>@<version> version --json` with the same npm auth configuration to debug package access."
191
+ };
192
+ }
193
+
194
+ /**
195
+ * @param {string} source
196
+ * @returns {string}
197
+ */
198
+ function catalogDoctorSourceFix(source) {
199
+ if (source.startsWith("github:")) {
200
+ return "Set GITHUB_TOKEN or GH_TOKEN with repository read access, use `gh auth login` only as a local no-token fallback, or pass --catalog ./topograms.catalog.json.";
201
+ }
202
+ if (source.startsWith("http://") || source.startsWith("https://")) {
203
+ return "Check the catalog URL and token access, or pass --catalog ./topograms.catalog.json.";
204
+ }
205
+ return "Check the local catalog path and run `topogram catalog check <path>`.";
206
+ }
207
+
208
+ /**
209
+ * @param {ReturnType<typeof buildCatalogDoctorPayload>} payload
210
+ * @returns {void}
211
+ */
212
+ export function printCatalogDoctor(payload) {
213
+ console.log(payload.ok ? "Catalog doctor passed." : "Catalog doctor found issues.");
214
+ console.log(`Source: ${payload.source}`);
215
+ if (payload.source.startsWith("github:")) {
216
+ const tokenStatus = payload.auth.githubTokenEnv || payload.auth.ghTokenEnv ? "yes" : "no";
217
+ const ghStatus = payload.auth.ghCli.checked
218
+ ? `${payload.auth.ghCli.authenticated ? "authenticated" : "not authenticated"}${payload.auth.ghCli.reason ? ` (${payload.auth.ghCli.reason})` : ""}`
219
+ : "not checked";
220
+ console.log(`GitHub token env: ${tokenStatus}`);
221
+ console.log(`gh auth: ${ghStatus}`);
222
+ }
223
+ console.log(`Catalog reachable: ${payload.catalog.reachable ? "yes" : "no"}`);
224
+ if (payload.catalog.reachable) {
225
+ console.log(`Version: ${payload.catalog.version}`);
226
+ console.log(`Entries: ${payload.catalog.entries}`);
227
+ }
228
+ if (payload.packages.length > 0) {
229
+ console.log("Packages:");
230
+ for (const item of payload.packages) {
231
+ console.log(`- ${item.id} (${item.kind}): ${item.packageSpec} ${item.ok ? "ok" : "failed"}`);
232
+ for (const diagnostic of item.diagnostics) {
233
+ console.log(` Error: ${diagnostic.message}`);
234
+ if (diagnostic.suggestedFix) {
235
+ console.log(` Fix: ${diagnostic.suggestedFix}`);
236
+ }
237
+ }
238
+ }
239
+ }
240
+ const packageIds = new Set(payload.packages.map((item) => item.id));
241
+ for (const diagnostic of payload.diagnostics.filter((item) => !item.path || !packageIds.has(item.path))) {
242
+ const label = diagnostic.severity === "warning" ? "Warning" : "Error";
243
+ console.log(`${label}: ${diagnostic.message}`);
244
+ if (diagnostic.suggestedFix) {
245
+ console.log(`Fix: ${diagnostic.suggestedFix}`);
246
+ }
247
+ }
248
+ }
@@ -0,0 +1,21 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @returns {void}
5
+ */
6
+ export function printCatalogHelp() {
7
+ console.log("Usage: topogram catalog list [--json] [--catalog <path-or-source>]");
8
+ console.log(" or: topogram catalog show <id> [--json] [--catalog <path-or-source>]");
9
+ console.log(" or: topogram catalog doctor [--json] [--catalog <path-or-source>]");
10
+ console.log(" or: topogram catalog check <path-or-url> [--json]");
11
+ console.log(" or: topogram catalog copy <id> <target> [--version <version>] [--json] [--catalog <path-or-source>]");
12
+ console.log("");
13
+ console.log("Catalog commands inspect the shared Topogram index. The catalog is an index; templates and topograms resolve to versioned packages.");
14
+ console.log("");
15
+ console.log("Examples:");
16
+ console.log(" topogram catalog list");
17
+ console.log(" topogram catalog show hello-web");
18
+ console.log(" topogram catalog doctor");
19
+ console.log(" topogram catalog check topograms.catalog.json");
20
+ console.log(" topogram catalog copy hello ./hello-topogram");
21
+ }
@@ -0,0 +1,52 @@
1
+ // @ts-check
2
+
3
+ import {
4
+ catalogSourceOrDefault,
5
+ loadCatalog
6
+ } from "../../../catalog.js";
7
+ import { shellCommandArg } from "./shared.js";
8
+
9
+ /**
10
+ * @param {string|null} source
11
+ * @returns {{ ok: boolean, source: string, catalog: any, entries: any[], diagnostics: any[], errors: string[] }}
12
+ */
13
+ export function buildCatalogListPayload(source) {
14
+ const loaded = loadCatalog(source || null);
15
+ return {
16
+ ok: true,
17
+ source: loaded.source,
18
+ catalog: {
19
+ version: loaded.catalog.version,
20
+ entries: loaded.catalog.entries.length
21
+ },
22
+ entries: loaded.catalog.entries,
23
+ diagnostics: loaded.diagnostics,
24
+ errors: []
25
+ };
26
+ }
27
+
28
+ /**
29
+ * @param {ReturnType<typeof buildCatalogListPayload>} payload
30
+ * @returns {void}
31
+ */
32
+ export function printCatalogList(payload) {
33
+ console.log("Catalog entries:");
34
+ console.log("Template entries create starters with `topogram new`; topogram entries copy editable Topogram source.");
35
+ console.log(`Catalog: ${payload.source}`);
36
+ console.log(`Version: ${payload.catalog.version}`);
37
+ const catalogOption = payload.source === catalogSourceOrDefault(null)
38
+ ? ""
39
+ : ` --catalog ${shellCommandArg(payload.source)}`;
40
+ for (const entry of payload.entries) {
41
+ console.log(`- ${entry.id} (${entry.kind})`);
42
+ console.log(` Package: ${entry.package}@${entry.defaultVersion}`);
43
+ console.log(` Description: ${entry.description}`);
44
+ console.log(` Trust scope: ${entry.trust.scope}`);
45
+ console.log(` Executable implementation: ${entry.trust.includesExecutableImplementation ? "yes" : "no"}`);
46
+ if (entry.kind === "template") {
47
+ console.log(` New: topogram new ./my-app --template ${shellCommandArg(entry.id)}${catalogOption}`);
48
+ } else {
49
+ console.log(` Copy: topogram catalog copy ${shellCommandArg(entry.id)} ./${entry.id}-topogram${catalogOption}`);
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,92 @@
1
+ // @ts-check
2
+
3
+ import { stableStringify } from "../../../format.js";
4
+ import { buildCatalogCheckPayload, printCatalogCheck } from "./check.js";
5
+ import { buildCatalogCopyPayload, printCatalogCopy } from "./copy.js";
6
+ import { buildCatalogDoctorPayload, printCatalogDoctor } from "./doctor.js";
7
+ import { printCatalogHelp } from "./help.js";
8
+ import { buildCatalogListPayload, printCatalogList } from "./list.js";
9
+ import { buildCatalogShowPayload, printCatalogShow } from "./show.js";
10
+
11
+ /**
12
+ * @param {{
13
+ * commandArgs: Record<string, any>,
14
+ * inputPath: string|null|undefined,
15
+ * catalogSource: string|null,
16
+ * requestedVersion: string|null,
17
+ * json: boolean
18
+ * }} context
19
+ * @returns {number}
20
+ */
21
+ export function runCatalogCommand(context) {
22
+ const { commandArgs, inputPath, catalogSource, requestedVersion, json } = context;
23
+ if (commandArgs.catalogCommand === "list") {
24
+ const payload = buildCatalogListPayload(catalogSource || inputPath || null);
25
+ if (json) {
26
+ console.log(stableStringify(payload));
27
+ } else {
28
+ printCatalogList(payload);
29
+ }
30
+ return 0;
31
+ }
32
+
33
+ if (commandArgs.catalogCommand === "show") {
34
+ if (!inputPath) {
35
+ console.error("Missing required <id>.");
36
+ printCatalogHelp();
37
+ return 1;
38
+ }
39
+ const payload = buildCatalogShowPayload(inputPath, catalogSource);
40
+ if (json) {
41
+ console.log(stableStringify(payload));
42
+ } else {
43
+ printCatalogShow(payload);
44
+ }
45
+ return payload.ok ? 0 : 1;
46
+ }
47
+
48
+ if (commandArgs.catalogCommand === "doctor") {
49
+ const payload = buildCatalogDoctorPayload(catalogSource || inputPath || null);
50
+ if (json) {
51
+ console.log(stableStringify(payload));
52
+ } else {
53
+ printCatalogDoctor(payload);
54
+ }
55
+ return payload.ok ? 0 : 1;
56
+ }
57
+
58
+ if (commandArgs.catalogCommand === "check") {
59
+ if (!inputPath) {
60
+ console.error("Missing required <path-or-url>.");
61
+ printCatalogHelp();
62
+ return 1;
63
+ }
64
+ const payload = buildCatalogCheckPayload(inputPath);
65
+ if (json) {
66
+ console.log(stableStringify(payload));
67
+ } else {
68
+ printCatalogCheck(payload);
69
+ }
70
+ return payload.ok ? 0 : 1;
71
+ }
72
+
73
+ if (commandArgs.catalogCommand === "copy") {
74
+ if (!commandArgs.catalogId || !inputPath) {
75
+ console.error("Missing required <id> or <target>.");
76
+ printCatalogHelp();
77
+ return 1;
78
+ }
79
+ const payload = buildCatalogCopyPayload(commandArgs.catalogId, inputPath, {
80
+ source: catalogSource,
81
+ version: requestedVersion
82
+ });
83
+ if (json) {
84
+ console.log(stableStringify(payload));
85
+ } else {
86
+ printCatalogCopy(payload);
87
+ }
88
+ return 0;
89
+ }
90
+
91
+ throw new Error(`Unknown catalog command '${commandArgs.catalogCommand}'`);
92
+ }
@@ -0,0 +1,17 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @param {unknown} error
5
+ * @returns {string}
6
+ */
7
+ export function messageFromError(error) {
8
+ return error instanceof Error ? error.message : String(error);
9
+ }
10
+
11
+ /**
12
+ * @param {string} value
13
+ * @returns {string}
14
+ */
15
+ export function shellCommandArg(value) {
16
+ return /^[A-Za-z0-9_./:@=-]+$/.test(value) ? value : JSON.stringify(value);
17
+ }