@topogram/cli 0.3.63 → 0.3.64

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 (121) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan.d.ts +6 -0
  3. package/src/adoption/reporting.d.ts +10 -0
  4. package/src/adoption/review-groups.d.ts +6 -0
  5. package/src/agent-brief.d.ts +3 -0
  6. package/src/agent-brief.js +495 -0
  7. package/src/agent-ops/query-builders.d.ts +26 -0
  8. package/src/archive/archive.d.ts +2 -0
  9. package/src/archive/compact.d.ts +1 -0
  10. package/src/archive/unarchive.d.ts +1 -0
  11. package/src/catalog.d.ts +10 -0
  12. package/src/catalog.js +62 -66
  13. package/src/cli/catalog-alias.d.ts +1 -0
  14. package/src/cli/command-parser.js +38 -0
  15. package/src/cli/command-parsers/core.js +102 -0
  16. package/src/cli/command-parsers/generator.js +39 -0
  17. package/src/cli/command-parsers/import.js +44 -0
  18. package/src/cli/command-parsers/legacy-workflow.js +21 -0
  19. package/src/cli/command-parsers/project.js +47 -0
  20. package/src/cli/command-parsers/sdlc.js +47 -0
  21. package/src/cli/command-parsers/shared.js +51 -0
  22. package/src/cli/command-parsers/template.js +48 -0
  23. package/src/cli/commands/agent.js +47 -0
  24. package/src/cli/commands/catalog.js +617 -0
  25. package/src/cli/commands/check.js +268 -0
  26. package/src/cli/commands/doctor.js +268 -0
  27. package/src/cli/commands/emit.js +149 -0
  28. package/src/cli/commands/generate.js +96 -0
  29. package/src/cli/commands/generator-policy.js +785 -0
  30. package/src/cli/commands/generator.js +443 -0
  31. package/src/cli/commands/import-runner.js +157 -0
  32. package/src/cli/commands/import.js +1734 -0
  33. package/src/cli/commands/inspect.js +55 -0
  34. package/src/cli/commands/new.js +94 -0
  35. package/src/cli/commands/package.js +815 -0
  36. package/src/cli/commands/query.js +1302 -0
  37. package/src/cli/commands/release-rollout.js +257 -0
  38. package/src/cli/commands/release-shared.js +528 -0
  39. package/src/cli/commands/release-status.js +429 -0
  40. package/src/cli/commands/release.js +107 -0
  41. package/src/cli/commands/sdlc.js +168 -0
  42. package/src/cli/commands/setup.js +76 -0
  43. package/src/cli/commands/source.js +291 -0
  44. package/src/cli/commands/template-runner.js +198 -0
  45. package/src/cli/commands/template.js +2145 -0
  46. package/src/cli/commands/trust.js +219 -0
  47. package/src/cli/commands/version.js +40 -0
  48. package/src/cli/commands/widget.js +168 -0
  49. package/src/cli/commands/workflow.js +63 -0
  50. package/src/cli/dispatcher.js +392 -0
  51. package/src/cli/help-dispatch.js +188 -0
  52. package/src/cli/help.js +296 -0
  53. package/src/cli/migration-guidance.js +59 -0
  54. package/src/cli/options.js +96 -0
  55. package/src/cli/output-safety.js +107 -0
  56. package/src/cli/path-normalization.js +29 -0
  57. package/src/cli.js +47 -11711
  58. package/src/example-implementation.d.ts +2 -0
  59. package/src/format.d.ts +1 -0
  60. package/src/generator/check.d.ts +1 -0
  61. package/src/generator/context/bundle.d.ts +1 -0
  62. package/src/generator/context/shared.d.ts +2 -0
  63. package/src/generator/native/parity-bundle.js +2 -1
  64. package/src/generator/surfaces/web/html-escape.js +22 -0
  65. package/src/generator/surfaces/web/react.js +10 -8
  66. package/src/generator/surfaces/web/sveltekit.js +7 -5
  67. package/src/generator/surfaces/web/vanilla.js +8 -4
  68. package/src/generator.d.ts +2 -0
  69. package/src/github-client.js +520 -0
  70. package/src/import/core/shared.js +20 -62
  71. package/src/import/extractors/api/flutter-dio.js +4 -8
  72. package/src/import/extractors/api/react-native-repository.js +4 -8
  73. package/src/import/index.d.ts +4 -0
  74. package/src/import/provenance.d.ts +4 -0
  75. package/src/new-project.js +100 -11
  76. package/src/npm-safety.js +79 -0
  77. package/src/parser.d.ts +1 -0
  78. package/src/path-helpers.d.ts +1 -0
  79. package/src/path-helpers.js +20 -0
  80. package/src/project-config.js +1 -0
  81. package/src/reconcile/docs.d.ts +8 -0
  82. package/src/reconcile/journeys.d.ts +1 -0
  83. package/src/resolver.d.ts +1 -0
  84. package/src/runtime-support.js +29 -0
  85. package/src/sdlc/adopt.d.ts +1 -0
  86. package/src/sdlc/check.d.ts +1 -0
  87. package/src/sdlc/explain.d.ts +1 -0
  88. package/src/sdlc/release.d.ts +1 -0
  89. package/src/sdlc/scaffold.d.ts +1 -0
  90. package/src/sdlc/transition.d.ts +1 -0
  91. package/src/text-helpers.d.ts +6 -0
  92. package/src/text-helpers.js +245 -0
  93. package/src/topogram-config.js +306 -0
  94. package/src/validator.d.ts +2 -0
  95. package/src/workflows/adoption/index.js +26 -0
  96. package/src/workflows/docs-generate.js +262 -0
  97. package/src/workflows/docs-scan.js +703 -0
  98. package/src/workflows/docs.js +15 -0
  99. package/src/workflows/import-app/api.js +799 -0
  100. package/src/workflows/import-app/db.js +538 -0
  101. package/src/workflows/import-app/index.js +30 -0
  102. package/src/workflows/import-app/shared.js +218 -0
  103. package/src/workflows/import-app/ui.js +443 -0
  104. package/src/workflows/import-app/workflow.js +159 -0
  105. package/src/workflows/reconcile/adoption-plan.js +742 -0
  106. package/src/workflows/reconcile/auth.js +692 -0
  107. package/src/workflows/reconcile/bundle-core.js +600 -0
  108. package/src/workflows/reconcile/bundle-shared.js +75 -0
  109. package/src/workflows/reconcile/candidate-model.js +477 -0
  110. package/src/workflows/reconcile/canonical-surface.js +264 -0
  111. package/src/workflows/reconcile/gap-report.js +333 -0
  112. package/src/workflows/reconcile/ids.js +6 -0
  113. package/src/workflows/reconcile/impacts.js +625 -0
  114. package/src/workflows/reconcile/index.js +7 -0
  115. package/src/workflows/reconcile/renderers.js +461 -0
  116. package/src/workflows/reconcile/summary.js +90 -0
  117. package/src/workflows/reconcile/workflow.js +309 -0
  118. package/src/workflows/shared.js +189 -0
  119. package/src/workflows/types.d.ts +93 -0
  120. package/src/workflows.d.ts +1 -0
  121. package/src/workflows.js +10 -7652
@@ -0,0 +1,4 @@
1
+ export function createImportContext(...args: any[]): any;
2
+ export function normalizeWorkspacePaths(...args: any[]): any;
3
+ export function parseImportTracks(...args: any[]): any;
4
+ export function runImportAppWorkflow(...args: any[]): any;
@@ -0,0 +1,4 @@
1
+ export const TOPOGRAM_IMPORT_FILE: string;
2
+ export function buildTopogramImportStatus(projectRoot: string): any;
3
+ export function collectImportSourceFileRecords(sourceRoot: string, options?: any): any[];
4
+ export function writeTopogramImportRecord(projectRoot: string, input: any): any;
@@ -7,7 +7,9 @@ import os from "node:os";
7
7
  import path from "node:path";
8
8
 
9
9
  import { defaultGeneratorPolicy, writeGeneratorPolicy } from "./generator-policy.js";
10
+ import { assertSafeNpmSpec, localNpmrcEnv } from "./npm-safety.js";
10
11
  import { writeTemplateTrustRecord } from "./template-trust.js";
12
+ import { githubRepoSlug } from "./topogram-config.js";
11
13
 
12
14
  const CLI_PACKAGE_NAME = "@topogram/cli";
13
15
  const DEFAULT_TEMPLATE_NAME = "hello-web";
@@ -464,12 +466,9 @@ function summarizeTemplateTopology(templateRoot) {
464
466
  * @returns {string}
465
467
  */
466
468
  export function installPackageSpec(templateSpec) {
469
+ assertSafeNpmSpec(templateSpec);
467
470
  const installRoot = fs.mkdtempSync(path.join(os.tmpdir(), "topogram-template-"));
468
471
  const npmBin = process.platform === "win32" ? "npm.cmd" : "npm";
469
- const localNpmConfig = path.join(process.cwd(), ".npmrc");
470
- const npmConfigEnv = !process.env.NPM_CONFIG_USERCONFIG && fs.existsSync(localNpmConfig)
471
- ? { NPM_CONFIG_USERCONFIG: localNpmConfig }
472
- : {};
473
472
  const result = childProcess.spawnSync(
474
473
  npmBin,
475
474
  [
@@ -480,13 +479,14 @@ export function installPackageSpec(templateSpec) {
480
479
  "--no-audit",
481
480
  "--no-fund",
482
481
  "--package-lock=false",
482
+ "--",
483
483
  templateSpec
484
484
  ],
485
485
  {
486
486
  encoding: "utf8",
487
487
  env: {
488
488
  ...process.env,
489
- ...npmConfigEnv,
489
+ ...localNpmrcEnv(process.cwd()),
490
490
  PATH: process.env.PATH || ""
491
491
  }
492
492
  }
@@ -509,7 +509,7 @@ export function installPackageSpec(templateSpec) {
509
509
  function formatPackageInstallError(templateSpec, result) {
510
510
  const output = [result.error?.message, result.stderr, result.stdout].filter(Boolean).join("\n").trim();
511
511
  const normalized = output.toLowerCase();
512
- const npmrcHint = "Ensure npm can access the registry required by this template package.";
512
+ const npmrcHint = "Ensure npm can access the registry required by this template package. Topogram ignores project .npmrc files unless TOPOGRAM_ALLOW_LOCAL_NPMRC=1 or --allow-local-npmrc is used.";
513
513
  const packageAccessHint = "For private package registries, configure a token with package read access.";
514
514
  const authHint = "For private template packages, configure npm auth for the package registry before installing.";
515
515
  const doctorHint = "Run `topogram doctor` to check Node.js, npm, package, and catalog access.";
@@ -1963,6 +1963,7 @@ function writeProjectPackage(projectRoot, engineRoot, template) {
1963
1963
  scripts: {
1964
1964
  explain: "node ./scripts/explain.mjs",
1965
1965
  doctor: "topogram doctor",
1966
+ "agent:brief": "topogram agent brief --json",
1966
1967
  "source:status": "topogram source status --local",
1967
1968
  "source:status:remote": "topogram source status --remote",
1968
1969
  check: "topogram check",
@@ -2022,29 +2023,33 @@ Topogram app workflow
2022
2023
  topogram/
2023
2024
  topogram.project.json
2024
2025
 
2025
- 2. Validate:
2026
+ 2. Start with project guidance:
2027
+ npm run agent:brief
2028
+
2029
+ 3. Validate:
2026
2030
  npm run doctor
2027
2031
  npm run source:status
2028
2032
  npm run template:explain
2029
2033
  npm run check
2030
2034
 
2031
- 3. Regenerate:
2035
+ 4. Regenerate:
2032
2036
  npm run generate
2033
2037
 
2034
- 4. Verify generated app:
2038
+ 5. Verify generated app:
2035
2039
  npm run verify
2036
2040
 
2037
- 5. Run locally:
2041
+ 6. Run locally:
2038
2042
  npm run bootstrap
2039
2043
  npm run dev
2040
2044
 
2041
- 6. Probe the running app from another terminal:
2045
+ 7. Probe the running app from another terminal:
2042
2046
  npm run app:probe
2043
2047
 
2044
2048
  Or run self-contained local runtime verification:
2045
2049
  npm run app:runtime
2046
2050
 
2047
2051
  Useful inspection:
2052
+ npm run agent:brief
2048
2053
  npm run check:json
2049
2054
  topogram emit ui-widget-contract ./topogram --json
2050
2055
  topogram emit widget-conformance-report ./topogram --json
@@ -2084,6 +2089,7 @@ function writeProjectReadme(projectRoot, projectConfig) {
2084
2089
  const workflowCommands = [
2085
2090
  "npm install",
2086
2091
  "npm run explain",
2092
+ "npm run agent:brief",
2087
2093
  "npm run doctor",
2088
2094
  "npm run source:status",
2089
2095
  "npm run template:explain",
@@ -2126,11 +2132,93 @@ ${workflowCommands.join("\n")}
2126
2132
  Edit \`topogram/\` and \`topogram.project.json\`, then regenerate with \`npm run generate\`.
2127
2133
  Generated app code is written to \`app/\`.
2128
2134
  Use \`topogram emit <target>\` to inspect contracts, reports, snapshots, and other artifacts without regenerating the app.
2135
+ Agents should start with \`AGENTS.md\` and \`npm run agent:brief\`. The direct \`topogram agent brief --json\` command is the canonical machine-readable first-run guidance.
2129
2136
  ${template.includesExecutableImplementation ? "\nThis template copied `implementation/` code. `topogram new` did not execute it; review `implementation/`, `topogram.template-policy.json`, and `.topogram-template-trust.json` before regenerating after edits.\n" : ""}
2130
2137
  `;
2131
2138
  fs.writeFileSync(path.join(projectRoot, "README.md"), readme, "utf8");
2132
2139
  }
2133
2140
 
2141
+ /**
2142
+ * @param {string} projectRoot
2143
+ * @param {Record<string, any>} projectConfig
2144
+ * @returns {void}
2145
+ */
2146
+ function writeAgentsGuide(projectRoot, projectConfig) {
2147
+ const template = projectConfig.template || {};
2148
+ const hasImplementation = Boolean(projectConfig.implementation || template.includesExecutableImplementation);
2149
+ const guide = `# Agent Guide
2150
+
2151
+ Start here before editing this Topogram project.
2152
+
2153
+ ## First Read
2154
+
2155
+ 1. \`AGENTS.md\`
2156
+ 2. \`README.md\`
2157
+ 3. \`topogram.project.json\`
2158
+ 4. \`topogram.template-policy.json\`
2159
+ 5. \`topogram.generator-policy.json\`
2160
+ ${hasImplementation ? "6. `.topogram-template-trust.json`\n7. `implementation/`\n8. Focused `topogram query ...` output\n" : "6. Focused `topogram query ...` output\n"}
2161
+ Machine-readable source:
2162
+
2163
+ \`\`\`bash
2164
+ topogram agent brief --json
2165
+ \`\`\`
2166
+
2167
+ Local shortcut:
2168
+
2169
+ \`\`\`bash
2170
+ npm run agent:brief
2171
+ \`\`\`
2172
+
2173
+ Reference: https://github.com/${githubRepoSlug(null)}/blob/main/docs/agent-first-run.md
2174
+
2175
+ ## First Commands
2176
+
2177
+ \`\`\`bash
2178
+ npm run agent:brief
2179
+ npm run doctor
2180
+ npm run source:status
2181
+ npm run template:explain
2182
+ npm run generator:policy:check
2183
+ ${hasImplementation ? "npm run trust:status\n" : ""}npm run check
2184
+ npm run query:list
2185
+ npm run query:show -- widget-behavior
2186
+ \`\`\`
2187
+
2188
+ ## Edit Rules
2189
+
2190
+ - Edit \`topogram/**\` and \`topogram.project.json\` first.
2191
+ - Review policy files before editing \`topogram.template-policy.json\` or \`topogram.generator-policy.json\`.
2192
+ - Do not make lasting edits under generated-owned \`app/**\`; use \`npm run generate\` to replace generated output.
2193
+ - If an output is changed to maintained ownership, agents may edit that app code directly after reading focused query packets.
2194
+
2195
+ ## UI And Widgets
2196
+
2197
+ - \`ui_contract\` owns screens, regions, widget bindings, behavior, visibility, and semantic design tokens.
2198
+ - Web/iOS/Android surfaces realize the shared UI contract; they do not own widget placement.
2199
+ - Use \`topogram widget check --json\`, \`topogram widget behavior --json\`, and focused \`topogram query ...\` packets after UI edits.
2200
+
2201
+ ## Template And Trust
2202
+
2203
+ - Local edits to template-derived Topogram files are project-owned.
2204
+ - Use \`npm run source:status\` and \`npm run template:update:recommend\` before applying template updates.
2205
+ ${hasImplementation ? "- This project has executable `implementation/` code. `topogram new` did not execute it. Do not refresh trust until the implementation has been reviewed.\n" : "- This template does not declare executable implementation code.\n"}
2206
+ ## Import And Adoption
2207
+
2208
+ - If \`.topogram-import.json\` exists, run \`topogram import check .\`, \`topogram import plan .\`, \`topogram import adopt --list .\`, and \`topogram import history . --verify\`.
2209
+ - Imported Topogram files are project-owned after adoption; source hashes record trusted import evidence at the time of import.
2210
+
2211
+ ## Verification Gates
2212
+
2213
+ \`\`\`bash
2214
+ npm run check
2215
+ npm run generate
2216
+ npm run verify
2217
+ \`\`\`
2218
+ `;
2219
+ fs.writeFileSync(path.join(projectRoot, "AGENTS.md"), guide, "utf8");
2220
+ }
2221
+
2134
2222
  /**
2135
2223
  * @param {CreateNewProjectOptions} options
2136
2224
  * @returns {{ projectRoot: string, templateName: string, template: Record<string, any>, topogramPath: string, appPath: string, warnings: string[] }}
@@ -2165,6 +2253,7 @@ export function createNewProject({
2165
2253
  writeProjectPackage(projectRoot, engineRoot, template);
2166
2254
  writeExplainScript(projectRoot);
2167
2255
  writeProjectReadme(projectRoot, projectConfig);
2256
+ writeAgentsGuide(projectRoot, projectConfig);
2168
2257
  writeTemplateFilesManifest(projectRoot, projectConfig);
2169
2258
  writeTemplatePolicy(projectRoot, defaultTemplatePolicyForTemplate(template));
2170
2259
  writeGeneratorPolicy(projectRoot, defaultGeneratorPolicy());
@@ -0,0 +1,79 @@
1
+ // @ts-check
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ export const LOCAL_NPMRC_ENV = "TOPOGRAM_ALLOW_LOCAL_NPMRC";
7
+
8
+ /**
9
+ * @param {string} spec
10
+ * @returns {string}
11
+ */
12
+ export function assertSafeNpmSpec(spec) {
13
+ if (typeof spec !== "string" || spec.trim().length === 0) {
14
+ throw new Error("Empty npm package spec.");
15
+ }
16
+ if (spec.startsWith("-")) {
17
+ throw new Error(`Refusing npm package spec starting with '-': '${spec}'.`);
18
+ }
19
+ if (/[\s\r\n\0]/.test(spec)) {
20
+ throw new Error(`Invalid characters in npm package spec: '${spec}'.`);
21
+ }
22
+ return spec;
23
+ }
24
+
25
+ /**
26
+ * @param {string|undefined} value
27
+ * @returns {boolean}
28
+ */
29
+ export function localNpmrcAllowedByEnv(value = process.env[LOCAL_NPMRC_ENV]) {
30
+ return value === "1" || value === "true" || value === "yes";
31
+ }
32
+
33
+ /**
34
+ * @param {string} cwd
35
+ * @returns {{ exists: boolean, path: string, enabled: boolean, reason: string }}
36
+ */
37
+ export function localNpmrcStatus(cwd = process.cwd()) {
38
+ const npmrcPath = path.join(cwd, ".npmrc");
39
+ const exists = fs.existsSync(npmrcPath);
40
+ if (process.env.NPM_CONFIG_USERCONFIG) {
41
+ return {
42
+ exists,
43
+ path: npmrcPath,
44
+ enabled: false,
45
+ reason: "NPM_CONFIG_USERCONFIG is already set explicitly."
46
+ };
47
+ }
48
+ if (!exists) {
49
+ return {
50
+ exists,
51
+ path: npmrcPath,
52
+ enabled: false,
53
+ reason: "No local .npmrc was found."
54
+ };
55
+ }
56
+ if (!localNpmrcAllowedByEnv()) {
57
+ return {
58
+ exists,
59
+ path: npmrcPath,
60
+ enabled: false,
61
+ reason: `Local .npmrc is ignored unless ${LOCAL_NPMRC_ENV}=1 or --allow-local-npmrc is used.`
62
+ };
63
+ }
64
+ return {
65
+ exists,
66
+ path: npmrcPath,
67
+ enabled: true,
68
+ reason: `Local .npmrc is enabled by ${LOCAL_NPMRC_ENV}.`
69
+ };
70
+ }
71
+
72
+ /**
73
+ * @param {string} cwd
74
+ * @returns {Record<string, string>}
75
+ */
76
+ export function localNpmrcEnv(cwd = process.cwd()) {
77
+ const status = localNpmrcStatus(cwd);
78
+ return status.enabled ? { NPM_CONFIG_USERCONFIG: status.path } : {};
79
+ }
@@ -0,0 +1 @@
1
+ export function parsePath(root: string): any;
@@ -0,0 +1 @@
1
+ export function relativeTo(from: string, to: string): string;
@@ -0,0 +1,20 @@
1
+ // @ts-check
2
+
3
+ import path from "node:path";
4
+
5
+ /**
6
+ * @param {string} value
7
+ * @returns {string}
8
+ */
9
+ export function toPosixPath(value) {
10
+ return String(value || "").replaceAll("\\", "/").replaceAll(path.sep, "/");
11
+ }
12
+
13
+ /**
14
+ * @param {string} base
15
+ * @param {string} filePath
16
+ * @returns {string}
17
+ */
18
+ export function relativeTo(base, filePath) {
19
+ return toPosixPath(path.relative(base, filePath));
20
+ }
@@ -35,6 +35,7 @@ import { validateProjectGeneratorPolicy } from "./generator-policy.js";
35
35
  * @property {Record<string, { path: string, ownership: "generated"|"maintained" }>} outputs
36
36
  * @property {{ runtimes: RuntimeTopologyRuntime[] }} topology
37
37
  * @property {{ id?: string, module?: string, export?: string, implementation_module?: string, implementation_export?: string }} [implementation]
38
+ * @property {Record<string, any>} [template]
38
39
  */
39
40
 
40
41
  /**
@@ -0,0 +1,8 @@
1
+ export function buildBundleDocDriftSummaries(...args: any[]): any[];
2
+ export function buildBundleDocMetadataPatches(...args: any[]): any[];
3
+ export function applyDocLinkPatchToMarkdown(...args: any[]): string;
4
+ export function applyDocMetadataPatchToMarkdown(...args: any[]): string;
5
+ export function buildDocDriftSummaries(...args: any[]): any[];
6
+ export function buildDocLinkSuggestions(...args: any[]): any[];
7
+ export function buildDocMetadataPatches(...args: any[]): any[];
8
+ export function extractDocMetadata(...args: any[]): any;
@@ -0,0 +1 @@
1
+ export function buildJourneyDrafts(graph: any): any;
@@ -0,0 +1 @@
1
+ export function resolveWorkspace(ast: any): any;
@@ -0,0 +1,29 @@
1
+ // @ts-check
2
+
3
+ export const MINIMUM_NODE_MAJOR = 20;
4
+
5
+ /**
6
+ * @param {string} version
7
+ * @returns {{ ok: boolean, major: number, minimum: number, message: string|null }}
8
+ */
9
+ export function nodeVersionSupport(version = process.versions.node) {
10
+ const major = Number.parseInt(String(version).split(".")[0] || "0", 10);
11
+ const ok = Number.isFinite(major) && major >= MINIMUM_NODE_MAJOR;
12
+ return {
13
+ ok,
14
+ major,
15
+ minimum: MINIMUM_NODE_MAJOR,
16
+ message: ok ? null : `Topogram requires Node.js ${MINIMUM_NODE_MAJOR}+; current runtime is ${version}.`
17
+ };
18
+ }
19
+
20
+ /**
21
+ * @param {string} [version]
22
+ * @returns {void}
23
+ */
24
+ export function assertSupportedNode(version = process.versions.node) {
25
+ const support = nodeVersionSupport(version);
26
+ if (!support.ok) {
27
+ throw new Error(support.message || `Topogram requires Node.js ${MINIMUM_NODE_MAJOR}+.`);
28
+ }
29
+ }
@@ -0,0 +1 @@
1
+ export function sdlcAdopt(...args: any[]): any;
@@ -0,0 +1 @@
1
+ export function checkWorkspace(...args: any[]): any;
@@ -0,0 +1 @@
1
+ export function explain(...args: any[]): any;
@@ -0,0 +1 @@
1
+ export function runRelease(...args: any[]): any;
@@ -0,0 +1 @@
1
+ export function scaffoldNew(...args: any[]): any;
@@ -0,0 +1 @@
1
+ export function transitionStatement(...args: any[]): any;
@@ -0,0 +1,6 @@
1
+ export const canonicalCandidateTerm: (value: unknown, options?: { technicalStopwords?: boolean }) => string;
2
+ export const ensureTrailingNewline: (value: string) => string;
3
+ export const extractRankedTerms: (markdown: string, options?: { technicalStopwords?: boolean }) => string[];
4
+ export const idHintify: (value: unknown) => string;
5
+ export const slugify: (value: unknown) => string;
6
+ export const titleCase: (value: unknown) => string;
@@ -0,0 +1,245 @@
1
+ // @ts-check
2
+
3
+ export const GENERIC_STOPWORDS = new Set([
4
+ "the",
5
+ "and",
6
+ "for",
7
+ "with",
8
+ "that",
9
+ "this",
10
+ "from",
11
+ "into",
12
+ "your",
13
+ "their",
14
+ "have",
15
+ "will",
16
+ "when",
17
+ "where",
18
+ "what",
19
+ "which",
20
+ "then",
21
+ "than",
22
+ "been",
23
+ "being",
24
+ "does",
25
+ "each",
26
+ "just",
27
+ "also",
28
+ "through",
29
+ "about",
30
+ "because",
31
+ "after",
32
+ "before",
33
+ "under",
34
+ "over",
35
+ "still",
36
+ "they",
37
+ "them",
38
+ "there",
39
+ "these",
40
+ "those",
41
+ "more",
42
+ "most",
43
+ "some",
44
+ "only",
45
+ "very",
46
+ "same",
47
+ "much",
48
+ "many",
49
+ "other",
50
+ "used",
51
+ "using"
52
+ ]);
53
+
54
+ export const TECHNICAL_STOPWORDS = new Set([
55
+ "readme",
56
+ "docs",
57
+ "topogram",
58
+ "generated",
59
+ "example",
60
+ "examples",
61
+ "app",
62
+ "apps",
63
+ "agreement",
64
+ "api",
65
+ "artifacts",
66
+ "bash",
67
+ "bundle",
68
+ "bundles",
69
+ "commands",
70
+ "compile",
71
+ "deployment",
72
+ "deploy",
73
+ "engine",
74
+ "environment",
75
+ "fixtures",
76
+ "files",
77
+ "fly",
78
+ "getting",
79
+ "implementation",
80
+ "include",
81
+ "layout",
82
+ "local",
83
+ "migrations",
84
+ "model",
85
+ "notes",
86
+ "npm",
87
+ "package",
88
+ "proof",
89
+ "react",
90
+ "recommended",
91
+ "report",
92
+ "reports",
93
+ "runnable",
94
+ "run",
95
+ "runtime",
96
+ "scripts",
97
+ "server",
98
+ "smoke",
99
+ "snapshot",
100
+ "sqlite",
101
+ "stack",
102
+ "stages",
103
+ "started",
104
+ "state",
105
+ "sveltekit",
106
+ "current",
107
+ "check",
108
+ "checks",
109
+ "env",
110
+ "usage",
111
+ "web",
112
+ "workspace"
113
+ ]);
114
+
115
+ /**
116
+ * @param {string} value
117
+ * @returns {string}
118
+ */
119
+ export function ensureTrailingNewline(value) {
120
+ return value.endsWith("\n") ? value : `${value}\n`;
121
+ }
122
+
123
+ /**
124
+ * @param {unknown} value
125
+ * @returns {string}
126
+ */
127
+ export function slugify(value) {
128
+ return String(value || "")
129
+ .trim()
130
+ .toLowerCase()
131
+ .replace(/[^a-z0-9]+/g, "-")
132
+ .replace(/^-+|-+$/g, "") || "untitled";
133
+ }
134
+
135
+ /**
136
+ * @param {unknown} value
137
+ * @returns {string}
138
+ */
139
+ export function idHintify(value) {
140
+ return String(value || "")
141
+ .trim()
142
+ .toLowerCase()
143
+ .replace(/[^a-z0-9]+/g, "_")
144
+ .replace(/^_+|_+$/g, "") || "untitled";
145
+ }
146
+
147
+ /**
148
+ * @param {unknown} value
149
+ * @returns {string}
150
+ */
151
+ export function canonicalCandidateTerm(value) {
152
+ const normalized = slugify(value);
153
+ if (normalized.endsWith("ies")) {
154
+ return `${normalized.slice(0, -3)}y`;
155
+ }
156
+ if (normalized === "status" || normalized === "stats") {
157
+ return normalized;
158
+ }
159
+ if (normalized.endsWith("s") && !normalized.endsWith("ss") && !normalized.endsWith("us") && !normalized.endsWith("is")) {
160
+ return normalized.slice(0, -1);
161
+ }
162
+ return normalized;
163
+ }
164
+
165
+ /**
166
+ * @param {unknown} value
167
+ * @returns {string}
168
+ */
169
+ export function pluralizeCandidateTerm(value) {
170
+ const normalized = String(value || "");
171
+ if (!normalized) return "items";
172
+ const parts = normalized.split("_");
173
+ const last = parts.pop() || "item";
174
+ let plural = last;
175
+ if (last === "stats") {
176
+ plural = "stats";
177
+ } else if (last.endsWith("sis")) {
178
+ plural = `${last.slice(0, -2)}es`;
179
+ } else if (last.endsWith("y") && !/[aeiou]y$/.test(last)) {
180
+ plural = `${last.slice(0, -1)}ies`;
181
+ } else if (/(s|x|z|ch|sh)$/.test(last)) {
182
+ plural = `${last}es`;
183
+ } else {
184
+ plural = `${last}s`;
185
+ }
186
+ return [...parts, plural].join("_");
187
+ }
188
+
189
+ /**
190
+ * @param {unknown} value
191
+ * @returns {string}
192
+ */
193
+ export function titleCase(value) {
194
+ return String(value || "")
195
+ .split(/[_\-\s]+/)
196
+ .filter(Boolean)
197
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
198
+ .join(" ");
199
+ }
200
+
201
+ /**
202
+ * @param {{ technicalStopwords?: boolean }} [options]
203
+ * @returns {Set<string>}
204
+ */
205
+ export function stopwordSet(options = {}) {
206
+ const includeTechnical = options.technicalStopwords !== false;
207
+ return new Set([
208
+ ...GENERIC_STOPWORDS,
209
+ ...(includeTechnical ? TECHNICAL_STOPWORDS : [])
210
+ ]);
211
+ }
212
+
213
+ /**
214
+ * @param {string} markdown
215
+ * @param {{ technicalStopwords?: boolean }} [options]
216
+ * @returns {string[]}
217
+ */
218
+ export function extractRankedTerms(markdown, options = {}) {
219
+ const stopwords = stopwordSet(options);
220
+ const normalized = markdown
221
+ .replace(/\[[^\]]+\]\([^)]+\)/g, " ")
222
+ .replace(/\/Users\/[^\s)]+/g, " ")
223
+ .replace(/https?:\/\/[^\s)]+/g, " ")
224
+ .replace(/`([^`]+)`/g, " $1 ")
225
+ .replace(/[_/]/g, " ");
226
+ const frequency = new Map();
227
+ const headings = normalized.match(/^#+\s+(.+)$/gm) || [];
228
+ for (const heading of headings) {
229
+ for (const word of heading.toLowerCase().match(/\b[a-z][a-z0-9-]{2,}\b/g) || []) {
230
+ if (stopwords.has(word)) {
231
+ continue;
232
+ }
233
+ frequency.set(word, (frequency.get(word) || 0) + 3);
234
+ }
235
+ }
236
+ for (const word of normalized.toLowerCase().match(/\b[a-z][a-z0-9-]{2,}\b/g) || []) {
237
+ if (stopwords.has(word)) {
238
+ continue;
239
+ }
240
+ frequency.set(word, (frequency.get(word) || 0) + 1);
241
+ }
242
+ return [...frequency.entries()]
243
+ .sort((a, b) => (b[1] - a[1]) || a[0].localeCompare(b[0]))
244
+ .map(([term]) => term);
245
+ }