@nimiplatform/nimi-coding 0.3.1 → 0.5.0
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/CHANGELOG.md +32 -0
- package/CONTRIBUTING.md +3 -3
- package/README.md +297 -41
- package/README.zh-CN.md +306 -31
- package/bin/nimicoding.mjs +1 -1
- package/cli/commands/authority.mjs +804 -0
- package/cli/commands/clear.mjs +27 -7
- package/cli/commands/doctor.mjs +10 -1
- package/cli/commands/start.mjs +23 -27
- package/cli/commands/sync.mjs +21 -101
- package/cli/constants.mjs +7 -31
- package/cli/dev/check-authority-surface.mjs +182 -0
- package/cli/dev/check-product-conformance.mjs +889 -0
- package/cli/help.mjs +10 -17
- package/cli/index.mjs +6 -47
- package/cli/lib/authority/anchors.mjs +491 -0
- package/cli/lib/authority/audit.mjs +567 -0
- package/cli/lib/authority/change-candidates.mjs +807 -0
- package/cli/lib/authority/closed-sets.mjs +645 -0
- package/cli/lib/authority/compile.mjs +92 -0
- package/cli/lib/authority/compiled-snapshot.mjs +143 -0
- package/cli/lib/authority/consumers.mjs +520 -0
- package/cli/lib/authority/diagnostics.mjs +93 -0
- package/cli/lib/authority/diff.mjs +310 -0
- package/cli/lib/authority/discover.mjs +447 -0
- package/cli/lib/authority/format.mjs +169 -0
- package/cli/lib/authority/git-snapshot.mjs +720 -0
- package/cli/lib/authority/graph.mjs +351 -0
- package/cli/lib/authority/impact.mjs +125 -0
- package/cli/lib/authority/lexicon.mjs +55 -0
- package/cli/lib/authority/query.mjs +162 -0
- package/cli/lib/authority/repository-corpus.mjs +37 -0
- package/cli/lib/authority/repository-inventory.mjs +34 -0
- package/cli/lib/authority/review.mjs +212 -0
- package/cli/lib/authority/sarif.mjs +174 -0
- package/cli/lib/authority/scope-bindings.mjs +225 -0
- package/cli/lib/authority/source-map.mjs +105 -0
- package/cli/lib/authority/source-markdown.mjs +167 -0
- package/cli/lib/authority/source-yaml.mjs +308 -0
- package/cli/lib/authority/surface.mjs +97 -0
- package/cli/lib/authority/terms.mjs +422 -0
- package/cli/lib/authority/tracked-text.mjs +70 -0
- package/cli/lib/authority/validate.mjs +300 -0
- package/cli/lib/bootstrap.mjs +55 -71
- package/cli/lib/doctor.mjs +40 -123
- package/cli/lib/entrypoints.mjs +104 -105
- package/cli/lib/fs-helpers.mjs +139 -15
- package/cli/lib/internal/governance/config.mjs +0 -36
- package/cli/lib/shared.mjs +8 -21
- package/cli/lib/sync.mjs +81 -92
- package/cli/lib/value-helpers.mjs +0 -26
- package/cli/lib/yaml-helpers.mjs +1 -124
- package/cli/seeds/bootstrap.mjs +31 -87
- package/cli/seeds/seed-policy.yaml +9 -63
- package/contracts/authority-scope-bindings.schema.yaml +15 -0
- package/contracts/authority-source.schema.yaml +58 -0
- package/contracts/authority-verifier-bindings.schema.yaml +31 -0
- package/methodology/authority-authoring.yaml +127 -0
- package/methodology/core.yaml +34 -27
- package/package.json +10 -6
- package/spec/authority-anchors.yaml +99 -0
- package/spec/authority-audit.yaml +89 -0
- package/spec/authority-authoring.yaml +137 -0
- package/spec/authority-change-candidates.yaml +146 -0
- package/spec/authority-closed-sets.yaml +65 -0
- package/spec/authority-consumers.yaml +77 -0
- package/spec/authority-context.yaml +50 -0
- package/spec/authority-discovery.yaml +119 -0
- package/spec/authority-graph.yaml +59 -0
- package/spec/authority-impact.yaml +61 -0
- package/spec/authority-review.yaml +71 -0
- package/spec/authority-terms.yaml +63 -0
- package/spec/product-scope.yaml +37 -23
- package/cli/commands/blueprint-audit.mjs +0 -91
- package/cli/commands/classify-spec-tree.mjs +0 -5
- package/cli/commands/generate-spec-derived-docs.mjs +0 -151
- package/cli/commands/generate-spec-migration-plan.mjs +0 -30
- package/cli/commands/surface-validator-command.mjs +0 -49
- package/cli/commands/validate-domain-admission.mjs +0 -5
- package/cli/commands/validate-guidance-bodies.mjs +0 -5
- package/cli/commands/validate-placement.mjs +0 -5
- package/cli/commands/validate-projection-edges.mjs +0 -5
- package/cli/commands/validate-spec-audit.mjs +0 -27
- package/cli/commands/validate-spec-governance.mjs +0 -144
- package/cli/commands/validate-spec-tree.mjs +0 -27
- package/cli/commands/validate-table-family.mjs +0 -5
- package/cli/commands/validate-tracked-output-admission.mjs +0 -5
- package/cli/lib/blueprint-audit.mjs +0 -363
- package/cli/lib/contracts.mjs +0 -21
- package/cli/lib/internal/contracts-loaders.mjs +0 -47
- package/cli/lib/internal/contracts-parse.mjs +0 -192
- package/cli/lib/internal/governance/runner.mjs +0 -35
- package/cli/lib/internal/surface-taxonomy-validators.mjs +0 -1147
- package/cli/lib/internal/validators-shared.mjs +0 -12
- package/cli/lib/internal/validators-spec-helpers.mjs +0 -52
- package/cli/lib/internal/validators-spec.mjs +0 -380
- package/cli/lib/validators.mjs +0 -30
- package/config/bootstrap.yaml +0 -6
- package/config/spec-generation-inputs.yaml +0 -41
- package/contracts/domain-admission.schema.yaml +0 -56
- package/contracts/migration-inventory.schema.yaml +0 -15
- package/contracts/negative-fixtures.yaml +0 -26
- package/contracts/placement-contract.schema.yaml +0 -127
- package/contracts/projection-edge.schema.yaml +0 -46
- package/contracts/shared-enums.yaml +0 -63
- package/contracts/spec-generation-audit.schema.yaml +0 -46
- package/contracts/spec-generation-inputs.schema.yaml +0 -43
- package/contracts/spec-layout.schema.yaml +0 -32
- package/contracts/surface-taxonomy.schema.yaml +0 -139
- package/contracts/table-family.schema.yaml +0 -133
- package/contracts/tracked-output-admission.schema.yaml +0 -17
- package/methodology/four-closure-policy.yaml +0 -20
- package/methodology/role-separation-policy.yaml +0 -22
- package/methodology/spec-reconstruction.yaml +0 -69
- package/spec/_meta/spec-tree-model.yaml +0 -44
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
import { localize } from "../lib/ui.mjs";
|
|
4
|
-
import { buildValidatorCliReport, validateSpecTree } from "../lib/validators.mjs";
|
|
5
|
-
import { loadGovernanceConfig, requireProfile } from "../lib/internal/governance/config.mjs";
|
|
6
|
-
import { runCommand } from "../lib/internal/governance/runner.mjs";
|
|
7
|
-
|
|
8
|
-
function parseOptions(args) {
|
|
9
|
-
const options = {
|
|
10
|
-
profile: null,
|
|
11
|
-
scope: "all",
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
15
|
-
const arg = args[index];
|
|
16
|
-
|
|
17
|
-
if (arg === "--profile") {
|
|
18
|
-
const value = args[index + 1];
|
|
19
|
-
if (!value || value.startsWith("--")) {
|
|
20
|
-
return { ok: false, error: "nimicoding validate-spec-governance refused: --profile requires a value.\n" };
|
|
21
|
-
}
|
|
22
|
-
options.profile = value;
|
|
23
|
-
index += 1;
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (arg === "--scope") {
|
|
28
|
-
const value = args[index + 1];
|
|
29
|
-
if (!value || value.startsWith("--")) {
|
|
30
|
-
return { ok: false, error: "nimicoding validate-spec-governance refused: --scope requires a value.\n" };
|
|
31
|
-
}
|
|
32
|
-
options.scope = value;
|
|
33
|
-
index += 1;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
ok: false,
|
|
39
|
-
error: `nimicoding validate-spec-governance refused: unknown option ${arg}.\n`,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return { ok: true, options };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function resolveScopes(scope, governanceConfig) {
|
|
47
|
-
const configuredScopes = Object.keys(governanceConfig.specGovernance.validateCommands || {});
|
|
48
|
-
if (scope === "all") {
|
|
49
|
-
return {
|
|
50
|
-
ok: true,
|
|
51
|
-
scopes: configuredScopes,
|
|
52
|
-
error: null,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
if (!configuredScopes.includes(scope)) {
|
|
56
|
-
return {
|
|
57
|
-
ok: false,
|
|
58
|
-
scopes: [],
|
|
59
|
-
error: `nimicoding validate-spec-governance refused: unsupported --scope value ${scope}.\n`,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
return {
|
|
63
|
-
ok: true,
|
|
64
|
-
scopes: [scope],
|
|
65
|
-
error: null,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function runValidateSpecGovernance(args) {
|
|
70
|
-
const parsed = parseOptions(args);
|
|
71
|
-
if (!parsed.ok) {
|
|
72
|
-
process.stderr.write(localize(parsed.error, parsed.error));
|
|
73
|
-
return 2;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const governance = await loadGovernanceConfig(process.cwd());
|
|
77
|
-
if (!governance.ok) {
|
|
78
|
-
process.stderr.write(localize(
|
|
79
|
-
`nimicoding validate-spec-governance refused: ${governance.reason} at ${governance.path}.\n`,
|
|
80
|
-
`nimicoding validate-spec-governance 已拒绝:${governance.path} 的治理配置不可用。\n`,
|
|
81
|
-
));
|
|
82
|
-
return 2;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const profileCheck = requireProfile(governance.config, parsed.options.profile);
|
|
86
|
-
if (!profileCheck.ok) {
|
|
87
|
-
process.stderr.write(localize(
|
|
88
|
-
`nimicoding validate-spec-governance refused: ${profileCheck.error}.\n`,
|
|
89
|
-
`nimicoding validate-spec-governance 已拒绝:${profileCheck.error}。\n`,
|
|
90
|
-
));
|
|
91
|
-
return 2;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const scopeResolution = resolveScopes(parsed.options.scope, governance.config);
|
|
95
|
-
if (!scopeResolution.ok) {
|
|
96
|
-
process.stderr.write(localize(scopeResolution.error, scopeResolution.error));
|
|
97
|
-
return 2;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (parsed.options.scope === "all") {
|
|
101
|
-
if (governance.config.specGovernance.canonicalRoot !== ".nimi/spec") {
|
|
102
|
-
process.stderr.write(localize(
|
|
103
|
-
"nimicoding validate-spec-governance refused: spec_governance.canonical_root must be .nimi/spec.\n",
|
|
104
|
-
"nimicoding validate-spec-governance 已拒绝:spec_governance.canonical_root 必须为 .nimi/spec。\n",
|
|
105
|
-
));
|
|
106
|
-
return 2;
|
|
107
|
-
}
|
|
108
|
-
const canonicalRoot = path.resolve(process.cwd(), ".nimi/spec");
|
|
109
|
-
const treeReport = await validateSpecTree(canonicalRoot, { projectRoot: process.cwd() });
|
|
110
|
-
if (!treeReport.ok) {
|
|
111
|
-
const cliReport = buildValidatorCliReport("validate-spec-tree", canonicalRoot, treeReport);
|
|
112
|
-
process.stdout.write(`${JSON.stringify(cliReport, null, 2)}\n`);
|
|
113
|
-
return 1;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
let failed = false;
|
|
118
|
-
for (const scope of scopeResolution.scopes) {
|
|
119
|
-
const commands = governance.config.specGovernance.validateCommands[scope] || [];
|
|
120
|
-
if (commands.length === 0) {
|
|
121
|
-
process.stderr.write(localize(
|
|
122
|
-
`nimicoding validate-spec-governance refused: scope ${scope} is not configured in .nimi/config/governance.yaml.\n`,
|
|
123
|
-
`nimicoding validate-spec-governance 已拒绝:.nimi/config/governance.yaml 未配置 ${scope}。\n`,
|
|
124
|
-
));
|
|
125
|
-
return 2;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
for (const command of commands) {
|
|
129
|
-
const result = runCommand(command, { cwd: process.cwd() });
|
|
130
|
-
if (result.stdout) process.stdout.write(result.stdout);
|
|
131
|
-
if (result.stderr) process.stderr.write(result.stderr);
|
|
132
|
-
if (!result.ok) {
|
|
133
|
-
failed = true;
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (failed) {
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return failed ? 1 : 0;
|
|
144
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
import { buildValidatorCliReport, validateSpecTree } from "../lib/validators.mjs";
|
|
4
|
-
import { localize } from "../lib/ui.mjs";
|
|
5
|
-
|
|
6
|
-
export async function runValidateSpecTree(args) {
|
|
7
|
-
const normalized = args[0] === "--" ? args.slice(1) : args;
|
|
8
|
-
let targetRoot = ".nimi/spec";
|
|
9
|
-
|
|
10
|
-
if (normalized.length > 1) {
|
|
11
|
-
process.stderr.write(localize(
|
|
12
|
-
"nimicoding validate-spec-tree refused: expected zero or one path argument.\n",
|
|
13
|
-
"nimicoding validate-spec-tree 已拒绝:期望零个或一个路径参数。\n",
|
|
14
|
-
));
|
|
15
|
-
return 2;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (normalized.length === 1) {
|
|
19
|
-
targetRoot = normalized[0];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const absoluteRoot = path.resolve(process.cwd(), targetRoot);
|
|
23
|
-
const report = await validateSpecTree(absoluteRoot, { projectRoot: process.cwd() });
|
|
24
|
-
const cliReport = buildValidatorCliReport("validate-spec-tree", absoluteRoot, report);
|
|
25
|
-
process.stdout.write(`${JSON.stringify(cliReport, null, 2)}\n`);
|
|
26
|
-
return report.ok ? 0 : 1;
|
|
27
|
-
}
|
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import YAML from "yaml";
|
|
4
|
-
|
|
5
|
-
import { loadBlueprintReference, loadSpecGenerationInputsConfig } from "./contracts.mjs";
|
|
6
|
-
import { pathExists } from "./fs-helpers.mjs";
|
|
7
|
-
import {
|
|
8
|
-
localize,
|
|
9
|
-
styleHeading,
|
|
10
|
-
styleLabel,
|
|
11
|
-
styleStatus,
|
|
12
|
-
} from "./ui.mjs";
|
|
13
|
-
|
|
14
|
-
const BLUEPRINT_AUDIT_CONTRACT_VERSION = "nimicoding.blueprint-audit.v2";
|
|
15
|
-
const DEFAULT_REPORT_PATH = ".nimi/local/state/spec-generation/blueprint-equivalence-audit.json";
|
|
16
|
-
|
|
17
|
-
async function collectFiles(rootPath, relativePrefix = "") {
|
|
18
|
-
const info = await pathExists(rootPath);
|
|
19
|
-
if (!info || !info.isDirectory()) {
|
|
20
|
-
return [];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const entries = await readdir(rootPath, { withFileTypes: true });
|
|
24
|
-
const collected = [];
|
|
25
|
-
for (const entry of entries) {
|
|
26
|
-
const absoluteChildPath = path.join(rootPath, entry.name);
|
|
27
|
-
const relativeChildPath = relativePrefix ? path.posix.join(relativePrefix, entry.name) : entry.name;
|
|
28
|
-
if (entry.isDirectory()) {
|
|
29
|
-
collected.push(...await collectFiles(absoluteChildPath, relativeChildPath));
|
|
30
|
-
} else if (entry.isFile()) {
|
|
31
|
-
collected.push(relativeChildPath.split(path.sep).join(path.posix.sep));
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return collected.sort();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function collectDomainInventory(rootPath) {
|
|
39
|
-
const info = await pathExists(rootPath);
|
|
40
|
-
if (!info || !info.isDirectory()) {
|
|
41
|
-
return {
|
|
42
|
-
domains: [],
|
|
43
|
-
hasIndex: false,
|
|
44
|
-
files: [],
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const entries = await readdir(rootPath, { withFileTypes: true });
|
|
49
|
-
const domains = [];
|
|
50
|
-
for (const entry of entries) {
|
|
51
|
-
if (!entry.isDirectory()) {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const kernelPath = path.join(rootPath, entry.name, "kernel");
|
|
56
|
-
const kernelInfo = await pathExists(kernelPath);
|
|
57
|
-
if (kernelInfo?.isDirectory()) {
|
|
58
|
-
domains.push(entry.name);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const indexInfo = await pathExists(path.join(rootPath, "INDEX.md"));
|
|
63
|
-
const files = await collectFiles(rootPath);
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
domains: domains.sort(),
|
|
67
|
-
hasIndex: Boolean(indexInfo?.isFile()),
|
|
68
|
-
files,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function categorizeBlueprintFiles(files) {
|
|
73
|
-
const kernelMarkdown = [];
|
|
74
|
-
const kernelTables = [];
|
|
75
|
-
const kernelGenerated = [];
|
|
76
|
-
const domainGuides = [];
|
|
77
|
-
|
|
78
|
-
for (const relativePath of files) {
|
|
79
|
-
if (/^([^/]+)\/kernel\/[^/]+\.md$/.test(relativePath)) {
|
|
80
|
-
kernelMarkdown.push(relativePath);
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (/^([^/]+)\/kernel\/tables\/.+\.(ya?ml)$/.test(relativePath)) {
|
|
85
|
-
kernelTables.push(relativePath);
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (/^([^/]+)\/kernel\/generated\/.+\.md$/.test(relativePath)) {
|
|
90
|
-
kernelGenerated.push(relativePath);
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (/^[^/]+\/[^/]+\.md$/.test(relativePath)) {
|
|
95
|
-
domainGuides.push(relativePath);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
kernelMarkdown,
|
|
101
|
-
kernelTables,
|
|
102
|
-
kernelGenerated,
|
|
103
|
-
domainGuides,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function compareFileSets(blueprintFiles, canonicalFiles) {
|
|
108
|
-
const canonicalSet = new Set(canonicalFiles);
|
|
109
|
-
const blueprintSet = new Set(blueprintFiles);
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
present: blueprintFiles.filter((filePath) => canonicalSet.has(filePath)),
|
|
113
|
-
missing: blueprintFiles.filter((filePath) => !canonicalSet.has(filePath)),
|
|
114
|
-
extra: canonicalFiles.filter((filePath) => !blueprintSet.has(filePath)),
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function buildSummarySection(compareResult) {
|
|
119
|
-
return {
|
|
120
|
-
present: compareResult.present.length,
|
|
121
|
-
missing: compareResult.missing.length,
|
|
122
|
-
extra: compareResult.extra.length,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function collectIds(value, ids = []) {
|
|
127
|
-
if (Array.isArray(value)) {
|
|
128
|
-
for (const entry of value) {
|
|
129
|
-
collectIds(entry, ids);
|
|
130
|
-
}
|
|
131
|
-
return ids;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (value && typeof value === "object") {
|
|
135
|
-
if (typeof value.id === "string") {
|
|
136
|
-
ids.push(value.id);
|
|
137
|
-
}
|
|
138
|
-
for (const nestedValue of Object.values(value)) {
|
|
139
|
-
collectIds(nestedValue, ids);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return ids;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function compareRuleIds(blueprintAbsoluteRoot, canonicalAbsoluteRoot, presentTablePaths) {
|
|
147
|
-
const files = [];
|
|
148
|
-
const aggregateMissing = new Set();
|
|
149
|
-
const aggregateExtra = new Set();
|
|
150
|
-
const parseErrors = [];
|
|
151
|
-
|
|
152
|
-
for (const relativePath of presentTablePaths) {
|
|
153
|
-
try {
|
|
154
|
-
const blueprintText = await readFile(path.join(blueprintAbsoluteRoot, relativePath), "utf8");
|
|
155
|
-
const canonicalText = await readFile(path.join(canonicalAbsoluteRoot, relativePath), "utf8");
|
|
156
|
-
const blueprintIds = Array.from(new Set(collectIds(YAML.parse(blueprintText)))).sort();
|
|
157
|
-
const canonicalIds = Array.from(new Set(collectIds(YAML.parse(canonicalText)))).sort();
|
|
158
|
-
const missingIds = blueprintIds.filter((entry) => !canonicalIds.includes(entry));
|
|
159
|
-
const extraIds = canonicalIds.filter((entry) => !blueprintIds.includes(entry));
|
|
160
|
-
|
|
161
|
-
missingIds.forEach((entry) => aggregateMissing.add(entry));
|
|
162
|
-
extraIds.forEach((entry) => aggregateExtra.add(entry));
|
|
163
|
-
|
|
164
|
-
files.push({
|
|
165
|
-
path: relativePath,
|
|
166
|
-
blueprintIds: blueprintIds.length,
|
|
167
|
-
canonicalIds: canonicalIds.length,
|
|
168
|
-
missingIds,
|
|
169
|
-
extraIds,
|
|
170
|
-
});
|
|
171
|
-
} catch (error) {
|
|
172
|
-
parseErrors.push({
|
|
173
|
-
path: relativePath,
|
|
174
|
-
error: error instanceof Error ? error.message : String(error),
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
comparedFiles: files.length,
|
|
181
|
-
files,
|
|
182
|
-
parseErrors,
|
|
183
|
-
missingRuleIds: Array.from(aggregateMissing).sort(),
|
|
184
|
-
extraRuleIds: Array.from(aggregateExtra).sort(),
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export async function buildBlueprintAuditPayload(projectRoot, options = {}) {
|
|
189
|
-
const blueprintReference = await loadBlueprintReference(projectRoot);
|
|
190
|
-
const specGenerationInputs = await loadSpecGenerationInputsConfig(projectRoot);
|
|
191
|
-
|
|
192
|
-
const canonicalRoot = options.canonicalRoot
|
|
193
|
-
?? specGenerationInputs.canonicalTargetRoot
|
|
194
|
-
?? ".nimi/spec";
|
|
195
|
-
const blueprintRoot = options.blueprintRoot
|
|
196
|
-
?? blueprintReference.root
|
|
197
|
-
?? specGenerationInputs.benchmarkBlueprintRoot
|
|
198
|
-
?? null;
|
|
199
|
-
|
|
200
|
-
if (!blueprintRoot) {
|
|
201
|
-
return {
|
|
202
|
-
ok: false,
|
|
203
|
-
exitCode: 2,
|
|
204
|
-
inputError: true,
|
|
205
|
-
error: `${localize(
|
|
206
|
-
"nimicoding blueprint-audit refused: no blueprint root is declared; pass --blueprint-root or add project-local blueprint reference metadata.",
|
|
207
|
-
"nimicoding blueprint-audit 已拒绝:没有声明 blueprint root;请传入 --blueprint-root,或补充项目本地 blueprint reference 元数据。",
|
|
208
|
-
)}\n`,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const blueprintAbsoluteRoot = path.resolve(projectRoot, blueprintRoot);
|
|
213
|
-
const canonicalAbsoluteRoot = path.resolve(projectRoot, canonicalRoot);
|
|
214
|
-
const blueprintInventory = await collectDomainInventory(blueprintAbsoluteRoot);
|
|
215
|
-
const canonicalInventory = await collectDomainInventory(canonicalAbsoluteRoot);
|
|
216
|
-
|
|
217
|
-
const missingDomains = blueprintInventory.domains.filter((domainId) => !canonicalInventory.domains.includes(domainId));
|
|
218
|
-
const extraDomains = canonicalInventory.domains.filter((domainId) => !blueprintInventory.domains.includes(domainId));
|
|
219
|
-
const blueprintFiles = categorizeBlueprintFiles(blueprintInventory.files);
|
|
220
|
-
const canonicalFiles = categorizeBlueprintFiles(canonicalInventory.files);
|
|
221
|
-
|
|
222
|
-
const kernelMarkdown = compareFileSets(blueprintFiles.kernelMarkdown, canonicalFiles.kernelMarkdown);
|
|
223
|
-
const kernelTables = compareFileSets(blueprintFiles.kernelTables, canonicalFiles.kernelTables);
|
|
224
|
-
const kernelGenerated = {
|
|
225
|
-
present: [],
|
|
226
|
-
missing: [],
|
|
227
|
-
extra: [],
|
|
228
|
-
};
|
|
229
|
-
const domainGuides = compareFileSets(blueprintFiles.domainGuides, canonicalFiles.domainGuides);
|
|
230
|
-
const ruleIdPreservation = await compareRuleIds(
|
|
231
|
-
blueprintAbsoluteRoot,
|
|
232
|
-
canonicalAbsoluteRoot,
|
|
233
|
-
kernelTables.present,
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
const indexPresent = blueprintInventory.hasIndex ? canonicalInventory.hasIndex : true;
|
|
237
|
-
const ok = missingDomains.length === 0
|
|
238
|
-
&& kernelMarkdown.missing.length === 0
|
|
239
|
-
&& kernelTables.missing.length === 0
|
|
240
|
-
&& domainGuides.missing.length === 0
|
|
241
|
-
&& ruleIdPreservation.missingRuleIds.length === 0
|
|
242
|
-
&& ruleIdPreservation.parseErrors.length === 0
|
|
243
|
-
&& indexPresent;
|
|
244
|
-
|
|
245
|
-
const reportPath = path.join(projectRoot, DEFAULT_REPORT_PATH);
|
|
246
|
-
const nextSteps = [];
|
|
247
|
-
if (missingDomains.length > 0 || kernelMarkdown.missing.length > 0 || kernelTables.missing.length > 0 || !indexPresent) {
|
|
248
|
-
nextSteps.push("Resolve the missing blueprint structure under `/.nimi/spec/**` before claiming equivalence.");
|
|
249
|
-
}
|
|
250
|
-
if (domainGuides.missing.length > 0) {
|
|
251
|
-
nextSteps.push("Thin and map domain guides only after kernel coverage is in place.");
|
|
252
|
-
}
|
|
253
|
-
if (ruleIdPreservation.missingRuleIds.length > 0 || ruleIdPreservation.parseErrors.length > 0) {
|
|
254
|
-
nextSteps.push("Preserve benchmark rule IDs in canonical kernel tables before treating the canonical tree as benchmark-equivalent.");
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
contractVersion: BLUEPRINT_AUDIT_CONTRACT_VERSION,
|
|
259
|
-
ok,
|
|
260
|
-
exitCode: ok ? 0 : 1,
|
|
261
|
-
projectRoot,
|
|
262
|
-
blueprintRoot,
|
|
263
|
-
canonicalRoot,
|
|
264
|
-
blueprintReference,
|
|
265
|
-
specGenerationInputs: {
|
|
266
|
-
ok: specGenerationInputs.ok,
|
|
267
|
-
mode: specGenerationInputs.mode,
|
|
268
|
-
acceptanceMode: specGenerationInputs.acceptanceMode,
|
|
269
|
-
benchmarkBlueprintRoot: specGenerationInputs.benchmarkBlueprintRoot,
|
|
270
|
-
},
|
|
271
|
-
inventory: {
|
|
272
|
-
blueprintDomains: blueprintInventory.domains,
|
|
273
|
-
canonicalDomains: canonicalInventory.domains,
|
|
274
|
-
missingDomains,
|
|
275
|
-
extraDomains,
|
|
276
|
-
indexPresent,
|
|
277
|
-
},
|
|
278
|
-
comparison: {
|
|
279
|
-
kernelMarkdown: {
|
|
280
|
-
...buildSummarySection(kernelMarkdown),
|
|
281
|
-
missingPaths: kernelMarkdown.missing,
|
|
282
|
-
},
|
|
283
|
-
kernelTables: {
|
|
284
|
-
...buildSummarySection(kernelTables),
|
|
285
|
-
missingPaths: kernelTables.missing,
|
|
286
|
-
},
|
|
287
|
-
kernelGenerated: {
|
|
288
|
-
...buildSummarySection(kernelGenerated),
|
|
289
|
-
missingPaths: kernelGenerated.missing,
|
|
290
|
-
},
|
|
291
|
-
domainGuides: {
|
|
292
|
-
...buildSummarySection(domainGuides),
|
|
293
|
-
missingPaths: domainGuides.missing,
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
structuralGaps: {
|
|
297
|
-
missingDomains,
|
|
298
|
-
missingKernelMarkdown: kernelMarkdown.missing,
|
|
299
|
-
missingKernelTables: kernelTables.missing,
|
|
300
|
-
indexMissing: !indexPresent,
|
|
301
|
-
},
|
|
302
|
-
semanticMappingGaps: {
|
|
303
|
-
ruleIdPreservation,
|
|
304
|
-
},
|
|
305
|
-
derivedViewGaps: {
|
|
306
|
-
missingKernelGenerated: kernelGenerated.missing,
|
|
307
|
-
missingDomainGuides: domainGuides.missing,
|
|
308
|
-
},
|
|
309
|
-
artifactPath: reportPath,
|
|
310
|
-
nextSteps,
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
export function formatBlueprintAuditPayload(payload) {
|
|
315
|
-
const lines = [
|
|
316
|
-
styleHeading(`nimicoding blueprint-audit: ${payload.projectRoot}`),
|
|
317
|
-
"",
|
|
318
|
-
styleLabel(localize("Summary:", "摘要:")),
|
|
319
|
-
` - ${localize("status", "状态")}: ${styleStatus(payload.ok ? "ok" : "needs_attention")}`,
|
|
320
|
-
` - ${localize("blueprint root", "blueprint root")}: ${payload.blueprintRoot}`,
|
|
321
|
-
` - ${localize("canonical root", "canonical root")}: ${payload.canonicalRoot}`,
|
|
322
|
-
` - ${localize("missing domains", "缺失域")}: ${payload.inventory.missingDomains.length}`,
|
|
323
|
-
` - ${localize("kernel markdown missing", "缺失 kernel markdown")}: ${payload.comparison.kernelMarkdown.missing}`,
|
|
324
|
-
` - ${localize("kernel tables missing", "缺失 kernel tables")}: ${payload.comparison.kernelTables.missing}`,
|
|
325
|
-
` - ${localize("generated views missing", "缺失 generated 视图")}: ${payload.comparison.kernelGenerated.missing}`,
|
|
326
|
-
` - ${localize("guide mappings missing", "缺失 guide 映射")}: ${payload.comparison.domainGuides.missing}`,
|
|
327
|
-
` - ${localize("rule ids missing", "缺失 rule id")}: ${payload.semanticMappingGaps.ruleIdPreservation.missingRuleIds.length}`,
|
|
328
|
-
"",
|
|
329
|
-
styleLabel(localize("Coverage:", "覆盖情况:")),
|
|
330
|
-
` - ${localize("blueprint domains", "blueprint 域")}: ${payload.inventory.blueprintDomains.length}`,
|
|
331
|
-
` - ${localize("canonical domains", "canonical 域")}: ${payload.inventory.canonicalDomains.length}`,
|
|
332
|
-
` - ${localize("INDEX present", "INDEX 已存在")}: ${payload.inventory.indexPresent ? "true" : "false"}`,
|
|
333
|
-
];
|
|
334
|
-
|
|
335
|
-
if (payload.inventory.missingDomains.length > 0) {
|
|
336
|
-
lines.push(` - ${localize("missing domain ids", "缺失域 ID")}: ${payload.inventory.missingDomains.join(", ")}`);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (payload.semanticMappingGaps.ruleIdPreservation.missingRuleIds.length > 0) {
|
|
340
|
-
lines.push(` - ${localize("missing rule ids", "缺失 rule id")}: ${payload.semanticMappingGaps.ruleIdPreservation.missingRuleIds.join(", ")}`);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if (payload.semanticMappingGaps.ruleIdPreservation.parseErrors.length > 0) {
|
|
344
|
-
lines.push(` - ${localize("rule-id parse errors", "rule-id 解析错误")}: ${payload.semanticMappingGaps.ruleIdPreservation.parseErrors.length}`);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (payload.nextSteps.length > 0) {
|
|
348
|
-
lines.push("", styleLabel(localize("Next:", "下一步:")));
|
|
349
|
-
for (const step of payload.nextSteps) {
|
|
350
|
-
lines.push(` - ${localize(step, step)}`);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return `${lines.join("\n")}\n`;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
export async function writeBlueprintAuditArtifact(projectRoot, payload) {
|
|
358
|
-
await mkdir(path.dirname(payload.artifactPath), { recursive: true });
|
|
359
|
-
await writeFile(payload.artifactPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
360
|
-
return {
|
|
361
|
-
jsonRef: path.relative(projectRoot, payload.artifactPath).split(path.sep).join(path.posix.sep),
|
|
362
|
-
};
|
|
363
|
-
}
|
package/cli/lib/contracts.mjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
loadBlueprintReference as loadBlueprintReferenceInternal,
|
|
3
|
-
loadSpecGenerationAuditContract as loadSpecGenerationAuditContractInternal,
|
|
4
|
-
loadSpecGenerationInputsConfig as loadSpecGenerationInputsConfigInternal,
|
|
5
|
-
loadSpecGenerationInputsContract as loadSpecGenerationInputsContractInternal,
|
|
6
|
-
} from "./internal/contracts-loaders.mjs";
|
|
7
|
-
|
|
8
|
-
export function loadSpecGenerationInputsContract(projectRoot) {
|
|
9
|
-
return loadSpecGenerationInputsContractInternal(projectRoot);
|
|
10
|
-
}
|
|
11
|
-
export function loadSpecGenerationAuditContract(projectRoot) {
|
|
12
|
-
return loadSpecGenerationAuditContractInternal(projectRoot);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function loadSpecGenerationInputsConfig(projectRoot) {
|
|
16
|
-
return loadSpecGenerationInputsConfigInternal(projectRoot);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function loadBlueprintReference(projectRoot) {
|
|
20
|
-
return loadBlueprintReferenceInternal(projectRoot);
|
|
21
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
BLUEPRINT_REFERENCE_REF,
|
|
5
|
-
SPEC_GENERATION_AUDIT_CONTRACT_REF,
|
|
6
|
-
SPEC_GENERATION_INPUTS_CONTRACT_REF,
|
|
7
|
-
SPEC_GENERATION_INPUTS_REF,
|
|
8
|
-
} from "../../constants.mjs";
|
|
9
|
-
import { readTextIfFile } from "../fs-helpers.mjs";
|
|
10
|
-
import {
|
|
11
|
-
parseBlueprintReference,
|
|
12
|
-
parseSpecGenerationAuditContract,
|
|
13
|
-
parseSpecGenerationInputsConfig,
|
|
14
|
-
parseSpecGenerationInputsContract,
|
|
15
|
-
} from "./contracts-parse.mjs";
|
|
16
|
-
|
|
17
|
-
async function loadParsedYaml(projectRoot, relativePath, parse) {
|
|
18
|
-
const text = await readTextIfFile(path.join(projectRoot, relativePath));
|
|
19
|
-
return {
|
|
20
|
-
path: relativePath,
|
|
21
|
-
text,
|
|
22
|
-
...parse(text),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
export function loadSpecGenerationInputsContract(projectRoot) {
|
|
26
|
-
return loadParsedYaml(
|
|
27
|
-
projectRoot,
|
|
28
|
-
SPEC_GENERATION_INPUTS_CONTRACT_REF,
|
|
29
|
-
parseSpecGenerationInputsContract,
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function loadSpecGenerationAuditContract(projectRoot) {
|
|
34
|
-
return loadParsedYaml(
|
|
35
|
-
projectRoot,
|
|
36
|
-
SPEC_GENERATION_AUDIT_CONTRACT_REF,
|
|
37
|
-
parseSpecGenerationAuditContract,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function loadSpecGenerationInputsConfig(projectRoot) {
|
|
42
|
-
return loadParsedYaml(projectRoot, SPEC_GENERATION_INPUTS_REF, parseSpecGenerationInputsConfig);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function loadBlueprintReference(projectRoot) {
|
|
46
|
-
return loadParsedYaml(projectRoot, BLUEPRINT_REFERENCE_REF, parseBlueprintReference);
|
|
47
|
-
}
|