@nimiplatform/nimi-coding 0.4.0 → 0.6.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 +16 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +92 -55
- package/README.zh-CN.md +93 -56
- package/cli/commands/authority.mjs +80 -125
- package/cli/commands/clear.mjs +3 -2
- package/cli/commands/code.mjs +225 -0
- package/cli/commands/start.mjs +3 -2
- package/cli/constants.mjs +2 -1
- package/cli/dev/check-authority-surface.mjs +17 -8
- package/cli/dev/check-product-conformance.mjs +4 -32
- package/cli/dev/run-cli-smoke.mjs +6 -0
- package/cli/help.mjs +8 -3
- package/cli/index.mjs +3 -2
- package/cli/lib/authority/audit.mjs +262 -185
- package/cli/lib/authority/change-candidates.mjs +807 -0
- package/cli/lib/authority/closed-sets.mjs +7 -8
- package/cli/lib/authority/compile.mjs +6 -24
- package/cli/lib/authority/compiled-snapshot.mjs +143 -0
- package/cli/lib/authority/consumers.mjs +69 -19
- package/cli/lib/authority/diagnostics.mjs +10 -2
- package/cli/lib/authority/diff.mjs +77 -54
- package/cli/lib/authority/format.mjs +4 -32
- package/cli/lib/authority/git-snapshot.mjs +34 -379
- package/cli/lib/authority/graph.mjs +10 -10
- package/cli/lib/authority/impact.mjs +17 -144
- package/cli/lib/authority/review.mjs +33 -43
- package/cli/lib/authority/sarif.mjs +1 -8
- package/cli/lib/authority/source-yaml.mjs +0 -4
- package/cli/lib/authority/surface.mjs +19 -1
- package/cli/lib/authority/terms.mjs +10 -9
- package/cli/lib/authority/validate.mjs +6 -14
- package/cli/lib/code/authority-annotation-response.mjs +43 -0
- package/cli/lib/code/authority-annotation-scan.mjs +162 -0
- package/cli/lib/code/authority-annotations.mjs +293 -0
- package/cli/lib/code/context.mjs +710 -0
- package/cli/lib/code/response.mjs +165 -0
- package/cli/lib/doctor.mjs +3 -2
- package/cli/lib/entrypoints.mjs +13 -5
- package/cli/seeds/seed-policy.yaml +2 -2
- package/contracts/authority-source.schema.yaml +0 -6
- package/contracts/authority-verifier-bindings.schema.yaml +21 -10
- package/methodology/authority-authoring.yaml +68 -70
- package/methodology/core.yaml +10 -6
- package/package.json +5 -3
- package/spec/authority-audit.yaml +56 -52
- package/spec/authority-authoring.yaml +3 -16
- package/spec/authority-change-candidates.yaml +146 -0
- package/spec/authority-consumers.yaml +7 -0
- package/spec/authority-discovery.yaml +2 -2
- package/spec/authority-graph.yaml +1 -1
- package/spec/authority-impact.yaml +16 -39
- package/spec/authority-review.yaml +29 -47
- package/spec/authority-terms.yaml +5 -1
- package/spec/code-authority-annotations.yaml +42 -0
- package/spec/code-context.yaml +38 -0
- package/spec/product-scope.yaml +15 -9
- package/cli/commands/validate-ai-governance.mjs +0 -335
- package/cli/lib/authority/evidence-snapshot.mjs +0 -1
- package/cli/lib/authority/evidence.mjs +0 -1432
- package/cli/lib/internal/governance/ai/ai-context-budget-core.mjs +0 -377
- package/cli/lib/internal/governance/ai/ai-structure-budget-core.mjs +0 -376
- package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +0 -155
- package/cli/lib/internal/governance/ai/check-high-risk-doc-metadata-core.mjs +0 -173
- package/cli/lib/internal/governance/config.mjs +0 -114
- package/cli/lib/internal/governance/shared/read-yaml-with-fragments.mjs +0 -49
- package/contracts/authority-evidence-bindings.schema.yaml +0 -53
- package/contracts/authority-evidence-probe-results.schema.yaml +0 -38
- package/contracts/authority-impact-dispositions.schema.yaml +0 -13
- package/spec/authority-evidence.yaml +0 -200
|
@@ -6,39 +6,36 @@ import {
|
|
|
6
6
|
refuseAnchorsOutputBudget,
|
|
7
7
|
} from "../lib/authority/anchors.mjs";
|
|
8
8
|
import { auditAuthorityPath } from "../lib/authority/audit.mjs";
|
|
9
|
+
import {
|
|
10
|
+
CHANGE_CANDIDATE_CHANNELS,
|
|
11
|
+
changeCandidatesAuthorityPaths,
|
|
12
|
+
} from "../lib/authority/change-candidates.mjs";
|
|
9
13
|
import { checkAuthorityPath, compileAuthorityPath } from "../lib/authority/compile.mjs";
|
|
10
14
|
import { closedSetsAuthorityRepository } from "../lib/authority/closed-sets.mjs";
|
|
11
15
|
import { consumersAuthorityRepository } from "../lib/authority/consumers.mjs";
|
|
12
16
|
import { diffAuthorityPaths } from "../lib/authority/diff.mjs";
|
|
13
17
|
import { discoverAuthorityPath } from "../lib/authority/discover.mjs";
|
|
14
|
-
import { evidenceAuthorityRepository } from "../lib/authority/evidence.mjs";
|
|
15
18
|
import { AuthorityInputError, formatAuthorityFile } from "../lib/authority/format.mjs";
|
|
16
19
|
import { pathAuthorityPath, refsAuthorityPath, subgraphAuthorityPath } from "../lib/authority/graph.mjs";
|
|
17
20
|
import { impactAuthorityPaths } from "../lib/authority/impact.mjs";
|
|
18
21
|
import { contextAuthorityPath, queryAuthorityPath } from "../lib/authority/query.mjs";
|
|
19
22
|
import { reviewAuthorityRepository } from "../lib/authority/review.mjs";
|
|
20
23
|
import { authorityAuditResultToSarif } from "../lib/authority/sarif.mjs";
|
|
21
|
-
import { loadAuthorityContract } from "../lib/authority/source-yaml.mjs";
|
|
22
24
|
import { AUTHORING_SURFACE, AUTHORITY_SURFACE, integerFlagAdmission, booleanFlagAdmission } from "../lib/authority/surface.mjs";
|
|
23
25
|
import { AUTHORITY_TERM_GRAMMAR_HELP, termsAuthorityRepository } from "../lib/authority/terms.mjs";
|
|
24
26
|
|
|
25
|
-
const AUTHORITY_SOURCE_CONTRACT = await loadAuthorityContract();
|
|
26
|
-
const ADMITTED_AUTHORITY_FORMATS = new Set([
|
|
27
|
-
AUTHORITY_SOURCE_CONTRACT.format,
|
|
28
|
-
AUTHORITY_SOURCE_CONTRACT.migration_inputs?.format,
|
|
29
|
-
].filter((format) => typeof format === "string"));
|
|
30
|
-
|
|
31
27
|
// The single source for the authority command list; the root help renders it
|
|
32
28
|
// rather than keeping a second copy that drifts.
|
|
33
29
|
export const AUTHORITY_COMMAND_USAGE = [
|
|
34
|
-
"nimicoding authority fmt <file> [--check] [--
|
|
35
|
-
"nimicoding authority check <path> [--scope-bindings <file>] [--
|
|
30
|
+
"nimicoding authority fmt <file> [--check] [--json]",
|
|
31
|
+
"nimicoding authority check <path> [--scope-bindings <file>] [--json]",
|
|
36
32
|
"nimicoding authority compile <path> [--json]",
|
|
37
33
|
"nimicoding authority anchors <repository-path> --spec <corpus-path> [--scope-bindings <file>] [--embedded-paths] --max-units <positive-safe-integer> --max-anchors <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
38
34
|
"nimicoding authority consumers <repository-path> --spec <corpus-path> [--external-root <scheme>=<path>]... --max-files <positive-safe-integer> --max-refs <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
39
35
|
"nimicoding authority closed-sets <repository-path> --spec <corpus-path> --max-units <positive-safe-integer> --max-sets <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
40
36
|
"nimicoding authority terms <repository-path> --spec <corpus-path> [--term <t>] [--code] --max-units <positive-safe-integer> --max-terms <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
41
37
|
"nimicoding authority discover <path> <query> [--kind <definition|rule>] [--owner <exact-owner>] [--scope <exact-scope>] [--lifecycle <active|removed>] --max-candidates <positive-safe-integer> --max-snippet-terms <positive-safe-integer> --max-bytes <positive-safe-integer> [--preview-direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --max-edges <positive-safe-integer>] [--json]",
|
|
38
|
+
"nimicoding authority change-candidates <before-path> <after-path> --channels <comma-separated-closed-set> --max-candidates <positive-safe-integer> --max-reasons <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
42
39
|
"nimicoding authority query <path> <id> --max-bytes <positive-integer> [--json]",
|
|
43
40
|
"nimicoding authority context <path> <id> --max-units <positive-integer> --max-bytes <positive-integer> [--json]",
|
|
44
41
|
"nimicoding authority refs <path> <id> --direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
@@ -46,9 +43,8 @@ export const AUTHORITY_COMMAND_USAGE = [
|
|
|
46
43
|
"nimicoding authority subgraph <path> <id> --direction <incoming|outgoing|both> --relations <comma-separated-relation-types> --depth <positive-safe-integer> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
47
44
|
"nimicoding authority audit <path> --bindings <file> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json|--sarif]",
|
|
48
45
|
"nimicoding authority diff <before-path> <after-path> --max-bytes <positive-integer> [--json]",
|
|
49
|
-
"nimicoding authority impact <before-path> <after-path> --
|
|
50
|
-
"nimicoding authority review <repository-path> --base <git-ref> --bindings <file> --
|
|
51
|
-
"nimicoding authority evidence <repository-path> --bindings <tracked-.nimi/config-path> [--probe-results <.nimi/local-path>] --max-units <positive-safe-integer> --max-bindings <positive-safe-integer> --max-locators <positive-safe-integer> --max-edges <positive-safe-integer> --max-input-bytes <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
46
|
+
"nimicoding authority impact <before-path> <after-path> --max-bytes <positive-integer> [--json]",
|
|
47
|
+
"nimicoding authority review <repository-path> --base <git-ref> --bindings <file> --max-units <positive-safe-integer> --max-edges <positive-safe-integer> --max-bytes <positive-safe-integer> [--json]",
|
|
52
48
|
];
|
|
53
49
|
|
|
54
50
|
const USAGE = [
|
|
@@ -62,26 +58,27 @@ const USAGE = [
|
|
|
62
58
|
const AUTHORITY_IDENTIFIER = /^[a-z](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z](?:[a-z0-9-]*[a-z0-9])?)+$/;
|
|
63
59
|
|
|
64
60
|
function parseOptions(subcommand, args) {
|
|
65
|
-
const options = { json: false, sarif: false, check: false,
|
|
61
|
+
const options = { json: false, sarif: false, check: false, embeddedPaths: false, code: false, path: null, repositoryPath: null, spec: null, term: null, base: null, id: null, fromId: null, toId: null, query: null, beforePath: null, afterPath: null, bindings: null, scopeBindings: null, kind: null, owner: null, scope: null, lifecycle: null, direction: null, previewDirection: null, traversal: null, relations: null, channels: null, depth: null, maxHops: null, maxCandidates: null, maxReasons: null, maxSnippetTerms: null, maxUnits: null, maxTerms: null, maxSets: null, maxAnchors: null, maxFiles: null, maxRefs: null, maxEdges: null, maxBytes: null, externalRoots: [] };
|
|
66
62
|
const graphCommands = ["refs", "path", "subgraph"];
|
|
67
63
|
const reportIntegerAdmission = integerFlagAdmission();
|
|
68
64
|
const reportIntegerCommands = (flag) => reportIntegerAdmission[flag] ?? [];
|
|
65
|
+
const surfaceValueCommands = (flag) => Object.entries(AUTHORITY_SURFACE)
|
|
66
|
+
.filter(([, entry]) => entry.valueFlags.includes(flag))
|
|
67
|
+
.map(([command]) => command);
|
|
69
68
|
const integerOptions = {
|
|
70
|
-
"--max-candidates": ["maxCandidates", ["discover"]],
|
|
69
|
+
"--max-candidates": ["maxCandidates", ["discover", "change-candidates"]],
|
|
70
|
+
"--max-reasons": ["maxReasons", ["change-candidates"]],
|
|
71
71
|
"--max-snippet-terms": ["maxSnippetTerms", ["discover"]],
|
|
72
72
|
// The report products' integer flags come from the enforced surface table,
|
|
73
73
|
// so the parser and the product contracts cannot drift apart silently.
|
|
74
|
-
"--max-units": ["maxUnits", ["context", "
|
|
74
|
+
"--max-units": ["maxUnits", ["context", "review", ...reportIntegerCommands("--max-units"), ...graphCommands]],
|
|
75
75
|
"--max-terms": ["maxTerms", reportIntegerCommands("--max-terms")],
|
|
76
76
|
"--max-sets": ["maxSets", reportIntegerCommands("--max-sets")],
|
|
77
77
|
"--max-anchors": ["maxAnchors", reportIntegerCommands("--max-anchors")],
|
|
78
78
|
"--max-files": ["maxFiles", reportIntegerCommands("--max-files")],
|
|
79
79
|
"--max-refs": ["maxRefs", reportIntegerCommands("--max-refs")],
|
|
80
|
-
"--max-
|
|
81
|
-
"--max-
|
|
82
|
-
"--max-edges": ["maxEdges", ["audit", "review", "evidence", "discover", ...graphCommands]],
|
|
83
|
-
"--max-input-bytes": ["maxInputBytes", ["evidence"]],
|
|
84
|
-
"--max-bytes": ["maxBytes", ["discover", "query", "context", "audit", "diff", "impact", "review", "evidence", "anchors", "consumers", "terms", "closed-sets", ...graphCommands]],
|
|
80
|
+
"--max-edges": ["maxEdges", ["review", "discover", ...reportIntegerCommands("--max-edges"), ...graphCommands]],
|
|
81
|
+
"--max-bytes": ["maxBytes", ["discover", "change-candidates", "query", "context", "diff", "impact", "review", ...reportIntegerCommands("--max-bytes"), ...graphCommands]],
|
|
85
82
|
"--max-hops": ["maxHops", ["path"]],
|
|
86
83
|
"--depth": ["depth", ["subgraph"]],
|
|
87
84
|
};
|
|
@@ -94,14 +91,13 @@ function parseOptions(subcommand, args) {
|
|
|
94
91
|
continue;
|
|
95
92
|
}
|
|
96
93
|
if (!terminated && arg.startsWith("--")) {
|
|
97
|
-
if (arg === "--json" && !options.json) {
|
|
94
|
+
if (arg === "--json" && !options.json && (AUTHORITY_SURFACE[subcommand]?.booleanFlags ?? ["json"]).includes("json")) {
|
|
98
95
|
if (options.sarif) return { ok: false, error: "authority audit requires --json and --sarif to be mutually exclusive" };
|
|
99
96
|
options.json = true;
|
|
100
|
-
} else if (arg === "--sarif" && subcommand
|
|
97
|
+
} else if (arg === "--sarif" && AUTHORITY_SURFACE[subcommand]?.booleanFlags.includes("sarif") && !options.sarif) {
|
|
101
98
|
if (options.json) return { ok: false, error: "authority audit requires --json and --sarif to be mutually exclusive" };
|
|
102
99
|
options.sarif = true;
|
|
103
100
|
} else if (arg === "--check" && AUTHORING_SURFACE[subcommand]?.booleanFlags.includes("check") && !options.check) options.check = true;
|
|
104
|
-
else if (arg === "--upgrade" && AUTHORING_SURFACE[subcommand]?.booleanFlags.includes("upgrade") && !options.upgrade) options.upgrade = true;
|
|
105
101
|
else if (arg === "--embedded-paths" && booleanFlagAdmission("embedded-paths").includes(subcommand) && !options.embeddedPaths) options.embeddedPaths = true;
|
|
106
102
|
else if (arg === "--code" && booleanFlagAdmission("code").includes(subcommand) && !options.code) options.code = true;
|
|
107
103
|
else if (arg === "--external-root" && (AUTHORITY_SURFACE[subcommand]?.repeatableFlags ?? []).includes("external-root")) {
|
|
@@ -156,6 +152,18 @@ function parseOptions(subcommand, args) {
|
|
|
156
152
|
if (!["directed", "incidence"].includes(value)) return { ok: false, error: "authority path requires --traversal directed or incidence" };
|
|
157
153
|
options.traversal = value;
|
|
158
154
|
index += 1;
|
|
155
|
+
} else if (arg === "--channels" && subcommand === "change-candidates" && options.channels === null) {
|
|
156
|
+
const value = args[index + 1];
|
|
157
|
+
const channels = value?.split(",") ?? [];
|
|
158
|
+
if (
|
|
159
|
+
channels.length === 0
|
|
160
|
+
|| channels.some((entry) => entry.length === 0 || !CHANGE_CANDIDATE_CHANNELS.includes(entry))
|
|
161
|
+
|| new Set(channels).size !== channels.length
|
|
162
|
+
) {
|
|
163
|
+
return { ok: false, error: `authority change-candidates requires a non-empty unique closed --channels set containing only ${CHANGE_CANDIDATE_CHANNELS.join(", ")}` };
|
|
164
|
+
}
|
|
165
|
+
options.channels = channels;
|
|
166
|
+
index += 1;
|
|
159
167
|
} else if (arg === "--relations" && [...graphCommands, "discover"].includes(subcommand) && options.relations === null) {
|
|
160
168
|
const value = args[index + 1];
|
|
161
169
|
const relations = value?.split(",") ?? [];
|
|
@@ -164,12 +172,7 @@ function parseOptions(subcommand, args) {
|
|
|
164
172
|
}
|
|
165
173
|
options.relations = relations.sort();
|
|
166
174
|
index += 1;
|
|
167
|
-
} else if (arg === "--
|
|
168
|
-
const value = args[index + 1];
|
|
169
|
-
if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --dispositions followed by one file` };
|
|
170
|
-
options.dispositions = value;
|
|
171
|
-
index += 1;
|
|
172
|
-
} else if (arg === "--bindings" && ["audit", "review", "evidence"].includes(subcommand) && options.bindings === null) {
|
|
175
|
+
} else if (arg === "--bindings" && ["review", ...surfaceValueCommands("bindings")].includes(subcommand) && options.bindings === null) {
|
|
173
176
|
const value = args[index + 1];
|
|
174
177
|
if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --bindings followed by one file` };
|
|
175
178
|
options.bindings = value;
|
|
@@ -179,13 +182,6 @@ function parseOptions(subcommand, args) {
|
|
|
179
182
|
if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --scope-bindings followed by one file` };
|
|
180
183
|
options.scopeBindings = value;
|
|
181
184
|
index += 1;
|
|
182
|
-
} else if (arg === "--require-format" && subcommand === "check" && options.requireFormat === null) {
|
|
183
|
-
const value = args[index + 1];
|
|
184
|
-
if (!ADMITTED_AUTHORITY_FORMATS.has(value)) {
|
|
185
|
-
return { ok: false, error: `authority check requires --require-format followed by an admitted source format: ${[...ADMITTED_AUTHORITY_FORMATS].join(" or ")}` };
|
|
186
|
-
}
|
|
187
|
-
options.requireFormat = value;
|
|
188
|
-
index += 1;
|
|
189
185
|
} else if (arg === "--spec" && ["anchors", "consumers", "terms", "closed-sets"].includes(subcommand) && options.spec === null) {
|
|
190
186
|
const value = args[index + 1];
|
|
191
187
|
if (!value || value.startsWith("--")) return { ok: false, error: `authority ${subcommand} requires --spec followed by one corpus path` };
|
|
@@ -196,11 +192,6 @@ function parseOptions(subcommand, args) {
|
|
|
196
192
|
if (!value || value.startsWith("--") || /[\0\r\n]/u.test(value)) return { ok: false, error: "authority terms requires --term followed by one non-empty single-line term" };
|
|
197
193
|
options.term = value;
|
|
198
194
|
index += 1;
|
|
199
|
-
} else if (arg === "--probe-results" && subcommand === "evidence" && options.probeResults === null) {
|
|
200
|
-
const value = args[index + 1];
|
|
201
|
-
if (!value || value.startsWith("--")) return { ok: false, error: "authority evidence requires --probe-results followed by one repository-relative file" };
|
|
202
|
-
options.probeResults = value;
|
|
203
|
-
index += 1;
|
|
204
195
|
} else if (arg === "--base" && subcommand === "review" && options.base === null) {
|
|
205
196
|
const value = args[index + 1];
|
|
206
197
|
if (!value || value.startsWith("-")) return { ok: false, error: "authority review requires --base followed by one non-option Git ref" };
|
|
@@ -211,17 +202,17 @@ function parseOptions(subcommand, args) {
|
|
|
211
202
|
}
|
|
212
203
|
positionals.push(arg);
|
|
213
204
|
}
|
|
214
|
-
const expected = subcommand === "path" ? 3 : ["discover", "query", "context", "refs", "subgraph", "diff", "impact"].includes(subcommand) ? 2 : 1;
|
|
205
|
+
const expected = subcommand === "path" ? 3 : ["discover", "change-candidates", "query", "context", "refs", "subgraph", "diff", "impact"].includes(subcommand) ? 2 : 1;
|
|
215
206
|
if (positionals.length !== expected) {
|
|
216
207
|
const requirement = subcommand === "discover"
|
|
217
208
|
? "one path and one query"
|
|
218
|
-
: ["diff", "impact"].includes(subcommand)
|
|
209
|
+
: ["change-candidates", "diff", "impact"].includes(subcommand)
|
|
219
210
|
? "one before path and one after path"
|
|
220
|
-
: subcommand === "path" ? "one path, one exact from ID, and one exact to ID" : ["review", "
|
|
211
|
+
: subcommand === "path" ? "one path, one exact from ID, and one exact to ID" : ["review", "anchors", "consumers", "terms", "closed-sets"].includes(subcommand) ? "one repository path" : expected === 1 ? "one path" : "one path and one exact ID";
|
|
221
212
|
return { ok: false, error: `authority ${subcommand} requires exactly ${requirement}` };
|
|
222
213
|
}
|
|
223
|
-
if (["diff", "impact"].includes(subcommand)) [options.beforePath, options.afterPath] = positionals;
|
|
224
|
-
else if (["review", "
|
|
214
|
+
if (["change-candidates", "diff", "impact"].includes(subcommand)) [options.beforePath, options.afterPath] = positionals;
|
|
215
|
+
else if (["review", "anchors", "consumers", "terms", "closed-sets"].includes(subcommand)) [options.repositoryPath] = positionals;
|
|
225
216
|
else if (subcommand === "path") [options.path, options.fromId, options.toId] = positionals;
|
|
226
217
|
else [options.path, options.id] = positionals;
|
|
227
218
|
if (subcommand === "discover") {
|
|
@@ -235,29 +226,24 @@ function parseOptions(subcommand, args) {
|
|
|
235
226
|
const provided = previewOptions.filter((value) => value !== null).length;
|
|
236
227
|
if (provided !== 0 && provided !== previewOptions.length) return { ok: false, error: "authority discover requires --preview-direction, --relations, and --max-edges together" };
|
|
237
228
|
}
|
|
229
|
+
if (subcommand === "change-candidates" && options.channels === null) return { ok: false, error: "authority change-candidates requires --channels followed by a non-empty closed comma-separated set" };
|
|
230
|
+
if (subcommand === "change-candidates" && options.maxCandidates === null) return { ok: false, error: "authority change-candidates requires --max-candidates followed by a positive integer" };
|
|
231
|
+
if (subcommand === "change-candidates" && options.maxReasons === null) return { ok: false, error: "authority change-candidates requires --max-reasons followed by a positive integer" };
|
|
238
232
|
if (["context", "anchors", "terms", "closed-sets", ...graphCommands].includes(subcommand) && options.maxUnits === null) return { ok: false, error: `authority ${subcommand} requires --max-units followed by a positive integer` };
|
|
239
233
|
if (graphCommands.includes(subcommand) && options.maxEdges === null) return { ok: false, error: `authority ${subcommand} requires --max-edges followed by a positive integer` };
|
|
240
|
-
if (["discover", "query", "context", "audit", "diff", "impact", "review", "
|
|
234
|
+
if (["discover", "change-candidates", "query", "context", "audit", "diff", "impact", "review", "anchors", "consumers", "terms", "closed-sets", ...graphCommands].includes(subcommand) && options.maxBytes === null) return { ok: false, error: `authority ${subcommand} requires --max-bytes followed by a positive integer` };
|
|
241
235
|
if (["refs", "subgraph"].includes(subcommand) && options.direction === null) return { ok: false, error: `authority ${subcommand} requires --direction` };
|
|
242
236
|
if (subcommand === "path" && options.traversal === null) return { ok: false, error: "authority path requires --traversal" };
|
|
243
237
|
if (graphCommands.includes(subcommand) && options.relations === null) return { ok: false, error: `authority ${subcommand} requires --relations` };
|
|
244
238
|
if (subcommand === "path" && options.maxHops === null) return { ok: false, error: "authority path requires --max-hops followed by a positive integer" };
|
|
245
239
|
if (subcommand === "subgraph" && options.depth === null) return { ok: false, error: "authority subgraph requires --depth followed by a positive integer" };
|
|
246
|
-
if (subcommand === "impact" && options.dispositions === null) return { ok: false, error: "authority impact requires --dispositions followed by one file" };
|
|
247
240
|
if (subcommand === "audit" && options.bindings === null) return { ok: false, error: "authority audit requires --bindings followed by one file" };
|
|
248
241
|
if (subcommand === "audit" && options.maxUnits === null) return { ok: false, error: "authority audit requires --max-units followed by a positive integer" };
|
|
249
242
|
if (subcommand === "audit" && options.maxEdges === null) return { ok: false, error: "authority audit requires --max-edges followed by a positive integer" };
|
|
250
243
|
if (subcommand === "review" && options.base === null) return { ok: false, error: "authority review requires --base followed by one Git ref" };
|
|
251
244
|
if (subcommand === "review" && options.bindings === null) return { ok: false, error: "authority review requires --bindings followed by one file" };
|
|
252
|
-
if (subcommand === "review" && options.dispositions === null) return { ok: false, error: "authority review requires --dispositions followed by one file" };
|
|
253
245
|
if (subcommand === "review" && options.maxUnits === null) return { ok: false, error: "authority review requires --max-units followed by a positive integer" };
|
|
254
246
|
if (subcommand === "review" && options.maxEdges === null) return { ok: false, error: "authority review requires --max-edges followed by a positive integer" };
|
|
255
|
-
if (subcommand === "evidence" && options.bindings === null) return { ok: false, error: "authority evidence requires --bindings followed by one repository-relative file" };
|
|
256
|
-
if (subcommand === "evidence" && options.maxUnits === null) return { ok: false, error: "authority evidence requires --max-units followed by a positive integer" };
|
|
257
|
-
if (subcommand === "evidence" && options.maxBindings === null) return { ok: false, error: "authority evidence requires --max-bindings followed by a positive integer" };
|
|
258
|
-
if (subcommand === "evidence" && options.maxLocators === null) return { ok: false, error: "authority evidence requires --max-locators followed by a positive integer" };
|
|
259
|
-
if (subcommand === "evidence" && options.maxEdges === null) return { ok: false, error: "authority evidence requires --max-edges followed by a positive integer" };
|
|
260
|
-
if (subcommand === "evidence" && options.maxInputBytes === null) return { ok: false, error: "authority evidence requires --max-input-bytes followed by a positive integer" };
|
|
261
247
|
if (subcommand === "anchors" && options.spec === null) return { ok: false, error: "authority anchors requires --spec followed by one corpus path" };
|
|
262
248
|
if (subcommand === "anchors" && options.maxAnchors === null) return { ok: false, error: "authority anchors requires --max-anchors followed by a positive integer" };
|
|
263
249
|
if (subcommand === "consumers" && options.spec === null) return { ok: false, error: "authority consumers requires --spec followed by one corpus path" };
|
|
@@ -284,19 +270,17 @@ function humanAuditLocation(location) {
|
|
|
284
270
|
function formatAuditHuman(report) {
|
|
285
271
|
const audit = report.audit;
|
|
286
272
|
const operationStatus = audit?.operationStatus ?? report.semantic_status;
|
|
287
|
-
const
|
|
288
|
-
const complete = audit?.complete ?? false;
|
|
273
|
+
const blockingStatus = audit?.blockingStatus ?? "not_evaluated";
|
|
289
274
|
const lines = [
|
|
290
|
-
`nimicoding authority audit: operation=${operationStatus};
|
|
275
|
+
`nimicoding authority audit: operation=${operationStatus}; blocking=${blockingStatus}`,
|
|
291
276
|
`files: ${report.summary.files}; units: ${report.summary.units}; diagnostics: ${report.summary.diagnostics}`,
|
|
292
277
|
`audit bytes: ${report.audit_bytes}`,
|
|
293
278
|
];
|
|
294
279
|
if (audit) {
|
|
295
|
-
lines.push(`bindings:
|
|
296
|
-
lines.push(`returned:
|
|
280
|
+
lines.push(`bindings: configured=${audit.counts.bindings.configured}; evaluated=${audit.counts.bindings.evaluated}`);
|
|
281
|
+
lines.push(`returned: findings=${audit.counts.returned.findings}`);
|
|
297
282
|
lines.push(`traversal: units=${audit.counts.traversal.units}; edges=${audit.counts.traversal.edges}`);
|
|
298
283
|
for (const finding of audit.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
|
|
299
|
-
for (const gap of audit.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
|
|
300
284
|
} else lines.push("counts: unavailable (audit refused before evaluation)");
|
|
301
285
|
for (const diagnostic of report.diagnostics) lines.push(humanDiagnostic(diagnostic));
|
|
302
286
|
return `${lines.join("\n")}\n`;
|
|
@@ -310,6 +294,7 @@ function outputReport(report, json) {
|
|
|
310
294
|
if (report.changed !== undefined) process.stdout.write(`changed: ${report.changed}\n`);
|
|
311
295
|
if (report.packet_bytes !== undefined) process.stdout.write(`packet bytes: ${report.packet_bytes}\n`);
|
|
312
296
|
if (report.discovery_bytes !== undefined) process.stdout.write(`discovery bytes: ${report.discovery_bytes}\n`);
|
|
297
|
+
if (report.candidate_bytes !== undefined) process.stdout.write(`change-candidate bytes: ${report.candidate_bytes}\n`);
|
|
313
298
|
if (report.payload_bytes !== undefined) process.stdout.write(`semantic payload bytes: ${report.payload_bytes}\n`);
|
|
314
299
|
if (report.graph_bytes !== undefined) process.stdout.write(`graph bytes: ${report.graph_bytes}\n`);
|
|
315
300
|
if (report.packet) {
|
|
@@ -321,6 +306,12 @@ function outputReport(report, json) {
|
|
|
321
306
|
process.stdout.write(`candidates: ${report.discovery.candidates.map((candidate) => candidate.id).join(", ")}\n`);
|
|
322
307
|
if (report.discovery.relationPreview) process.stdout.write(`relation preview: units=${report.discovery.relationPreview.counts.units}; edges=${report.discovery.relationPreview.counts.edges}; complete=${report.discovery.relationPreview.complete}\n`);
|
|
323
308
|
}
|
|
309
|
+
if (report.change_candidates) {
|
|
310
|
+
const product = report.change_candidates;
|
|
311
|
+
process.stdout.write(`requested channels: ${product.requestedChannels.join(", ")}\n`);
|
|
312
|
+
process.stdout.write(`changed roots: ${product.counts.changedRoots}; eligible survivors: ${product.counts.eligibleAfterUnits}; recalled candidates: ${product.counts.candidates}; reasons: ${product.counts.reasons}\n`);
|
|
313
|
+
process.stdout.write(`candidates: ${product.candidates.map((candidate) => candidate.id).join(", ")}\n`);
|
|
314
|
+
}
|
|
324
315
|
if (report.diff) process.stdout.write(`semantic changes: ${report.diff.summary.changes}\n`);
|
|
325
316
|
if (report.impact) process.stdout.write(`impacted units: ${report.impact.impactedUnits.map((unit) => unit.id).join(", ")}\n`);
|
|
326
317
|
if (report.graph) {
|
|
@@ -348,19 +339,18 @@ function outputAuditReport(report, output) {
|
|
|
348
339
|
function formatReviewHuman(report) {
|
|
349
340
|
const review = report.review;
|
|
350
341
|
const lines = [
|
|
351
|
-
`nimicoding authority review: operation=${review?.operationStatus ?? "refused"};
|
|
342
|
+
`nimicoding authority review: operation=${review?.operationStatus ?? "refused"}; analysis-complete=${review?.analysisComplete ?? false}; configured-audit-blocking=${review?.audit?.blockingStatus ?? "not_evaluated"}`,
|
|
352
343
|
];
|
|
353
344
|
if (review) {
|
|
354
345
|
const base = review.snapshots.base;
|
|
355
346
|
const worktree = review.snapshots.worktree;
|
|
356
|
-
const unresolved = review.impact.obligations.filter((obligation) => obligation.disposition === null).length;
|
|
357
347
|
lines.push(`base: commit=${base.commitOid}; identity=${base.contentIdentity}; files=${base.counts.files}; units=${base.counts.units}`);
|
|
358
348
|
lines.push(`worktree: identity=${worktree.contentIdentity}; files=${worktree.counts.files}; units=${worktree.counts.units}`);
|
|
359
|
-
lines.push(`semantic changes: ${review.diff.summary.changes}; impacted units: ${review.impact.impactedUnits.length};
|
|
360
|
-
lines.push(`current audit:
|
|
349
|
+
lines.push(`semantic changes: ${review.diff.summary.changes}; impacted units: ${review.impact.impactedUnits.length}; declared obligations: ${review.impact.obligations.length}`);
|
|
350
|
+
lines.push(`current configured audit: blocking=${review.audit.blockingStatus}; findings=${review.audit.findings.length}`);
|
|
351
|
+
lines.push(`business semantics: ${review.businessSemanticStatus}; implementation conformance: ${review.implementationConformanceStatus}`);
|
|
361
352
|
lines.push(`review bytes: ${report.review_bytes}`);
|
|
362
353
|
for (const finding of review.audit.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
|
|
363
|
-
for (const gap of review.audit.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
|
|
364
354
|
} else {
|
|
365
355
|
lines.push("review unavailable; partial=false");
|
|
366
356
|
}
|
|
@@ -373,32 +363,6 @@ function outputReviewReport(report, json) {
|
|
|
373
363
|
else process.stdout.write(formatReviewHuman(report));
|
|
374
364
|
}
|
|
375
365
|
|
|
376
|
-
function formatEvidenceHuman(report) {
|
|
377
|
-
const evidence = report.evidence;
|
|
378
|
-
const lines = [
|
|
379
|
-
`nimicoding authority evidence: operation=${evidence?.operationStatus ?? "refused"}; evidence=${evidence?.evidenceStatus ?? "indeterminate"}; complete=${evidence?.complete ?? false}; conformance=${evidence?.conformanceStatus ?? "not_evaluated"}`,
|
|
380
|
-
];
|
|
381
|
-
if (evidence) {
|
|
382
|
-
lines.push(`snapshot: head=${evidence.identities.headCommitOid}; authority=${evidence.identities.authorityContentIdentity}; repository-input=${evidence.identities.repositoryInputIdentity}`);
|
|
383
|
-
lines.push(`bindings: required=${evidence.counts.bindings.required}; configured=${evidence.counts.bindings.configured}; evaluated=${evidence.counts.bindings.evaluated}`);
|
|
384
|
-
lines.push(`returned: observations=${evidence.counts.returned.observations}; findings=${evidence.counts.returned.findings}; gaps=${evidence.counts.returned.gaps}`);
|
|
385
|
-
lines.push(`evidence bytes: ${report.evidence_bytes}`);
|
|
386
|
-
for (const binding of evidence.bindings) {
|
|
387
|
-
lines.push(`binding ${binding.id}: required=${binding.required}; reachability=${binding.packageProbe.reachability}; execution=${binding.packageProbe.execution}; conformance=${binding.packageProbe.conformance}`);
|
|
388
|
-
if (binding.externalProbe) lines.push(` external ${binding.externalProbe.id}@${binding.externalProbe.version}: ${binding.externalProbe.status}${binding.externalProbe.reportedOutcome ? `/${binding.externalProbe.reportedOutcome}` : ""}; package-attestation=false`);
|
|
389
|
-
}
|
|
390
|
-
for (const finding of evidence.findings) lines.push(`finding ${finding.code} ${finding.fingerprint} at ${humanAuditLocation(finding.primaryLocation)}: ${finding.message}`);
|
|
391
|
-
for (const gap of evidence.gaps) lines.push(`gap ${gap.code} ${gap.fingerprint} at ${humanAuditLocation(gap.primaryLocation)}: ${gap.message}`);
|
|
392
|
-
} else lines.push("evidence unavailable; partial=false");
|
|
393
|
-
for (const diagnostic of report.diagnostics) lines.push(humanDiagnostic(diagnostic));
|
|
394
|
-
return `${lines.join("\n")}\n`;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function outputEvidenceReport(report, json) {
|
|
398
|
-
if (json) process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
399
|
-
else process.stdout.write(formatEvidenceHuman(report));
|
|
400
|
-
}
|
|
401
|
-
|
|
402
366
|
function formatAnchorsReport(report, json) {
|
|
403
367
|
if (json) return `${JSON.stringify(report, null, 2)}\n`;
|
|
404
368
|
const lines = [
|
|
@@ -544,7 +508,7 @@ export async function runAuthority(args) {
|
|
|
544
508
|
process.stdout.write(`${USAGE}\n`);
|
|
545
509
|
return 0;
|
|
546
510
|
}
|
|
547
|
-
if (!["fmt", "check", "compile", "anchors", "consumers", "terms", "closed-sets", "discover", "query", "context", "refs", "path", "subgraph", "audit", "diff", "impact", "review"
|
|
511
|
+
if (!["fmt", "check", "compile", "anchors", "consumers", "terms", "closed-sets", "discover", "change-candidates", "query", "context", "refs", "path", "subgraph", "audit", "diff", "impact", "review"].includes(subcommand)) {
|
|
548
512
|
process.stderr.write(`nimicoding authority refused unknown subcommand: ${subcommand}\n${USAGE}\n`);
|
|
549
513
|
return 2;
|
|
550
514
|
}
|
|
@@ -555,15 +519,14 @@ export async function runAuthority(args) {
|
|
|
555
519
|
}
|
|
556
520
|
try {
|
|
557
521
|
if (subcommand === "fmt") {
|
|
558
|
-
const result = await formatAuthorityFile(parsed.options.path, { check: parsed.options.check
|
|
559
|
-
const report = makeReport("fmt", result, "not_evaluated", { changed: result.changed, check: parsed.options.check
|
|
522
|
+
const result = await formatAuthorityFile(parsed.options.path, { check: parsed.options.check });
|
|
523
|
+
const report = makeReport("fmt", result, "not_evaluated", { changed: result.changed, check: parsed.options.check });
|
|
560
524
|
outputReport(report, parsed.options.json);
|
|
561
525
|
return result.ok ? 0 : 1;
|
|
562
526
|
}
|
|
563
527
|
if (subcommand === "check") {
|
|
564
528
|
const result = await checkAuthorityPath(parsed.options.path, {
|
|
565
529
|
scopeBindings: parsed.options.scopeBindings,
|
|
566
|
-
requireFormat: parsed.options.requireFormat,
|
|
567
530
|
});
|
|
568
531
|
const report = makeReport("check", result, result.ok ? "valid" : "invalid");
|
|
569
532
|
outputReport(report, parsed.options.json);
|
|
@@ -592,6 +555,21 @@ export async function runAuthority(args) {
|
|
|
592
555
|
outputReport(report, parsed.options.json);
|
|
593
556
|
return result.ok ? 0 : 1;
|
|
594
557
|
}
|
|
558
|
+
if (subcommand === "change-candidates") {
|
|
559
|
+
const result = await changeCandidatesAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, {
|
|
560
|
+
channels: parsed.options.channels,
|
|
561
|
+
maxCandidates: parsed.options.maxCandidates,
|
|
562
|
+
maxReasons: parsed.options.maxReasons,
|
|
563
|
+
maxBytes: parsed.options.maxBytes,
|
|
564
|
+
});
|
|
565
|
+
const report = makeReport("change-candidates", result, result.ok ? "completed" : "refused", {
|
|
566
|
+
candidate_bytes: result.candidateBytes,
|
|
567
|
+
change_candidates: result.changeCandidates,
|
|
568
|
+
partial: result.partial,
|
|
569
|
+
});
|
|
570
|
+
outputReport(report, parsed.options.json);
|
|
571
|
+
return result.ok ? 0 : 1;
|
|
572
|
+
}
|
|
595
573
|
if (subcommand === "query") {
|
|
596
574
|
const result = await queryAuthorityPath(parsed.options.path, parsed.options.id, { maxBytes: parsed.options.maxBytes });
|
|
597
575
|
const report = makeReport("query", result, result.ok ? "valid" : "invalid", { packet_bytes: result.packetBytes, packet: result.packet });
|
|
@@ -635,17 +613,17 @@ export async function runAuthority(args) {
|
|
|
635
613
|
});
|
|
636
614
|
outputAuditReport(report, parsed.options.json ? "json" : "human");
|
|
637
615
|
}
|
|
638
|
-
return result.audit?.operationStatus === "completed" && result.audit.
|
|
616
|
+
return result.audit?.operationStatus === "completed" && result.audit.blockingStatus === "clear" ? 0 : 1;
|
|
639
617
|
}
|
|
640
618
|
if (subcommand === "diff") {
|
|
641
619
|
const result = await diffAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, { maxBytes: parsed.options.maxBytes });
|
|
642
|
-
const report = makeReport("diff", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff });
|
|
620
|
+
const report = makeReport("diff", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff, partial: result.partial });
|
|
643
621
|
outputReport(report, parsed.options.json);
|
|
644
622
|
return result.ok ? 0 : 1;
|
|
645
623
|
}
|
|
646
624
|
if (subcommand === "impact") {
|
|
647
|
-
const result = await impactAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath,
|
|
648
|
-
const report = makeReport("impact", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff, impact: result.impact });
|
|
625
|
+
const result = await impactAuthorityPaths(parsed.options.beforePath, parsed.options.afterPath, { maxBytes: parsed.options.maxBytes });
|
|
626
|
+
const report = makeReport("impact", result, result.ok ? "valid" : "invalid", { payload_bytes: result.payloadBytes, diff: result.diff, impact: result.impact, partial: result.partial });
|
|
649
627
|
outputReport(report, parsed.options.json);
|
|
650
628
|
return result.ok ? 0 : 1;
|
|
651
629
|
}
|
|
@@ -803,33 +781,10 @@ export async function runAuthority(args) {
|
|
|
803
781
|
outputClosedSetsReport(report, parsed.options.json);
|
|
804
782
|
return result.ok ? 0 : 1;
|
|
805
783
|
}
|
|
806
|
-
if (subcommand === "evidence") {
|
|
807
|
-
const result = await evidenceAuthorityRepository(
|
|
808
|
-
parsed.options.repositoryPath,
|
|
809
|
-
parsed.options.bindings,
|
|
810
|
-
parsed.options.probeResults,
|
|
811
|
-
{
|
|
812
|
-
maxUnits: parsed.options.maxUnits,
|
|
813
|
-
maxBindings: parsed.options.maxBindings,
|
|
814
|
-
maxLocators: parsed.options.maxLocators,
|
|
815
|
-
maxEdges: parsed.options.maxEdges,
|
|
816
|
-
maxInputBytes: parsed.options.maxInputBytes,
|
|
817
|
-
maxBytes: parsed.options.maxBytes,
|
|
818
|
-
},
|
|
819
|
-
);
|
|
820
|
-
const report = makeReport("evidence", result, result.evidence?.operationStatus ?? "refused", {
|
|
821
|
-
evidence_bytes: result.evidenceBytes,
|
|
822
|
-
evidence: result.evidence,
|
|
823
|
-
partial: result.partial,
|
|
824
|
-
});
|
|
825
|
-
outputEvidenceReport(report, parsed.options.json);
|
|
826
|
-
return result.evidence?.complete && result.evidence.evidenceStatus === "available" ? 0 : 1;
|
|
827
|
-
}
|
|
828
784
|
const result = await reviewAuthorityRepository(
|
|
829
785
|
parsed.options.repositoryPath,
|
|
830
786
|
parsed.options.base,
|
|
831
787
|
parsed.options.bindings,
|
|
832
|
-
parsed.options.dispositions,
|
|
833
788
|
{ maxUnits: parsed.options.maxUnits, maxEdges: parsed.options.maxEdges, maxBytes: parsed.options.maxBytes },
|
|
834
789
|
);
|
|
835
790
|
const report = makeReport("review", result, result.review?.operationStatus ?? "refused", {
|
|
@@ -838,7 +793,7 @@ export async function runAuthority(args) {
|
|
|
838
793
|
partial: result.partial,
|
|
839
794
|
});
|
|
840
795
|
outputReviewReport(report, parsed.options.json);
|
|
841
|
-
return result.review?.
|
|
796
|
+
return result.review?.analysisComplete === true && result.review.audit.blockingStatus === "clear" ? 0 : 1;
|
|
842
797
|
} catch (error) {
|
|
843
798
|
const message = error instanceof AuthorityInputError ? error.message : `authority ${subcommand} could not evaluate input: ${error.message}`;
|
|
844
799
|
process.stderr.write(`${message}\n`);
|
package/cli/commands/clear.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
|
|
4
|
+
import { PROJECT_LOCAL_CLI_INVOCATION } from "../constants.mjs";
|
|
4
5
|
import {
|
|
5
6
|
previewBootstrapRemoval,
|
|
6
7
|
previewEntrypointRemoval,
|
|
@@ -177,8 +178,8 @@ ${styleLabel(localize("What You Should Review Next:", "你下一步应该检查
|
|
|
177
178
|
"如果你想完整移除项目本地 AI truth,请手动检查 .nimi/spec/** 和本地产物。",
|
|
178
179
|
)}
|
|
179
180
|
- ${localize(
|
|
180
|
-
|
|
181
|
-
|
|
181
|
+
`If you want nimicoding again later, run \`${PROJECT_LOCAL_CLI_INVOCATION} start\`.`,
|
|
182
|
+
`如果以后还要重新接入 nimicoding,请运行 \`${PROJECT_LOCAL_CLI_INVOCATION} start\`。`,
|
|
182
183
|
)}
|
|
183
184
|
`);
|
|
184
185
|
|