@narrativetrace/cli 0.1.3 → 0.2.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/README.md +77 -6
- package/dist/cli-bin.cjs +234 -513
- package/dist/cli-bin.cjs.map +1 -1
- package/dist/cli-bin.d.cts +1 -0
- package/dist/cli-bin.d.ts +1 -0
- package/dist/cli-bin.js +249 -13
- package/dist/cli-bin.js.map +1 -1
- package/dist/index.cjs +9 -520
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -91
- package/dist/index.d.ts +1 -91
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/skills/agents/add-narrative-tracing/SKILL.md +124 -0
- package/skills/agents/narrativetrace-doctor/SKILL.md +49 -0
- package/skills/catalogue.json +17 -0
- package/skills/claude/add-narrative-tracing/SKILL.md +126 -0
- package/skills/claude/narrativetrace-doctor/SKILL.md +51 -0
- package/dist/chunk-WUK4GWFW.js +0 -523
- package/dist/chunk-WUK4GWFW.js.map +0 -1
package/dist/cli-bin.cjs
CHANGED
|
@@ -1,373 +1,135 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
installationPrerequisites: `${BASE}installation-guide.md#prerequisites`,
|
|
8
|
-
installationDependencies: `${BASE}installation-guide.md#1-add-dependencies`,
|
|
9
|
-
vitestConfiguration: `${BASE}configuration-guide.md#2-vitest-configuration`,
|
|
10
|
-
whereSettingsComeFrom: `${BASE}configuration-guide.md#2b-where-settings-come-from`,
|
|
11
|
-
proxyOptions: `${BASE}configuration-guide.md#6-proxy-options`,
|
|
12
|
-
eventPipelineBuffering: `${BASE}configuration-guide.md#8-event-pipeline-buffering-bufferedeventconsumer`,
|
|
13
|
-
noTraceFilesWritten: `${BASE}troubleshooting.md#no-trace-files-are-written`,
|
|
14
|
-
manualParameterNames: `${BASE}troubleshooting.md#parameters-show-as-arg0-arg1`,
|
|
15
|
-
redactionSurfaceBySurface: `${BASE}privacy-and-redaction.md#redaction-surface-by-surface`,
|
|
16
|
-
approvalTracesEndToEnd: `${BASE}structural-trace-format.md#approval-traces-end-to-end`,
|
|
17
|
-
sixtySecondsNewProject: `${BASE}sixty-seconds.md#1-new-project-add-the-packages`
|
|
18
|
-
};
|
|
4
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_@swc+core@1.15.18_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.9.0/node_modules/tsup/assets/cjs_shims.js
|
|
5
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
6
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
19
7
|
|
|
20
|
-
// src/
|
|
21
|
-
|
|
22
|
-
return { id, status: "pass", message, fix: "", docUrl };
|
|
23
|
-
}
|
|
24
|
-
function fail(id, message, fix, docUrl) {
|
|
25
|
-
return { id, status: "fail", message, fix, docUrl };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// src/doctor/checks/approval-traces.ts
|
|
29
|
-
var ID = "trap.approval-traces";
|
|
30
|
-
var FIX = "Review each .received.nt diff against its .approved.nt baseline, then run pnpm run approve-narratives (or narrativetrace-approve) to promote it, or delete it if the change was wrong \u2014 never commit a .received.nt file.";
|
|
31
|
-
var checkApprovalTraces = (snapshot) => {
|
|
32
|
-
const paths = [...snapshot.approvedDirFiles.keys()];
|
|
33
|
-
if (paths.length === 0) {
|
|
34
|
-
return pass(
|
|
35
|
-
ID,
|
|
36
|
-
"no approval traces configured yet \u2014 nothing to check",
|
|
37
|
-
DOC.approvalTracesEndToEnd
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
const received = paths.filter((p) => p.endsWith(".received.nt"));
|
|
41
|
-
if (received.length > 0) {
|
|
42
|
-
const message2 = `${received.length} stale received trace(s) found: ${received.join(", ")}`;
|
|
43
|
-
return fail(ID, message2, FIX, DOC.approvalTracesEndToEnd);
|
|
44
|
-
}
|
|
45
|
-
const approved = paths.filter((p) => p.endsWith(".approved.nt"));
|
|
46
|
-
const message = `${approved.length} approved trace(s) found, no pending received diffs`;
|
|
47
|
-
return pass(ID, message, DOC.approvalTracesEndToEnd);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// src/doctor/checks/llms-before-you-start.ts
|
|
51
|
-
var ID2 = "trap.llms-before-you-start";
|
|
52
|
-
var PLAIN_JS = /\.js$/;
|
|
53
|
-
var ESM_IMPORT = /^\s*import\s/m;
|
|
54
|
-
var FIX2 = "Run `npm pkg set type=module` before `npm add` \u2014 without it, Node throws SyntaxError: Cannot use import statement outside a module (pnpm/yarn default to it and never hit this).";
|
|
55
|
-
var checkLlmsBeforeYouStart = (snapshot) => {
|
|
56
|
-
if (snapshot.rootPackageJson?.type === "module") {
|
|
57
|
-
return pass(ID2, 'package.json declares "type": "module"', DOC.sixtySecondsNewProject);
|
|
58
|
-
}
|
|
59
|
-
const offender = [...snapshot.sourceFiles].find(
|
|
60
|
-
([path2, content]) => PLAIN_JS.test(path2) && ESM_IMPORT.test(content)
|
|
61
|
-
);
|
|
62
|
-
if (!offender) {
|
|
63
|
-
const message2 = 'no plain .js file uses ESM import syntax without "type": "module"';
|
|
64
|
-
return pass(ID2, message2, DOC.sixtySecondsNewProject);
|
|
65
|
-
}
|
|
66
|
-
const [path] = offender;
|
|
67
|
-
const message = `${path} uses ESM import syntax but package.json has no "type": "module"`;
|
|
68
|
-
return fail(ID2, message, FIX2, DOC.sixtySecondsNewProject);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// src/semver-lite.ts
|
|
72
|
-
function parseVersion(raw) {
|
|
73
|
-
const cleaned = raw.trim().replace(/^v/, "");
|
|
74
|
-
const match = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(cleaned);
|
|
75
|
-
if (!match) return void 0;
|
|
76
|
-
return {
|
|
77
|
-
major: Number(match[1]),
|
|
78
|
-
minor: match[2] === void 0 ? 0 : Number(match[2]),
|
|
79
|
-
patch: match[3] === void 0 ? 0 : Number(match[3])
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
function compareVersions(left, right) {
|
|
83
|
-
if (left.major !== right.major) return left.major - right.major;
|
|
84
|
-
if (left.minor !== right.minor) return left.minor - right.minor;
|
|
85
|
-
return left.patch - right.patch;
|
|
86
|
-
}
|
|
87
|
-
function satisfiesGte(version, floor) {
|
|
88
|
-
return compareVersions(version, floor) >= 0;
|
|
89
|
-
}
|
|
90
|
-
function satisfiesCaret(version, base) {
|
|
91
|
-
if (compareVersions(version, base) < 0) return false;
|
|
92
|
-
if (base.major > 0) return version.major === base.major;
|
|
93
|
-
if (base.minor > 0) return version.major === 0 && version.minor === base.minor;
|
|
94
|
-
return version.major === 0 && version.minor === 0 && version.patch === base.patch;
|
|
95
|
-
}
|
|
96
|
-
function satisfiesClause(version, trimmed) {
|
|
97
|
-
if (trimmed.startsWith("^")) {
|
|
98
|
-
const base = parseVersion(trimmed.slice(1));
|
|
99
|
-
return base !== void 0 && satisfiesCaret(version, base);
|
|
100
|
-
}
|
|
101
|
-
if (trimmed.startsWith(">=")) {
|
|
102
|
-
const floor = parseVersion(trimmed.slice(2));
|
|
103
|
-
return floor !== void 0 && satisfiesGte(version, floor);
|
|
104
|
-
}
|
|
105
|
-
if (/\.x\b/.test(trimmed)) {
|
|
106
|
-
const base = parseVersion(trimmed.replace(/\.x/g, ".0"));
|
|
107
|
-
return base !== void 0 && satisfiesCaret(version, base);
|
|
108
|
-
}
|
|
109
|
-
const exact = parseVersion(trimmed);
|
|
110
|
-
return exact !== void 0 && compareVersions(version, exact) === 0;
|
|
111
|
-
}
|
|
112
|
-
function satisfiesRange(versionRaw, range) {
|
|
113
|
-
const version = parseVersion(versionRaw);
|
|
114
|
-
if (!version) return false;
|
|
115
|
-
return range.split("||").map((clause) => clause.trim()).filter(Boolean).some((clause) => satisfiesClause(version, clause));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// src/doctor/checks/node-engine.ts
|
|
119
|
-
var ID3 = "toolchain.node-engine";
|
|
120
|
-
var DEFAULT_ENGINE_RANGE = ">=20";
|
|
121
|
-
function requiredRange(snapshot) {
|
|
122
|
-
const core = snapshot.installedPackages.get("@narrativetrace/core");
|
|
123
|
-
const coreNode = snapshot.installedPackages.get("@narrativetrace/core-node");
|
|
124
|
-
return core?.engines?.node ?? coreNode?.engines?.node ?? DEFAULT_ENGINE_RANGE;
|
|
125
|
-
}
|
|
126
|
-
var checkNodeEngine = (snapshot) => {
|
|
127
|
-
const required = requiredRange(snapshot);
|
|
128
|
-
if (satisfiesRange(snapshot.nodeVersion, required)) {
|
|
129
|
-
return pass(
|
|
130
|
-
ID3,
|
|
131
|
-
`Node ${snapshot.nodeVersion} satisfies the required ${required}`,
|
|
132
|
-
DOC.installationPrerequisites
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
return fail(
|
|
136
|
-
ID3,
|
|
137
|
-
`Node ${snapshot.nodeVersion} does not satisfy the required ${required}`,
|
|
138
|
-
`Upgrade Node to a version satisfying ${required} (nvm, volta, asdf, or your CI image).`,
|
|
139
|
-
DOC.installationPrerequisites
|
|
140
|
-
);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
// src/doctor/checks/output-env.ts
|
|
144
|
-
var ID4 = "config.output-env";
|
|
145
|
-
var VALID_VALUES = /* @__PURE__ */ new Set(["true", "false"]);
|
|
146
|
-
var checkOutputEnv = (snapshot) => {
|
|
147
|
-
const raw = snapshot.env.NARRATIVETRACE_OUTPUT;
|
|
148
|
-
if (raw === void 0) {
|
|
149
|
-
return pass(
|
|
150
|
-
ID4,
|
|
151
|
-
"NARRATIVETRACE_OUTPUT is not set \u2014 output stays on its default (on)",
|
|
152
|
-
DOC.whereSettingsComeFrom
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
if (VALID_VALUES.has(raw.toLowerCase())) {
|
|
156
|
-
return pass(ID4, `NARRATIVETRACE_OUTPUT=${raw}`, DOC.whereSettingsComeFrom);
|
|
157
|
-
}
|
|
158
|
-
return fail(
|
|
159
|
-
ID4,
|
|
160
|
-
`NARRATIVETRACE_OUTPUT is set to '${raw}', which is neither "true" nor "false"`,
|
|
161
|
-
"Set NARRATIVETRACE_OUTPUT=true or NARRATIVETRACE_OUTPUT=false (or unset it) \u2014 any other value is read as a truthy string, most likely turning output on when you meant to turn it off.",
|
|
162
|
-
DOC.whereSettingsComeFrom
|
|
163
|
-
);
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// src/doctor/checks/parameter-arg0.ts
|
|
167
|
-
var ID5 = "trap.parameter-arg0";
|
|
168
|
-
var ARG_PLACEHOLDER = /\barg0\b/;
|
|
169
|
-
var FIX3 = 'Pass parameter names explicitly: traceObject(target, context, { methodName: ["paramA", "paramB"] }) \u2014 required for classes you do not own, or when a build tool strips them.';
|
|
170
|
-
var checkParameterArg0 = (snapshot) => {
|
|
171
|
-
if (snapshot.outputFiles.size === 0) {
|
|
172
|
-
const message2 = "no rendered output found yet \u2014 run your tests or app once to check this";
|
|
173
|
-
return pass(ID5, message2, DOC.manualParameterNames);
|
|
174
|
-
}
|
|
175
|
-
const offender = [...snapshot.outputFiles].find(([, content]) => ARG_PLACEHOLDER.test(content));
|
|
176
|
-
if (!offender) {
|
|
177
|
-
const message2 = "rendered output carries real parameter names \u2014 no arg0 placeholders found";
|
|
178
|
-
return pass(ID5, message2, DOC.manualParameterNames);
|
|
179
|
-
}
|
|
180
|
-
const [path] = offender;
|
|
181
|
-
const message = `rendered output shows arg0-style placeholders (first seen in ${path}) \u2014 parameter names were not captured`;
|
|
182
|
-
return fail(ID5, message, FIX3, DOC.manualParameterNames);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
// src/doctor/checks/redaction-proof.ts
|
|
186
|
-
var ID6 = "trap.redaction-proof";
|
|
187
|
-
var TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$/;
|
|
188
|
-
var REDACTED_ASSERTION = /\[REDACTED\]/;
|
|
189
|
-
var checkRedactionProof = (snapshot) => {
|
|
190
|
-
const testFiles = [...snapshot.sourceFiles].filter(([path]) => TEST_FILE.test(path));
|
|
191
|
-
const proven = testFiles.some(([, content]) => REDACTED_ASSERTION.test(content));
|
|
192
|
-
if (proven) {
|
|
193
|
-
return pass(
|
|
194
|
-
ID6,
|
|
195
|
-
"a test asserts [REDACTED] for a deny-listed parameter name",
|
|
196
|
-
DOC.redactionSurfaceBySurface
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
return fail(
|
|
200
|
-
ID6,
|
|
201
|
-
"no test asserts [REDACTED] \u2014 redaction is unproven",
|
|
202
|
-
'Render a call with a deny-listed parameter name (e.g. "password", "token") in a test and assert the output contains "[REDACTED]" \u2014 and that a neighboring, non-sensitive value is still present, so an over-broad redaction also fails.',
|
|
203
|
-
DOC.redactionSurfaceBySurface
|
|
204
|
-
);
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
// src/doctor/checks/reporter-subpath.ts
|
|
208
|
-
var ID7 = "config.reporter-subpath";
|
|
209
|
-
var VITEST_CONFIG_NAME = /(^|\/)vitest\.config\.[cm]?[jt]s$/;
|
|
210
|
-
var REPORTER_NAMES = /\b(ClaritySuiteReporter|GlossarySuiteReporter|StructuralSuiteReporter)\b/;
|
|
211
|
-
var ROOT_IMPORT = /from\s+["']@narrativetrace\/vitest["']/;
|
|
212
|
-
var SUBPATH_IMPORT = /from\s+["']@narrativetrace\/vitest\/reporters["']/;
|
|
213
|
-
function importsFromRoot(content) {
|
|
214
|
-
return REPORTER_NAMES.test(content) && ROOT_IMPORT.test(content) && !SUBPATH_IMPORT.test(content);
|
|
215
|
-
}
|
|
216
|
-
var checkReporterSubpath = (snapshot) => {
|
|
217
|
-
const configs = [...snapshot.sourceFiles].filter(([path]) => VITEST_CONFIG_NAME.test(path));
|
|
218
|
-
const offender = configs.find(([, content]) => importsFromRoot(content));
|
|
219
|
-
if (offender) {
|
|
220
|
-
const [path] = offender;
|
|
221
|
-
return fail(
|
|
222
|
-
ID7,
|
|
223
|
-
`${path} imports a NarrativeTrace vitest reporter from the package root, not the /reporters subpath`,
|
|
224
|
-
'Import reporters from "@narrativetrace/vitest/reporters" \u2014 importing from the package root also loads vitest itself and crashes config loading.',
|
|
225
|
-
DOC.vitestConfiguration
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
const message = configs.length === 0 ? "no vitest.config found \u2014 nothing to check" : "every registered reporter is imported from the /reporters subpath";
|
|
229
|
-
return pass(ID7, message, DOC.vitestConfiguration);
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
// src/doctor/checks/sibling-packages.ts
|
|
233
|
-
var ID8 = "toolchain.sibling-packages";
|
|
234
|
-
var checkSiblingPackages = (snapshot) => {
|
|
235
|
-
const ntVitest = snapshot.installedPackages.get("@narrativetrace/vitest");
|
|
236
|
-
if (!ntVitest) {
|
|
237
|
-
const message2 = "@narrativetrace/vitest is not installed \u2014 nothing to check";
|
|
238
|
-
return pass(ID8, message2, DOC.installationDependencies);
|
|
239
|
-
}
|
|
240
|
-
const siblings = Object.keys(ntVitest.dependencies ?? {}).filter(
|
|
241
|
-
(n) => n.startsWith("@narrativetrace/")
|
|
242
|
-
);
|
|
243
|
-
const unresolved = siblings.filter((name) => !snapshot.installedPackages.has(name));
|
|
244
|
-
if (unresolved.length === 0) {
|
|
245
|
-
const message2 = `all ${siblings.length} sibling package(s) of @narrativetrace/vitest resolve from the consumer`;
|
|
246
|
-
return pass(ID8, message2, DOC.installationDependencies);
|
|
247
|
-
}
|
|
248
|
-
const message = `${unresolved.length} sibling package(s) of @narrativetrace/vitest do not resolve from the consumer: ${unresolved.join(", ")}`;
|
|
249
|
-
const fix = `Add ${unresolved.join(", ")} as explicit direct dependencies \u2014 pnpm's strict layout does not hoist a dependency's own transitive dependencies to your project root.`;
|
|
250
|
-
return fail(ID8, message, fix, DOC.installationDependencies);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
// src/doctor/checks/silent-sink.ts
|
|
254
|
-
var ID9 = "trap.silent-sink";
|
|
255
|
-
var TRACE_OBJECT_CALL = /\btraceObject\s*\(/;
|
|
256
|
-
var SINK_SIGNAL = /\b(BufferedEventConsumer|captureTrace|registerConsumer|createNarrativeTest|narrativeTest)\b|@narrativetrace\/(vitest|pino|winston|opentelemetry|observability)\b/;
|
|
257
|
-
var checkSilentSink = (snapshot) => {
|
|
258
|
-
const contents = [...snapshot.sourceFiles.values()];
|
|
259
|
-
if (!contents.some((content) => TRACE_OBJECT_CALL.test(content))) {
|
|
260
|
-
return pass(ID9, "traceObject() is not used \u2014 nothing to check", DOC.noTraceFilesWritten);
|
|
261
|
-
}
|
|
262
|
-
if (contents.some((content) => SINK_SIGNAL.test(content))) {
|
|
263
|
-
return pass(
|
|
264
|
-
ID9,
|
|
265
|
-
"traceObject() is used and a consumer/sink is attached",
|
|
266
|
-
DOC.noTraceFilesWritten
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
return fail(
|
|
270
|
-
ID9,
|
|
271
|
-
"traceObject() is used but no consumer or sink (BufferedEventConsumer, captureTrace(), a log/OTel bridge, or @narrativetrace/vitest) was found",
|
|
272
|
-
"Attach a sink: pass a BufferedEventConsumer to your pipeline, call captureTrace() and do something with the tree, or wire a log/OTel bridge \u2014 otherwise every traced call narrates to nowhere.",
|
|
273
|
-
DOC.noTraceFilesWritten
|
|
274
|
-
);
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
// src/doctor/checks/trace-object-keys.ts
|
|
278
|
-
var ID10 = "config.trace-object-keys";
|
|
279
|
-
var OLD_SHAPE = /\bmethods\s*:\s*\{[^{}]*:\s*\[/;
|
|
280
|
-
var checkTraceObjectKeys = (snapshot) => {
|
|
281
|
-
const offender = [...snapshot.sourceFiles].find(([, content]) => OLD_SHAPE.test(content));
|
|
282
|
-
if (offender) {
|
|
283
|
-
const [path] = offender;
|
|
284
|
-
return fail(
|
|
285
|
-
ID10,
|
|
286
|
-
`${path} calls traceObject(...) with a method entry that skips the per-method config object`,
|
|
287
|
-
"Nest the parameter names under params: traceObject(target, context, { methods: { methodName: { params: [...] } } }) \u2014 a bare array under the method name throws.",
|
|
288
|
-
DOC.proxyOptions
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
return pass(
|
|
292
|
-
ID10,
|
|
293
|
-
"no traceObject(...) call uses the old { methods: { ... } } option shape",
|
|
294
|
-
DOC.proxyOptions
|
|
295
|
-
);
|
|
296
|
-
};
|
|
8
|
+
// src/cli-bin.ts
|
|
9
|
+
var import_tooling4 = require("@narrativetrace/tooling");
|
|
297
10
|
|
|
298
|
-
// src/
|
|
299
|
-
var
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return
|
|
304
|
-
}
|
|
305
|
-
function mismatchedVitest(range, installed) {
|
|
306
|
-
const message = `vitest@${installed} does not satisfy @narrativetrace/vitest's declared peer range ${range}`;
|
|
307
|
-
const fix = `Install a vitest version satisfying ${range}, then reinstall clean (rm -rf node_modules && npm ci) \u2014 a mismatched peer here is the one failure mode that breaks the library's own build.`;
|
|
308
|
-
return fail(ID11, message, fix, DOC.vitestConfiguration);
|
|
11
|
+
// src/carrier-locator.ts
|
|
12
|
+
var import_node_path = require("path");
|
|
13
|
+
var import_node_url = require("url");
|
|
14
|
+
var import_tooling = require("@narrativetrace/tooling");
|
|
15
|
+
function cliPackageDirectory() {
|
|
16
|
+
return (0, import_node_path.dirname)((0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl)));
|
|
309
17
|
}
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
if (satisfiesRange(vitest.version, range)) {
|
|
313
|
-
const message = `vitest@${vitest.version} satisfies the declared peer range ${range}`;
|
|
314
|
-
return pass(ID11, message, DOC.vitestConfiguration);
|
|
315
|
-
}
|
|
316
|
-
return mismatchedVitest(range, vitest.version);
|
|
18
|
+
function openCarrierFor(projectDirectory, from) {
|
|
19
|
+
return (0, import_tooling.resolveCarrier)({ projectDirectory, bundledDirectory: cliPackageDirectory(), from });
|
|
317
20
|
}
|
|
318
|
-
var checkVitestPeer = (snapshot) => {
|
|
319
|
-
const ntVitest = snapshot.installedPackages.get("@narrativetrace/vitest");
|
|
320
|
-
const range = ntVitest?.peerDependencies?.vitest;
|
|
321
|
-
if (!ntVitest || !range) {
|
|
322
|
-
return pass(
|
|
323
|
-
ID11,
|
|
324
|
-
"@narrativetrace/vitest is not installed \u2014 nothing to check",
|
|
325
|
-
DOC.vitestConfiguration
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
return evaluateInstalledVitest(range, snapshot.installedPackages.get("vitest"));
|
|
329
|
-
};
|
|
330
21
|
|
|
331
|
-
// src/
|
|
332
|
-
var
|
|
333
|
-
checkNodeEngine,
|
|
334
|
-
checkVitestPeer,
|
|
335
|
-
checkSiblingPackages,
|
|
336
|
-
checkOutputEnv,
|
|
337
|
-
checkReporterSubpath,
|
|
338
|
-
checkTraceObjectKeys,
|
|
339
|
-
checkSilentSink,
|
|
340
|
-
checkParameterArg0,
|
|
341
|
-
checkRedactionProof,
|
|
342
|
-
checkApprovalTraces,
|
|
343
|
-
checkLlmsBeforeYouStart
|
|
344
|
-
];
|
|
345
|
-
function runDoctor(snapshot) {
|
|
346
|
-
const findings = DOCTOR_CHECKS.map((check) => check(snapshot));
|
|
347
|
-
const exitCode2 = findings.some((f) => f.status === "fail") ? 1 : 0;
|
|
348
|
-
return { findings, exitCode: exitCode2 };
|
|
349
|
-
}
|
|
22
|
+
// src/cli.ts
|
|
23
|
+
var import_tooling3 = require("@narrativetrace/tooling");
|
|
350
24
|
|
|
351
|
-
// src/
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
25
|
+
// src/installer-arguments.ts
|
|
26
|
+
var import_tooling2 = require("@narrativetrace/tooling");
|
|
27
|
+
var VALUE_FLAGS = /* @__PURE__ */ new Set(["--only", "--vendor", "--from"]);
|
|
28
|
+
var SWITCHES = /* @__PURE__ */ new Map([
|
|
29
|
+
[
|
|
30
|
+
"--dry-run",
|
|
31
|
+
(reading) => {
|
|
32
|
+
reading.draft.dryRun = true;
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
"--write-existing",
|
|
37
|
+
(reading) => {
|
|
38
|
+
reading.draft.writeExisting = true;
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
"--force",
|
|
43
|
+
(reading) => {
|
|
44
|
+
reading.draft.force = true;
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"--json",
|
|
49
|
+
(reading) => {
|
|
50
|
+
reading.json = true;
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"--help",
|
|
55
|
+
(reading) => {
|
|
56
|
+
reading.help = true;
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
"-h",
|
|
61
|
+
(reading) => {
|
|
62
|
+
reading.help = true;
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
]);
|
|
66
|
+
function fail(reading, message) {
|
|
67
|
+
reading.error ??= message;
|
|
68
|
+
}
|
|
69
|
+
function readScope(value, reading) {
|
|
70
|
+
if (value === "skills" || value === "agents-md") reading.draft.scope = value;
|
|
71
|
+
else fail(reading, `--only takes skills or agents-md, got "${value}"`);
|
|
72
|
+
}
|
|
73
|
+
function readVendor(value, reading) {
|
|
74
|
+
if (value === "claude") reading.draft.vendorClaude = "on";
|
|
75
|
+
else if (value === "none") reading.draft.vendorClaude = "off";
|
|
76
|
+
else fail(reading, `--vendor takes claude or none, got "${value}"`);
|
|
77
|
+
}
|
|
78
|
+
function nextFlagMessage(name, value) {
|
|
79
|
+
return `${name} needs a value, and "${value}" reads as the next flag \u2014 write ${name}=${value} if it really is the value`;
|
|
80
|
+
}
|
|
81
|
+
function readValued(name, value, spelledOut, reading) {
|
|
82
|
+
if (value === void 0 || value === "") {
|
|
83
|
+
fail(reading, `${name} needs a value`);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (!spelledOut && value.startsWith("--")) {
|
|
87
|
+
fail(reading, nextFlagMessage(name, value));
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (name === "--only") readScope(value, reading);
|
|
91
|
+
else if (name === "--vendor") readVendor(value, reading);
|
|
92
|
+
else reading.from = value;
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
function splitArgument(argument) {
|
|
96
|
+
const equals = argument.indexOf("=");
|
|
97
|
+
return equals < 0 ? { name: argument, spelled: void 0 } : { name: argument.slice(0, equals), spelled: argument.slice(equals + 1) };
|
|
98
|
+
}
|
|
99
|
+
function readOne(args, index, reading) {
|
|
100
|
+
const argument = args[index];
|
|
101
|
+
const { name, spelled } = splitArgument(argument);
|
|
102
|
+
const turnOn = SWITCHES.get(name);
|
|
103
|
+
if (turnOn !== void 0) {
|
|
104
|
+
if (spelled === void 0) turnOn(reading);
|
|
105
|
+
else fail(reading, `${name} takes no value, got "${argument}"`);
|
|
106
|
+
return index;
|
|
107
|
+
}
|
|
108
|
+
if (!VALUE_FLAGS.has(name)) {
|
|
109
|
+
fail(reading, `unknown option: "${argument}"`);
|
|
110
|
+
return index;
|
|
111
|
+
}
|
|
112
|
+
const valueIndex = spelled === void 0 ? index + 1 : index;
|
|
113
|
+
const value = spelled ?? args[valueIndex];
|
|
114
|
+
return readValued(name, value, spelled !== void 0, reading) ? valueIndex : index;
|
|
115
|
+
}
|
|
116
|
+
function parseInstallerArguments(args) {
|
|
117
|
+
if (args == null) throw new TypeError("a command line is a list of arguments, never null");
|
|
118
|
+
const reading = {
|
|
119
|
+
draft: {},
|
|
120
|
+
from: void 0,
|
|
121
|
+
json: false,
|
|
122
|
+
help: false,
|
|
123
|
+
error: void 0
|
|
124
|
+
};
|
|
125
|
+
for (let index = 0; index < args.length; index += 1) index = readOne(args, index, reading);
|
|
126
|
+
return Object.freeze({
|
|
127
|
+
options: (0, import_tooling2.initOptions)(reading.draft),
|
|
128
|
+
from: reading.from,
|
|
129
|
+
json: reading.json,
|
|
130
|
+
help: reading.help,
|
|
131
|
+
error: reading.error
|
|
132
|
+
});
|
|
371
133
|
}
|
|
372
134
|
|
|
373
135
|
// src/cli.ts
|
|
@@ -375,9 +137,14 @@ var USAGE = `narrativetrace \u2014 one CLI over NarrativeTrace's open artifact f
|
|
|
375
137
|
|
|
376
138
|
Usage:
|
|
377
139
|
narrativetrace doctor [--json]
|
|
140
|
+
narrativetrace init [--dry-run] [--write-existing] [--force] [--only <half>] [--vendor <vendor>]
|
|
141
|
+
[--from <dir>] [--json]
|
|
142
|
+
narrativetrace uninstall [--dry-run] [--only <half>] [--json]
|
|
378
143
|
|
|
379
144
|
Commands:
|
|
380
|
-
doctor
|
|
145
|
+
doctor Read-only project diagnosis: toolchain, configuration, and known traps. Zero network.
|
|
146
|
+
init Installs the NarrativeTrace agent skills and the AGENTS.md section into this project.
|
|
147
|
+
uninstall Removes exactly what init wrote, and nothing beside it.
|
|
381
148
|
|
|
382
149
|
Options:
|
|
383
150
|
--json Machine-readable output instead of human text.
|
|
@@ -386,6 +153,43 @@ var DOCTOR_USAGE = `narrativetrace doctor [--json]
|
|
|
386
153
|
|
|
387
154
|
Read-only. Checks toolchain/install state, configuration, and known traps against the current
|
|
388
155
|
project. Exit 0 = clean, 1 = findings, 2 = could not run.`;
|
|
156
|
+
var INSTALLER_OPTIONS = ` --dry-run Show the plan and the unified diff. Writes nothing, always exits 0.
|
|
157
|
+
--write-existing Permission to touch an AGENTS.md or CLAUDE.md that is already there.
|
|
158
|
+
--force Permission to overwrite a skill directory somebody else owns.
|
|
159
|
+
--only <half> skills | agents-md. Both halves by default.
|
|
160
|
+
--vendor <vendor> claude | none. Detected from the project by default.
|
|
161
|
+
--from <dir> A directory holding the carrier: a checked-out @narrativetrace/skills, or an
|
|
162
|
+
unpacked tarball of one. The copy bundled with this CLI by default.
|
|
163
|
+
--json Machine-readable output instead of human text.
|
|
164
|
+
|
|
165
|
+
Exit 0 = applied (or a dry run), 1 = something was refused, 2 = could not run.`;
|
|
166
|
+
var INIT_USAGE = `narrativetrace init [options]
|
|
167
|
+
|
|
168
|
+
Copies the NarrativeTrace agent skills into .agents/skills/ (and .claude/skills/ where the project
|
|
169
|
+
is one of that vendor's) and writes one marked section into AGENTS.md. Zero network.
|
|
170
|
+
|
|
171
|
+
Run it again to refresh: this CLI installs no build hook and schedules nothing, so a re-run IS the
|
|
172
|
+
refresh. On a project that already carries the skills it rewrites only our own pages and our own
|
|
173
|
+
marked section, and leaves everything else where it is.
|
|
174
|
+
|
|
175
|
+
${INSTALLER_OPTIONS}`;
|
|
176
|
+
var UNINSTALL_OPTIONS = ` --dry-run Show the plan and the unified diff. Removes nothing, always exits 0.
|
|
177
|
+
--only <half> skills | agents-md. Both halves by default.
|
|
178
|
+
--json Machine-readable output instead of human text.
|
|
179
|
+
|
|
180
|
+
The install-only flags (--write-existing, --force, --vendor, --from) are accepted and ignored: there
|
|
181
|
+
is no carrier to open and no permission to ask for when removing what this tool itself wrote.
|
|
182
|
+
|
|
183
|
+
Exit 0 = removed (or a dry run), 1 = something was refused, 2 = could not run.`;
|
|
184
|
+
var UNINSTALL_USAGE = `narrativetrace uninstall [options]
|
|
185
|
+
|
|
186
|
+
Removes exactly what init wrote: skill directories carrying its provenance line, the marked
|
|
187
|
+
section, and the one @AGENTS.md import line. Never touches anything else.
|
|
188
|
+
|
|
189
|
+
${UNINSTALL_OPTIONS}`;
|
|
190
|
+
var CARRIER_HINT = `Nothing was fetched: this command makes no network call. Point --from at a carrier you have:
|
|
191
|
+
--from node_modules/@narrativetrace/skills
|
|
192
|
+
npm pack @narrativetrace/skills && tar xzf narrativetrace-skills-*.tgz && narrativetrace init --from package`;
|
|
389
193
|
function runDoctorCommand(rest, deps) {
|
|
390
194
|
if (rest.includes("--help") || rest.includes("-h")) {
|
|
391
195
|
deps.log(DOCTOR_USAGE);
|
|
@@ -404,179 +208,96 @@ ${DOCTOR_USAGE}`);
|
|
|
404
208
|
deps.error(`Could not run: no readable package.json found at ${deps.cwd}`);
|
|
405
209
|
return 2;
|
|
406
210
|
}
|
|
407
|
-
const
|
|
408
|
-
deps.log(json ? renderJson(
|
|
409
|
-
return
|
|
211
|
+
const report2 = (0, import_tooling3.runDoctor)(snapshot);
|
|
212
|
+
deps.log(json ? (0, import_tooling3.renderJson)(report2) : (0, import_tooling3.renderHuman)(report2));
|
|
213
|
+
return report2.exitCode;
|
|
410
214
|
}
|
|
411
|
-
function
|
|
412
|
-
const
|
|
413
|
-
if (
|
|
414
|
-
deps.
|
|
415
|
-
return
|
|
416
|
-
}
|
|
417
|
-
if (verb === "--help" || verb === "-h") {
|
|
418
|
-
deps.log(USAGE);
|
|
419
|
-
return 0;
|
|
215
|
+
function report(plan, parsed, deps) {
|
|
216
|
+
const options = { json: parsed.json };
|
|
217
|
+
if (parsed.options.dryRun) {
|
|
218
|
+
deps.print((0, import_tooling3.renderPlan)(plan, options));
|
|
219
|
+
return (0, import_tooling3.planExitCode)(plan);
|
|
420
220
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
${USAGE}`);
|
|
425
|
-
return 2;
|
|
426
|
-
}
|
|
427
|
-
return runDoctorCommand(rest, deps);
|
|
221
|
+
const executed = (0, import_tooling3.applyPlan)(plan, deps.cwd);
|
|
222
|
+
deps.print((0, import_tooling3.renderReport)(executed, options));
|
|
223
|
+
return (0, import_tooling3.reportExitCode)(executed);
|
|
428
224
|
}
|
|
429
|
-
|
|
430
|
-
// src/doctor/environment.ts
|
|
431
|
-
var import_node_fs = require("fs");
|
|
432
|
-
var import_node_module = require("module");
|
|
433
|
-
var import_node_path = require("path");
|
|
434
|
-
var EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
435
|
-
// Stryker disable next-line StringLiteral: equivalent — every dot-prefixed entry name is
|
|
436
|
-
// already excluded earlier, in visitEntry's leading-dot check (the only exception there is
|
|
437
|
-
// ".env", which isn't a directory this set would ever mention), so ".git" never actually
|
|
438
|
-
// reaches this set's `.has()` check.
|
|
439
|
-
".git",
|
|
440
|
-
// Stryker disable next-line StringLiteral: same equivalence as ".git" above.
|
|
441
|
-
".turbo",
|
|
442
|
-
// Stryker disable next-line StringLiteral: same equivalence as ".git" above.
|
|
443
|
-
".stryker-tmp",
|
|
444
|
-
// Not dot-prefixed, so NOT equivalent — reaching this set's `.has()` check is the only thing
|
|
445
|
-
// that excludes each of the four names below; each is covered by a real test.
|
|
446
|
-
"node_modules",
|
|
447
|
-
"dist",
|
|
448
|
-
"build",
|
|
449
|
-
"coverage"
|
|
450
|
-
]);
|
|
451
|
-
var SOURCE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
|
|
452
|
-
var BASE_PACKAGES = [
|
|
453
|
-
"vitest",
|
|
454
|
-
"@narrativetrace/core",
|
|
455
|
-
"@narrativetrace/core-node",
|
|
456
|
-
"@narrativetrace/vitest"
|
|
457
|
-
];
|
|
458
|
-
var MAX_FILES = 2e4;
|
|
459
|
-
function readJson(path) {
|
|
225
|
+
function runPlan(planner, parsed, deps) {
|
|
460
226
|
try {
|
|
461
|
-
return
|
|
462
|
-
} catch {
|
|
463
|
-
|
|
227
|
+
return report(planner((0, import_tooling3.readProjectState)(deps.cwd, deps.env)), parsed, deps);
|
|
228
|
+
} catch (cause) {
|
|
229
|
+
deps.error(cause.message);
|
|
230
|
+
return 1;
|
|
464
231
|
}
|
|
465
232
|
}
|
|
466
|
-
function
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return readJson(require2.resolve(`${name}/package.json`));
|
|
470
|
-
} catch {
|
|
471
|
-
return void 0;
|
|
472
|
-
}
|
|
233
|
+
function noteVersions(carrier, state, deps) {
|
|
234
|
+
const warning = (0, import_tooling3.carrierVersionWarning)(carrier, state);
|
|
235
|
+
if (warning !== void 0) deps.error(warning);
|
|
473
236
|
}
|
|
474
|
-
function
|
|
237
|
+
function runInit(parsed, deps) {
|
|
238
|
+
let carrier;
|
|
475
239
|
try {
|
|
476
|
-
|
|
477
|
-
} catch {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
240
|
+
carrier = deps.openCarrier(parsed.from);
|
|
241
|
+
} catch (cause) {
|
|
242
|
+
deps.error(`${cause.message}
|
|
243
|
+
|
|
244
|
+
${CARRIER_HINT}`);
|
|
245
|
+
return 1;
|
|
246
|
+
}
|
|
247
|
+
return runPlan(
|
|
248
|
+
(state) => {
|
|
249
|
+
noteVersions(carrier, state, deps);
|
|
250
|
+
return (0, import_tooling3.planInstall)(state, carrier, parsed.options);
|
|
251
|
+
},
|
|
252
|
+
parsed,
|
|
253
|
+
deps
|
|
254
|
+
);
|
|
487
255
|
}
|
|
488
|
-
function
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
256
|
+
function runInstaller(install, rest, deps) {
|
|
257
|
+
const parsed = parseInstallerArguments(rest);
|
|
258
|
+
const usage = install ? INIT_USAGE : UNINSTALL_USAGE;
|
|
259
|
+
if (parsed.help) {
|
|
260
|
+
deps.log(usage);
|
|
261
|
+
return 0;
|
|
493
262
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
return SOURCE_EXTENSIONS.some((ext) => rel.endsWith(ext)) ? "source" : "skip";
|
|
500
|
-
}
|
|
501
|
-
function bucketFor(state, kind) {
|
|
502
|
-
return kind === "output" ? state.outputFiles : kind === "approved" ? state.approvedDirFiles : state.sourceFiles;
|
|
503
|
-
}
|
|
504
|
-
function visitEntry(ctx, dir, entry) {
|
|
505
|
-
if (entry.startsWith(".") && entry !== ".env") return;
|
|
506
|
-
const full = (0, import_node_path.join)(dir, entry);
|
|
507
|
-
const isDir = isDirectorySafe(full);
|
|
508
|
-
if (isDir === void 0) return;
|
|
509
|
-
if (isDir) {
|
|
510
|
-
if (!EXCLUDED_DIRS.has(entry)) ctx.queue.push(full);
|
|
511
|
-
return;
|
|
263
|
+
if (parsed.error !== void 0) {
|
|
264
|
+
deps.error(`${parsed.error}
|
|
265
|
+
|
|
266
|
+
${usage}`);
|
|
267
|
+
return 2;
|
|
512
268
|
}
|
|
513
|
-
|
|
514
|
-
const rel = (0, import_node_path.relative)(ctx.root, full);
|
|
515
|
-
const kind = classify(rel, ctx.output, ctx.approved);
|
|
516
|
-
if (kind === "skip") return;
|
|
517
|
-
bucketFor(ctx.state, kind).set(rel, safeRead(full));
|
|
269
|
+
return install ? runInit(parsed, deps) : runPlan((state) => (0, import_tooling3.planUninstall)(state, parsed.options), parsed, deps);
|
|
518
270
|
}
|
|
519
|
-
function
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
visited: 0
|
|
525
|
-
};
|
|
526
|
-
const ctx = { state, root, output, approved, queue: [root] };
|
|
527
|
-
while (ctx.queue.length > 0 && state.visited < MAX_FILES) {
|
|
528
|
-
const dir = ctx.queue.shift();
|
|
529
|
-
for (const entry of listDirectory(dir)) {
|
|
530
|
-
if (state.visited >= MAX_FILES) break;
|
|
531
|
-
visitEntry(ctx, dir, entry);
|
|
532
|
-
}
|
|
271
|
+
function runCli(argv, deps) {
|
|
272
|
+
const [verb, ...rest] = argv;
|
|
273
|
+
if (verb === void 0) {
|
|
274
|
+
deps.error(USAGE);
|
|
275
|
+
return 2;
|
|
533
276
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
const installed = /* @__PURE__ */ new Map();
|
|
538
|
-
for (const name of BASE_PACKAGES) {
|
|
539
|
-
const pkg = resolvePackageJson(name, cwd);
|
|
540
|
-
if (pkg) installed.set(name, pkg);
|
|
277
|
+
if (verb === "--help" || verb === "-h") {
|
|
278
|
+
deps.log(USAGE);
|
|
279
|
+
return 0;
|
|
541
280
|
}
|
|
542
|
-
return
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
const ntVitest = installed.get("@narrativetrace/vitest");
|
|
546
|
-
for (const name of Object.keys(ntVitest?.dependencies ?? {})) {
|
|
547
|
-
if (!name.startsWith("@narrativetrace/") || installed.has(name)) continue;
|
|
548
|
-
const pkg = resolvePackageJson(name, cwd);
|
|
549
|
-
if (pkg) installed.set(name, pkg);
|
|
281
|
+
if (verb === "doctor") return runDoctorCommand(rest, deps);
|
|
282
|
+
if (verb === "init" || verb === "uninstall") {
|
|
283
|
+
return runInstaller(verb === "init", rest, deps);
|
|
550
284
|
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
return installed;
|
|
556
|
-
}
|
|
557
|
-
function buildSnapshot(cwd, env) {
|
|
558
|
-
const output = env.NARRATIVETRACE_OUTPUT_DIR ?? "narrativetrace-output";
|
|
559
|
-
const approved = env.NARRATIVETRACE_APPROVED_DIR ?? "narratives";
|
|
560
|
-
const { sourceFiles, outputFiles, approvedDirFiles } = walk(cwd, output, approved);
|
|
561
|
-
return {
|
|
562
|
-
cwd,
|
|
563
|
-
nodeVersion: process.version.replace(/^v/, ""),
|
|
564
|
-
env,
|
|
565
|
-
rootPackageJson: readJson((0, import_node_path.join)(cwd, "package.json")),
|
|
566
|
-
sourceFiles,
|
|
567
|
-
outputFiles,
|
|
568
|
-
approvedDirFiles,
|
|
569
|
-
installedPackages: resolveInstalledPackages(cwd)
|
|
570
|
-
};
|
|
285
|
+
deps.error(`Unknown command: ${verb}
|
|
286
|
+
|
|
287
|
+
${USAGE}`);
|
|
288
|
+
return 2;
|
|
571
289
|
}
|
|
572
290
|
|
|
573
291
|
// src/cli-bin.ts
|
|
292
|
+
var cwd = process.cwd();
|
|
574
293
|
var exitCode = runCli(process.argv.slice(2), {
|
|
575
|
-
cwd
|
|
294
|
+
cwd,
|
|
576
295
|
env: process.env,
|
|
577
|
-
buildSnapshot,
|
|
296
|
+
buildSnapshot: (c, e) => (0, import_tooling4.buildSnapshot)(c, e, cliPackageDirectory()),
|
|
297
|
+
openCarrier: (from) => openCarrierFor(cwd, from),
|
|
578
298
|
log: (message) => process.stdout.write(`${message}
|
|
579
299
|
`),
|
|
300
|
+
print: (text) => process.stdout.write(text),
|
|
580
301
|
error: (message) => process.stderr.write(`${message}
|
|
581
302
|
`)
|
|
582
303
|
});
|