ushman-ledger 1.1.0 → 1.2.1
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/AGENTS.md +11 -7
- package/CHANGELOG.md +6 -0
- package/README.md +79 -8
- package/dist/blobs.js +3 -3
- package/dist/builders.d.ts +44 -2
- package/dist/builders.d.ts.map +1 -1
- package/dist/builders.js +7 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +346 -62
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +104 -4
- package/dist/handle.d.ts +28 -6
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +105 -11
- package/dist/helpers.d.ts +7 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +38 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/list.d.ts +44 -2
- package/dist/list.d.ts.map +1 -1
- package/dist/list.js +7 -5
- package/dist/note.d.ts +27 -0
- package/dist/note.d.ts.map +1 -1
- package/dist/note.js +11 -0
- package/dist/patch-resolver.d.ts +39 -0
- package/dist/patch-resolver.d.ts.map +1 -0
- package/dist/patch-resolver.js +196 -0
- package/dist/read-index.d.ts +7 -7
- package/dist/read-index.d.ts.map +1 -1
- package/dist/record.d.ts.map +1 -1
- package/dist/record.js +15 -40
- package/dist/render/migration-log.d.ts +10 -0
- package/dist/render/migration-log.d.ts.map +1 -0
- package/dist/render/migration-log.js +79 -0
- package/dist/render/retro.d.ts.map +1 -1
- package/dist/render/retro.js +34 -21
- package/dist/render/workspace-narrative.d.ts +12 -0
- package/dist/render/workspace-narrative.d.ts.map +1 -0
- package/dist/render/workspace-narrative.js +137 -0
- package/dist/schema/entry-core.d.ts +110 -0
- package/dist/schema/entry-core.d.ts.map +1 -0
- package/dist/schema/entry-core.js +143 -0
- package/dist/schema/entry-migrations.d.ts +3 -0
- package/dist/schema/entry-migrations.d.ts.map +1 -0
- package/dist/schema/entry-migrations.js +48 -0
- package/dist/schema/entry-read.d.ts +694 -0
- package/dist/schema/entry-read.d.ts.map +1 -0
- package/dist/schema/entry-read.js +92 -0
- package/dist/schema/entry-write.d.ts +865 -0
- package/dist/schema/entry-write.d.ts.map +1 -0
- package/dist/schema/entry-write.js +105 -0
- package/dist/schema/entry.d.ts +6 -1369
- package/dist/schema/entry.d.ts.map +1 -1
- package/dist/schema/entry.js +9 -286
- package/dist/schema/note.d.ts +1 -1
- package/dist/schema/note.d.ts.map +1 -1
- package/dist/schema/note.js +12 -1
- package/dist/storage/filesystem.d.ts +9 -0
- package/dist/storage/filesystem.d.ts.map +1 -1
- package/dist/storage/filesystem.js +82 -5
- package/dist/storage/lock-reclaimer.d.ts +2 -0
- package/dist/storage/lock-reclaimer.d.ts.map +1 -0
- package/dist/storage/lock-reclaimer.js +45 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -7,12 +7,35 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
8
|
import * as v from 'valibot';
|
|
9
9
|
import { openLedger } from "./handle.js";
|
|
10
|
-
import {
|
|
10
|
+
import { deriveFilesChangedFromPatch } from "./patch-resolver.js";
|
|
11
|
+
import { ChangeLogParityStatusSchema, ChangeLogSmokeResultSchema, ChangeLogSubkindSchema, LEDGER_KINDS, LEDGER_PHASES, parseLedgerRecord, WorkspaceRelativePathSchema, } from "./schema/entry.js";
|
|
11
12
|
import { NoteSubkindSchema } from "./schema/note.js";
|
|
12
13
|
import { LEDGER_LIBRARY_VERSION } from "./version.js";
|
|
13
14
|
const execFileAsync = promisify(execFile);
|
|
14
|
-
const
|
|
15
|
-
const
|
|
15
|
+
const DEFAULT_GIT_DIFF_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
16
|
+
const DEFAULT_GIT_DIFF_TIMEOUT_MS = 30_000;
|
|
17
|
+
const RENDER_TARGETS = [
|
|
18
|
+
'retro',
|
|
19
|
+
'jsonl',
|
|
20
|
+
'timeline-html',
|
|
21
|
+
'dependency-graph',
|
|
22
|
+
'migration-log-md',
|
|
23
|
+
'workspace-narrative-md',
|
|
24
|
+
];
|
|
25
|
+
const GIT_DIFF_FLAG_NAMES = ['git-diff-max-buffer-bytes', 'git-diff-timeout-ms', 'git-paths'];
|
|
26
|
+
const CHANGE_LOG_RECORD_ONLY_FLAGS = [
|
|
27
|
+
'commands',
|
|
28
|
+
'commands-from',
|
|
29
|
+
'files-changed',
|
|
30
|
+
'hypothesis',
|
|
31
|
+
'parity-status',
|
|
32
|
+
'rollback-plan',
|
|
33
|
+
'rolls-back',
|
|
34
|
+
'smoke-notes',
|
|
35
|
+
'smoke-result',
|
|
36
|
+
'subkind',
|
|
37
|
+
];
|
|
38
|
+
const renderRecordUsage = (commandName) => `${commandName} record [--workspace=<ws>] --kind=<kind> --phase=<phase> --summary="..." [--rationale="..."] [--action=<operator-action>] [--check-id=<check-id>] [--diff=<patch-file>] [--diff-from-git=<ref>] [--git-paths=<csv>] [--git-diff-timeout-ms=<ms>] [--git-diff-max-buffer-bytes=<bytes>] [--idempotency-key=<key>] [--subkind=<change-log-subkind>] [--files-changed=<csv>] [--hypothesis="..."] [--commands="cmd1\ncmd2" | --commands-from=<file>] [--smoke-result=<result>] [--smoke-notes="..."] [--parity-status=<status>] [--rollback-plan="..."] [--rolls-back=<entry-id>] [--from-stdin]`;
|
|
16
39
|
class CliUsageError extends Error {
|
|
17
40
|
}
|
|
18
41
|
const DEFAULT_CONTEXT = {
|
|
@@ -34,12 +57,12 @@ const renderValidValues = () => `Valid values:
|
|
|
34
57
|
const renderHelp = (commandName) => `${commandName}
|
|
35
58
|
|
|
36
59
|
Commands:
|
|
37
|
-
${commandName}
|
|
60
|
+
${renderRecordUsage(commandName)}
|
|
38
61
|
${commandName} note <subkind> [--workspace=<ws>] --phase=<phase> --summary="..." [--body=<markdown-file>] [--from-stdin]
|
|
39
62
|
${commandName} list [--workspace=<ws>] [--phase=<phase>] [--kind=<kind>] [--since=<iso>] [--limit=<n>] [--json]
|
|
40
63
|
${commandName} show [--workspace=<ws>] <entry-id>
|
|
41
64
|
${commandName} tail [--workspace=<ws>] [--phase=<phase>] [--limit=<n>]
|
|
42
|
-
${commandName} render [--workspace=<ws>] [--to=retro|jsonl|timeline-html|dependency-graph] [--phase=<phase>] [--out=<file>]
|
|
65
|
+
${commandName} render [--workspace=<ws>] [--to=retro|jsonl|timeline-html|dependency-graph|migration-log-md|workspace-narrative-md] [--phase=<phase>] [--since=<iso>] [--limit=<n>] [--out=<file>]
|
|
43
66
|
${commandName} archive [--workspace=<ws>] --out=<file.tgz>
|
|
44
67
|
${commandName} doctor [--workspace=<ws>]
|
|
45
68
|
${commandName} --version
|
|
@@ -48,7 +71,7 @@ ${renderValidValues()}`;
|
|
|
48
71
|
const renderCommandHelp = (commandName, command) => {
|
|
49
72
|
switch (command) {
|
|
50
73
|
case 'record':
|
|
51
|
-
return `${commandName}
|
|
74
|
+
return `${renderRecordUsage(commandName)}
|
|
52
75
|
|
|
53
76
|
${renderValidValues()}`;
|
|
54
77
|
case 'note':
|
|
@@ -60,7 +83,7 @@ ${renderValidValues()}`;
|
|
|
60
83
|
|
|
61
84
|
${renderValidValues()}`;
|
|
62
85
|
case 'render':
|
|
63
|
-
return `${commandName} render [--workspace=<ws>] [--to=<target>] [--phase=<phase>] [--out=<file>]
|
|
86
|
+
return `${commandName} render [--workspace=<ws>] [--to=<target>] [--phase=<phase>] [--since=<iso>] [--limit=<n>] [--out=<file>]
|
|
64
87
|
|
|
65
88
|
${renderValidValues()}`;
|
|
66
89
|
default:
|
|
@@ -99,6 +122,9 @@ const parseArgv = (argv) => {
|
|
|
99
122
|
};
|
|
100
123
|
const getFlag = (flags, name) => {
|
|
101
124
|
const value = flags[name];
|
|
125
|
+
if (value === true) {
|
|
126
|
+
throw new CliUsageError(`Missing value for --${name}.`);
|
|
127
|
+
}
|
|
102
128
|
return typeof value === 'string' ? value : undefined;
|
|
103
129
|
};
|
|
104
130
|
const hasFlag = (flags, name) => flags[name] === true || typeof flags[name] === 'string';
|
|
@@ -135,13 +161,64 @@ const ensureFileExists = async (filePath, flagName) => {
|
|
|
135
161
|
throw error;
|
|
136
162
|
}
|
|
137
163
|
};
|
|
138
|
-
const
|
|
164
|
+
const getErrorCode = (error) => typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;
|
|
165
|
+
const parsePositiveIntegerFlag = ({ defaultValue, flagName, flags, }) => {
|
|
166
|
+
const raw = getFlag(flags, flagName);
|
|
167
|
+
if (!raw) {
|
|
168
|
+
return defaultValue;
|
|
169
|
+
}
|
|
170
|
+
if (!/^[1-9]\d*$/u.test(raw)) {
|
|
171
|
+
throw new CliUsageError(`Invalid --${flagName} value: ${raw}. Expected a positive integer.`);
|
|
172
|
+
}
|
|
173
|
+
return Number.parseInt(raw, 10);
|
|
174
|
+
};
|
|
175
|
+
const parseWorkspaceRelativePathCsv = ({ flagName, raw }) => {
|
|
176
|
+
const uniquePaths = [
|
|
177
|
+
...new Set(raw
|
|
178
|
+
.split(',')
|
|
179
|
+
.map((value) => value.trim())
|
|
180
|
+
.filter((value) => value.length > 0)
|
|
181
|
+
.map((filePath) => {
|
|
182
|
+
try {
|
|
183
|
+
return v.parse(WorkspaceRelativePathSchema, filePath);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
throw new CliUsageError(`--${flagName} path is not a normalized workspace-relative path: ${filePath}`);
|
|
187
|
+
}
|
|
188
|
+
})),
|
|
189
|
+
];
|
|
190
|
+
if (uniquePaths.length === 0) {
|
|
191
|
+
throw new CliUsageError(`--${flagName} must include at least one workspace-relative path.`);
|
|
192
|
+
}
|
|
193
|
+
return uniquePaths;
|
|
194
|
+
};
|
|
195
|
+
const isGitDiffMaxBufferError = (error) => {
|
|
196
|
+
const code = getErrorCode(error);
|
|
197
|
+
if (code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
return error instanceof RangeError && error.message.includes('maxBuffer');
|
|
201
|
+
};
|
|
202
|
+
const isGitDiffTimeoutError = (error) => {
|
|
203
|
+
const code = getErrorCode(error);
|
|
204
|
+
if (code === 'ETIMEDOUT') {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
return (typeof error === 'object' &&
|
|
208
|
+
error !== null &&
|
|
209
|
+
'killed' in error &&
|
|
210
|
+
'signal' in error &&
|
|
211
|
+
error.killed === true &&
|
|
212
|
+
error.signal === 'SIGTERM');
|
|
213
|
+
};
|
|
214
|
+
const materializeGitDiff = async ({ gitOptions, gitRef, workspaceRoot, }) => {
|
|
139
215
|
let tempDir;
|
|
140
216
|
try {
|
|
141
|
-
const
|
|
217
|
+
const gitArgs = gitOptions.scopedPaths.length === 0 ? ['diff', gitRef] : ['diff', gitRef, '--', ...gitOptions.scopedPaths];
|
|
218
|
+
const { stdout } = await execFileAsync('git', gitArgs, {
|
|
142
219
|
cwd: workspaceRoot,
|
|
143
|
-
maxBuffer:
|
|
144
|
-
timeout:
|
|
220
|
+
maxBuffer: gitOptions.maxBufferBytes,
|
|
221
|
+
timeout: gitOptions.timeoutMs,
|
|
145
222
|
});
|
|
146
223
|
tempDir = await mkdtemp(path.join(os.tmpdir(), 'ushman-ledger-git-diff-'));
|
|
147
224
|
const patchPath = path.join(tempDir, 'patch.diff');
|
|
@@ -152,11 +229,14 @@ const materializeGitDiff = async (workspaceRoot, gitRef) => {
|
|
|
152
229
|
if (tempDir) {
|
|
153
230
|
await rm(tempDir, { force: true, recursive: true });
|
|
154
231
|
}
|
|
155
|
-
if (error
|
|
232
|
+
if (getErrorCode(error) === 'ENOENT') {
|
|
156
233
|
throw new CliUsageError('git is required for --diff-from-git and was not found in PATH.');
|
|
157
234
|
}
|
|
158
|
-
if (error
|
|
159
|
-
throw new CliUsageError(`git diff ${gitRef} timed out after ${
|
|
235
|
+
if (isGitDiffTimeoutError(error)) {
|
|
236
|
+
throw new CliUsageError(`git diff ${gitRef} timed out after ${gitOptions.timeoutMs}ms. Narrow the diff or increase --git-diff-timeout-ms.`);
|
|
237
|
+
}
|
|
238
|
+
if (isGitDiffMaxBufferError(error)) {
|
|
239
|
+
throw new CliUsageError(`git diff ${gitRef} exceeded the configured stdout buffer (${gitOptions.maxBufferBytes} bytes). Narrow the diff or increase --git-diff-max-buffer-bytes.`);
|
|
160
240
|
}
|
|
161
241
|
throw error;
|
|
162
242
|
}
|
|
@@ -172,16 +252,13 @@ const parseJsonInput = (text, sourceLabel) => {
|
|
|
172
252
|
const print = (context, text) => {
|
|
173
253
|
context.stdout.write(text.endsWith('\n') ? text : `${text}\n`);
|
|
174
254
|
};
|
|
175
|
-
const parseLimit = (flags) =>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
return Number.parseInt(raw, 10);
|
|
184
|
-
};
|
|
255
|
+
const parseLimit = (flags) => getFlag(flags, 'limit')
|
|
256
|
+
? parsePositiveIntegerFlag({
|
|
257
|
+
defaultValue: 0,
|
|
258
|
+
flagName: 'limit',
|
|
259
|
+
flags,
|
|
260
|
+
})
|
|
261
|
+
: undefined;
|
|
185
262
|
const parseOptionalKind = (flags) => {
|
|
186
263
|
const kind = getFlag(flags, 'kind');
|
|
187
264
|
if (!kind) {
|
|
@@ -234,18 +311,162 @@ const parseRenderTarget = (flags) => {
|
|
|
234
311
|
}
|
|
235
312
|
return target;
|
|
236
313
|
};
|
|
314
|
+
const splitCommandLines = (value) => value
|
|
315
|
+
.split(/\r?\n/u)
|
|
316
|
+
.map((command) => command.trim())
|
|
317
|
+
.filter((command) => command.length > 0);
|
|
318
|
+
const getUsedFlags = (flags, names) => names.filter((name) => hasFlag(flags, name)).map((name) => `--${name}`);
|
|
319
|
+
const rejectUnsupportedFlags = (supportedKindLabel, flags, names) => {
|
|
320
|
+
const usedFlags = getUsedFlags(flags, names);
|
|
321
|
+
if (usedFlags.length === 0) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
throw new CliUsageError(`${usedFlags.join(', ')} ${usedFlags.length === 1 ? 'is' : 'are'} only supported for ${supportedKindLabel} records.`);
|
|
325
|
+
};
|
|
326
|
+
const parseRequiredChangeLogSubkind = (flags) => {
|
|
327
|
+
const subkind = getRequiredString(flags, 'subkind');
|
|
328
|
+
if (!ChangeLogSubkindSchema.options.includes(subkind)) {
|
|
329
|
+
throw new CliUsageError(`Invalid --subkind value: ${subkind}. Expected one of: ${ChangeLogSubkindSchema.options.join(', ')}.`);
|
|
330
|
+
}
|
|
331
|
+
return subkind;
|
|
332
|
+
};
|
|
333
|
+
const parseOptionalChangeLogPicklist = ({ flagName, flags, options, }) => {
|
|
334
|
+
const value = getFlag(flags, flagName);
|
|
335
|
+
if (!value) {
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
if (!options.includes(value)) {
|
|
339
|
+
throw new CliUsageError(`Invalid --${flagName} value: ${value}. Expected one of: ${options.join(', ')}.`);
|
|
340
|
+
}
|
|
341
|
+
return value;
|
|
342
|
+
};
|
|
343
|
+
const parseFilesChangedCsv = (raw) => parseWorkspaceRelativePathCsv({
|
|
344
|
+
flagName: 'files-changed',
|
|
345
|
+
raw,
|
|
346
|
+
}).map((filePath) => ({ path: filePath }));
|
|
347
|
+
const parseGitDiffOptions = (flags) => {
|
|
348
|
+
const gitPaths = getFlag(flags, 'git-paths');
|
|
349
|
+
return {
|
|
350
|
+
maxBufferBytes: parsePositiveIntegerFlag({
|
|
351
|
+
defaultValue: DEFAULT_GIT_DIFF_MAX_BUFFER_BYTES,
|
|
352
|
+
flagName: 'git-diff-max-buffer-bytes',
|
|
353
|
+
flags,
|
|
354
|
+
}),
|
|
355
|
+
scopedPaths: gitPaths
|
|
356
|
+
? parseWorkspaceRelativePathCsv({
|
|
357
|
+
flagName: 'git-paths',
|
|
358
|
+
raw: gitPaths,
|
|
359
|
+
})
|
|
360
|
+
: [],
|
|
361
|
+
timeoutMs: parsePositiveIntegerFlag({
|
|
362
|
+
defaultValue: DEFAULT_GIT_DIFF_TIMEOUT_MS,
|
|
363
|
+
flagName: 'git-diff-timeout-ms',
|
|
364
|
+
flags,
|
|
365
|
+
}),
|
|
366
|
+
};
|
|
367
|
+
};
|
|
368
|
+
const readCommandLines = async (flags) => {
|
|
369
|
+
const inlineCommands = getFlag(flags, 'commands');
|
|
370
|
+
const commandsFrom = getFlag(flags, 'commands-from');
|
|
371
|
+
if (inlineCommands && commandsFrom) {
|
|
372
|
+
throw new CliUsageError('Use either --commands or --commands-from, not both.');
|
|
373
|
+
}
|
|
374
|
+
if (commandsFrom) {
|
|
375
|
+
const commandsPath = path.resolve(commandsFrom);
|
|
376
|
+
await ensureFileExists(commandsPath, '--commands-from');
|
|
377
|
+
return splitCommandLines(await readFile(commandsPath, 'utf8'));
|
|
378
|
+
}
|
|
379
|
+
if (inlineCommands) {
|
|
380
|
+
return splitCommandLines(inlineCommands);
|
|
381
|
+
}
|
|
382
|
+
return undefined;
|
|
383
|
+
};
|
|
384
|
+
const validateGitDiffFlagUsage = (flags, diffFromGitProvided) => {
|
|
385
|
+
if (diffFromGitProvided) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const usedGitFlags = getUsedFlags(flags, GIT_DIFF_FLAG_NAMES);
|
|
389
|
+
if (usedGitFlags.length === 0) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
throw new CliUsageError(`${usedGitFlags.join(', ')} ${usedGitFlags.length === 1 ? 'is' : 'are'} only supported with --diff-from-git.`);
|
|
393
|
+
};
|
|
394
|
+
const assertDiffRecordKindSupported = (kind) => {
|
|
395
|
+
if (kind === 'agent-patch' || kind === 'operator-patch' || kind === 'change-log') {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
throw new CliUsageError('--diff and --diff-from-git are only supported for patch and change-log records.');
|
|
399
|
+
};
|
|
400
|
+
const readPatchText = async (diffPath) => readFile(diffPath, 'utf8');
|
|
401
|
+
const readPatchTextForRecordKind = async (kind, diffPath) => {
|
|
402
|
+
if (kind === 'change-log' || kind === 'agent-patch' || kind === 'operator-patch') {
|
|
403
|
+
return readPatchText(diffPath);
|
|
404
|
+
}
|
|
405
|
+
return undefined;
|
|
406
|
+
};
|
|
407
|
+
const deriveAffectedFiles = (patchText) => [
|
|
408
|
+
...new Set(deriveFilesChangedFromPatch(patchText).map((fileChange) => fileChange.path)),
|
|
409
|
+
];
|
|
410
|
+
const resolveDiffInput = async ({ flags, kind, workspaceRoot, }) => {
|
|
411
|
+
const diffPath = getFlag(flags, 'diff');
|
|
412
|
+
const diffFromGit = getFlag(flags, 'diff-from-git');
|
|
413
|
+
const diffFromGitProvided = hasFlag(flags, 'diff-from-git');
|
|
414
|
+
if (diffPath && diffFromGitProvided) {
|
|
415
|
+
throw new CliUsageError('Use either --diff or --diff-from-git, not both.');
|
|
416
|
+
}
|
|
417
|
+
if (diffFromGitProvided && diffFromGit === '') {
|
|
418
|
+
throw new CliUsageError('--diff-from-git must not be empty.');
|
|
419
|
+
}
|
|
420
|
+
validateGitDiffFlagUsage(flags, diffFromGitProvided);
|
|
421
|
+
if (!diffPath && !diffFromGitProvided) {
|
|
422
|
+
return {};
|
|
423
|
+
}
|
|
424
|
+
assertDiffRecordKindSupported(kind);
|
|
425
|
+
if (diffPath) {
|
|
426
|
+
const resolvedDiffPath = path.resolve(diffPath);
|
|
427
|
+
await ensureFileExists(resolvedDiffPath, '--diff');
|
|
428
|
+
return {
|
|
429
|
+
diffPath: resolvedDiffPath,
|
|
430
|
+
patchText: await readPatchTextForRecordKind(kind, resolvedDiffPath),
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
const materialized = await materializeGitDiff({
|
|
434
|
+
gitOptions: parseGitDiffOptions(flags),
|
|
435
|
+
gitRef: diffFromGit ?? '',
|
|
436
|
+
workspaceRoot,
|
|
437
|
+
});
|
|
438
|
+
return {
|
|
439
|
+
cleanupTempDir: materialized.tempDir,
|
|
440
|
+
diffPath: materialized.patchPath,
|
|
441
|
+
gitRef: diffFromGit,
|
|
442
|
+
patchText: await readPatchTextForRecordKind(kind, materialized.patchPath),
|
|
443
|
+
};
|
|
444
|
+
};
|
|
237
445
|
const validateRecordStdinFlags = (flags) => {
|
|
238
446
|
const conflictingFlags = [
|
|
239
447
|
'agent',
|
|
240
448
|
'action',
|
|
241
449
|
'check-id',
|
|
450
|
+
'commands',
|
|
451
|
+
'commands-from',
|
|
242
452
|
'diff',
|
|
243
453
|
'diff-from-git',
|
|
454
|
+
'files-changed',
|
|
455
|
+
'git-diff-max-buffer-bytes',
|
|
456
|
+
'git-diff-timeout-ms',
|
|
457
|
+
'git-paths',
|
|
458
|
+
'hypothesis',
|
|
244
459
|
'idempotency-key',
|
|
245
460
|
'kind',
|
|
246
461
|
'operator',
|
|
462
|
+
'parity-status',
|
|
247
463
|
'phase',
|
|
248
464
|
'rationale',
|
|
465
|
+
'rollback-plan',
|
|
466
|
+
'rolls-back',
|
|
467
|
+
'smoke-notes',
|
|
468
|
+
'smoke-result',
|
|
469
|
+
'subkind',
|
|
249
470
|
'summary',
|
|
250
471
|
].filter((flagName) => hasFlag(flags, flagName));
|
|
251
472
|
if (conflictingFlags.length === 0) {
|
|
@@ -257,6 +478,9 @@ const validateRecordStdinFlags = (flags) => {
|
|
|
257
478
|
};
|
|
258
479
|
const buildBaseRecordFromFlags = (parsed, context) => {
|
|
259
480
|
const kind = getRequiredKind(parsed.flags);
|
|
481
|
+
if (kind !== 'change-log') {
|
|
482
|
+
rejectUnsupportedFlags('change-log', parsed.flags, CHANGE_LOG_RECORD_ONLY_FLAGS);
|
|
483
|
+
}
|
|
260
484
|
const record = {
|
|
261
485
|
emitter: {
|
|
262
486
|
tool: context.defaultEmitter.tool,
|
|
@@ -296,43 +520,74 @@ const buildBaseRecordFromFlags = (parsed, context) => {
|
|
|
296
520
|
rationale: rationale ?? '',
|
|
297
521
|
};
|
|
298
522
|
}
|
|
523
|
+
if (kind === 'change-log') {
|
|
524
|
+
record.subkind = parseRequiredChangeLogSubkind(parsed.flags);
|
|
525
|
+
}
|
|
299
526
|
const idempotencyKey = getFlag(parsed.flags, 'idempotency-key');
|
|
300
527
|
if (idempotencyKey) {
|
|
301
528
|
record.idempotencyKey = idempotencyKey;
|
|
302
529
|
}
|
|
303
530
|
return { kind, record };
|
|
304
531
|
};
|
|
305
|
-
const applyPatchInputToRecord = async ({ kind,
|
|
306
|
-
const diffPath = getFlag(parsed.flags, 'diff');
|
|
307
|
-
if (diffPath) {
|
|
308
|
-
if (kind !== 'agent-patch' && kind !== 'operator-patch') {
|
|
309
|
-
throw new CliUsageError('--diff is only supported for patch records.');
|
|
310
|
-
}
|
|
311
|
-
await ensureFileExists(path.resolve(diffPath), '--diff');
|
|
312
|
-
record.diffPath = diffPath;
|
|
313
|
-
}
|
|
314
|
-
const diffFromGit = getFlag(parsed.flags, 'diff-from-git');
|
|
315
|
-
if (!diffFromGit) {
|
|
316
|
-
return {};
|
|
317
|
-
}
|
|
532
|
+
const applyPatchInputToRecord = async ({ kind, patchInput, record, }) => {
|
|
318
533
|
if (kind !== 'agent-patch' && kind !== 'operator-patch') {
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
if (diffPath) {
|
|
322
|
-
throw new CliUsageError('Use either --diff or --diff-from-git, not both.');
|
|
534
|
+
return {};
|
|
323
535
|
}
|
|
324
|
-
const materialized = await materializeGitDiff(workspaceRoot, diffFromGit);
|
|
325
536
|
return {
|
|
326
|
-
cleanupTempDir:
|
|
537
|
+
cleanupTempDir: patchInput.cleanupTempDir,
|
|
327
538
|
record: {
|
|
328
539
|
...record,
|
|
329
|
-
diffPath:
|
|
330
|
-
links:
|
|
331
|
-
|
|
332
|
-
|
|
540
|
+
diffPath: patchInput.diffPath,
|
|
541
|
+
links: patchInput.gitRef
|
|
542
|
+
? {
|
|
543
|
+
affectedFiles: patchInput.patchText ? deriveAffectedFiles(patchInput.patchText) : undefined,
|
|
544
|
+
...record.links,
|
|
545
|
+
gitRef: patchInput.gitRef,
|
|
546
|
+
}
|
|
547
|
+
: patchInput.patchText
|
|
548
|
+
? {
|
|
549
|
+
affectedFiles: deriveAffectedFiles(patchInput.patchText),
|
|
550
|
+
...record.links,
|
|
551
|
+
}
|
|
552
|
+
: record.links,
|
|
333
553
|
},
|
|
334
554
|
};
|
|
335
555
|
};
|
|
556
|
+
const applyChangeLogFlagsToRecord = async ({ flags, patchInput, record, }) => {
|
|
557
|
+
const filesChangedFlag = getFlag(flags, 'files-changed');
|
|
558
|
+
if (filesChangedFlag && patchInput.patchText) {
|
|
559
|
+
throw new CliUsageError('Use either --files-changed or --diff/--diff-from-git to populate change-log files.');
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
...record,
|
|
563
|
+
commandsRun: await readCommandLines(flags),
|
|
564
|
+
filesChanged: filesChangedFlag
|
|
565
|
+
? parseFilesChangedCsv(filesChangedFlag)
|
|
566
|
+
: patchInput.patchText
|
|
567
|
+
? deriveFilesChangedFromPatch(patchInput.patchText)
|
|
568
|
+
: undefined,
|
|
569
|
+
hypothesis: getFlag(flags, 'hypothesis'),
|
|
570
|
+
links: patchInput.gitRef
|
|
571
|
+
? {
|
|
572
|
+
...record.links,
|
|
573
|
+
gitRef: patchInput.gitRef,
|
|
574
|
+
}
|
|
575
|
+
: record.links,
|
|
576
|
+
parityStatus: parseOptionalChangeLogPicklist({
|
|
577
|
+
flagName: 'parity-status',
|
|
578
|
+
flags,
|
|
579
|
+
options: ChangeLogParityStatusSchema.options,
|
|
580
|
+
}),
|
|
581
|
+
rollbackPlan: getFlag(flags, 'rollback-plan'),
|
|
582
|
+
rollsBack: getFlag(flags, 'rolls-back'),
|
|
583
|
+
smokeNotes: getFlag(flags, 'smoke-notes'),
|
|
584
|
+
smokeResult: parseOptionalChangeLogPicklist({
|
|
585
|
+
flagName: 'smoke-result',
|
|
586
|
+
flags,
|
|
587
|
+
options: ChangeLogSmokeResultSchema.options,
|
|
588
|
+
}),
|
|
589
|
+
};
|
|
590
|
+
};
|
|
336
591
|
const buildRecordFromFlags = async (parsed, context) => {
|
|
337
592
|
const workspaceRoot = getWorkspaceRoot(parsed.flags);
|
|
338
593
|
if (hasFlag(parsed.flags, 'from-stdin')) {
|
|
@@ -344,17 +599,41 @@ const buildRecordFromFlags = async (parsed, context) => {
|
|
|
344
599
|
};
|
|
345
600
|
}
|
|
346
601
|
const { kind, record } = buildBaseRecordFromFlags(parsed, context);
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
602
|
+
let diffInput;
|
|
603
|
+
try {
|
|
604
|
+
diffInput = await resolveDiffInput({
|
|
605
|
+
flags: parsed.flags,
|
|
606
|
+
kind,
|
|
607
|
+
workspaceRoot,
|
|
608
|
+
});
|
|
609
|
+
if (kind === 'change-log') {
|
|
610
|
+
return {
|
|
611
|
+
cleanupTempDir: diffInput.cleanupTempDir,
|
|
612
|
+
record: await applyChangeLogFlagsToRecord({
|
|
613
|
+
flags: parsed.flags,
|
|
614
|
+
patchInput: diffInput,
|
|
615
|
+
record,
|
|
616
|
+
}),
|
|
617
|
+
workspaceRoot,
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
const patchInput = await applyPatchInputToRecord({
|
|
621
|
+
kind,
|
|
622
|
+
patchInput: diffInput,
|
|
623
|
+
record,
|
|
624
|
+
});
|
|
625
|
+
return {
|
|
626
|
+
cleanupTempDir: patchInput.cleanupTempDir,
|
|
627
|
+
record: patchInput.record ?? record,
|
|
628
|
+
workspaceRoot,
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
catch (error) {
|
|
632
|
+
if (diffInput?.cleanupTempDir) {
|
|
633
|
+
await rm(diffInput.cleanupTempDir, { force: true, recursive: true });
|
|
634
|
+
}
|
|
635
|
+
throw error;
|
|
636
|
+
}
|
|
358
637
|
};
|
|
359
638
|
const buildNoteRecord = async (parsed, context) => {
|
|
360
639
|
const [subkind] = parsed.positionals;
|
|
@@ -461,14 +740,19 @@ const runTailCli = async (parsed, context) => runListCli({
|
|
|
461
740
|
const runRenderCli = async (parsed, context) => {
|
|
462
741
|
const target = parseRenderTarget(parsed.flags);
|
|
463
742
|
const ledger = await openLedger(getWorkspaceRoot(parsed.flags));
|
|
464
|
-
const
|
|
743
|
+
const renderOptions = {
|
|
744
|
+
limit: parseLimit(parsed.flags),
|
|
465
745
|
out: getFlag(parsed.flags, 'out'),
|
|
466
746
|
phase: parseOptionalPhase(parsed.flags),
|
|
747
|
+
since: parseSince(parsed.flags),
|
|
467
748
|
to: target,
|
|
468
|
-
}
|
|
469
|
-
if (
|
|
470
|
-
|
|
749
|
+
};
|
|
750
|
+
if (renderOptions.out) {
|
|
751
|
+
await ledger.renderTo(renderOptions);
|
|
752
|
+
return 0;
|
|
471
753
|
}
|
|
754
|
+
const content = await ledger.render(renderOptions);
|
|
755
|
+
print(context, content);
|
|
472
756
|
return 0;
|
|
473
757
|
};
|
|
474
758
|
const runArchiveCli = async (parsed, context) => {
|
package/dist/doctor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AA+V1E,eAAO,MAAM,eAAe,GACxB,eAAe,MAAM,EACrB,UAAS;IAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAA;CAAO;;;EAyCzF,CAAC"}
|