@topogram/cli 0.3.62 → 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.
- package/package.json +1 -1
- package/src/adoption/plan.d.ts +6 -0
- package/src/adoption/reporting.d.ts +10 -0
- package/src/adoption/review-groups.d.ts +6 -0
- package/src/agent-brief.d.ts +3 -0
- package/src/agent-brief.js +495 -0
- package/src/agent-ops/query-builders.d.ts +26 -0
- package/src/archive/archive.d.ts +2 -0
- package/src/archive/compact.d.ts +1 -0
- package/src/archive/unarchive.d.ts +1 -0
- package/src/catalog.d.ts +10 -0
- package/src/catalog.js +62 -66
- package/src/cli/catalog-alias.d.ts +1 -0
- package/src/cli/command-parser.js +38 -0
- package/src/cli/command-parsers/core.js +102 -0
- package/src/cli/command-parsers/generator.js +39 -0
- package/src/cli/command-parsers/import.js +44 -0
- package/src/cli/command-parsers/legacy-workflow.js +21 -0
- package/src/cli/command-parsers/project.js +47 -0
- package/src/cli/command-parsers/sdlc.js +47 -0
- package/src/cli/command-parsers/shared.js +51 -0
- package/src/cli/command-parsers/template.js +48 -0
- package/src/cli/commands/agent.js +47 -0
- package/src/cli/commands/catalog.js +617 -0
- package/src/cli/commands/check.js +268 -0
- package/src/cli/commands/doctor.js +268 -0
- package/src/cli/commands/emit.js +149 -0
- package/src/cli/commands/generate.js +96 -0
- package/src/cli/commands/generator-policy.js +785 -0
- package/src/cli/commands/generator.js +443 -0
- package/src/cli/commands/import-runner.js +157 -0
- package/src/cli/commands/import.js +1734 -0
- package/src/cli/commands/inspect.js +55 -0
- package/src/cli/commands/new.js +94 -0
- package/src/cli/commands/package.js +815 -0
- package/src/cli/commands/query.js +1302 -0
- package/src/cli/commands/release-rollout.js +257 -0
- package/src/cli/commands/release-shared.js +528 -0
- package/src/cli/commands/release-status.js +429 -0
- package/src/cli/commands/release.js +107 -0
- package/src/cli/commands/sdlc.js +168 -0
- package/src/cli/commands/setup.js +76 -0
- package/src/cli/commands/source.js +291 -0
- package/src/cli/commands/template-runner.js +198 -0
- package/src/cli/commands/template.js +2145 -0
- package/src/cli/commands/trust.js +219 -0
- package/src/cli/commands/version.js +40 -0
- package/src/cli/commands/widget.js +168 -0
- package/src/cli/commands/workflow.js +63 -0
- package/src/cli/dispatcher.js +392 -0
- package/src/cli/help-dispatch.js +188 -0
- package/src/cli/help.js +296 -0
- package/src/cli/migration-guidance.js +59 -0
- package/src/cli/options.js +96 -0
- package/src/cli/output-safety.js +107 -0
- package/src/cli/path-normalization.js +29 -0
- package/src/cli.js +47 -11711
- package/src/example-implementation.d.ts +2 -0
- package/src/format.d.ts +1 -0
- package/src/generator/check.d.ts +1 -0
- package/src/generator/context/bundle.d.ts +1 -0
- package/src/generator/context/shared.d.ts +2 -0
- package/src/generator/native/parity-bundle.js +2 -1
- package/src/generator/surfaces/web/html-escape.js +22 -0
- package/src/generator/surfaces/web/react.js +10 -8
- package/src/generator/surfaces/web/sveltekit.js +7 -5
- package/src/generator/surfaces/web/vanilla.js +8 -4
- package/src/generator.d.ts +2 -0
- package/src/github-client.js +520 -0
- package/src/import/core/shared.js +20 -62
- package/src/import/extractors/api/flutter-dio.js +4 -8
- package/src/import/extractors/api/react-native-repository.js +4 -8
- package/src/import/index.d.ts +4 -0
- package/src/import/provenance.d.ts +4 -0
- package/src/new-project.js +100 -11
- package/src/npm-safety.js +79 -0
- package/src/parser.d.ts +1 -0
- package/src/path-helpers.d.ts +1 -0
- package/src/path-helpers.js +20 -0
- package/src/project-config.js +1 -0
- package/src/reconcile/docs.d.ts +8 -0
- package/src/reconcile/journeys.d.ts +1 -0
- package/src/resolver.d.ts +1 -0
- package/src/runtime-support.js +29 -0
- package/src/sdlc/adopt.d.ts +1 -0
- package/src/sdlc/check.d.ts +1 -0
- package/src/sdlc/explain.d.ts +1 -0
- package/src/sdlc/release.d.ts +1 -0
- package/src/sdlc/scaffold.d.ts +1 -0
- package/src/sdlc/transition.d.ts +1 -0
- package/src/text-helpers.d.ts +6 -0
- package/src/text-helpers.js +245 -0
- package/src/topogram-config.js +306 -0
- package/src/validator.d.ts +2 -0
- package/src/workflows/adoption/index.js +26 -0
- package/src/workflows/docs-generate.js +262 -0
- package/src/workflows/docs-scan.js +703 -0
- package/src/workflows/docs.js +15 -0
- package/src/workflows/import-app/api.js +799 -0
- package/src/workflows/import-app/db.js +538 -0
- package/src/workflows/import-app/index.js +30 -0
- package/src/workflows/import-app/shared.js +218 -0
- package/src/workflows/import-app/ui.js +443 -0
- package/src/workflows/import-app/workflow.js +159 -0
- package/src/workflows/reconcile/adoption-plan.js +742 -0
- package/src/workflows/reconcile/auth.js +692 -0
- package/src/workflows/reconcile/bundle-core.js +600 -0
- package/src/workflows/reconcile/bundle-shared.js +75 -0
- package/src/workflows/reconcile/candidate-model.js +477 -0
- package/src/workflows/reconcile/canonical-surface.js +264 -0
- package/src/workflows/reconcile/gap-report.js +333 -0
- package/src/workflows/reconcile/ids.js +6 -0
- package/src/workflows/reconcile/impacts.js +625 -0
- package/src/workflows/reconcile/index.js +7 -0
- package/src/workflows/reconcile/renderers.js +461 -0
- package/src/workflows/reconcile/summary.js +90 -0
- package/src/workflows/reconcile/workflow.js +309 -0
- package/src/workflows/shared.js +189 -0
- package/src/workflows/types.d.ts +93 -0
- package/src/workflows.d.ts +1 -0
- package/src/workflows.js +10 -7652
|
@@ -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;
|
package/src/new-project.js
CHANGED
|
@@ -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
|
-
...
|
|
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.
|
|
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
|
-
|
|
2035
|
+
4. Regenerate:
|
|
2032
2036
|
npm run generate
|
|
2033
2037
|
|
|
2034
|
-
|
|
2038
|
+
5. Verify generated app:
|
|
2035
2039
|
npm run verify
|
|
2036
2040
|
|
|
2037
|
-
|
|
2041
|
+
6. Run locally:
|
|
2038
2042
|
npm run bootstrap
|
|
2039
2043
|
npm run dev
|
|
2040
2044
|
|
|
2041
|
-
|
|
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
|
+
}
|
package/src/parser.d.ts
ADDED
|
@@ -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
|
+
}
|
package/src/project-config.js
CHANGED
|
@@ -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
|
+
}
|