ushman-ledger 1.2.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/README.md +57 -5
- package/dist/builders.d.ts +1 -2
- package/dist/builders.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +156 -53
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +1 -1
- package/dist/handle.d.ts +27 -7
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +96 -20
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/list.d.ts +1 -1
- package/dist/list.d.ts.map +1 -1
- package/dist/list.js +7 -5
- package/dist/note.d.ts +7 -0
- package/dist/note.d.ts.map +1 -1
- package/dist/note.js +6 -0
- package/dist/patch-resolver.d.ts +12 -0
- package/dist/patch-resolver.d.ts.map +1 -1
- package/dist/patch-resolver.js +12 -0
- package/dist/read-index.d.ts.map +1 -1
- package/dist/record.d.ts.map +1 -1
- package/dist/record.js +1 -2
- package/dist/render/migration-log.d.ts +8 -1
- package/dist/render/migration-log.d.ts.map +1 -1
- package/dist/render/migration-log.js +40 -33
- package/dist/render/retro.d.ts.map +1 -1
- package/dist/render/retro.js +1 -7
- package/dist/render/workspace-narrative.d.ts +7 -1
- package/dist/render/workspace-narrative.d.ts.map +1 -1
- package/dist/render/workspace-narrative.js +114 -46
- package/dist/schema/entry-read.d.ts.map +1 -1
- package/dist/schema/entry-read.js +1 -1
- package/dist/schema/entry-write.d.ts.map +1 -1
- package/dist/schema/entry-write.js +1 -1
- package/dist/schema/entry.d.ts.map +1 -1
- package/dist/storage/filesystem.d.ts +7 -0
- package/dist/storage/filesystem.d.ts.map +1 -1
- package/dist/storage/filesystem.js +80 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,9 +27,20 @@ bun link ushman-ledger
|
|
|
27
27
|
## Library API
|
|
28
28
|
|
|
29
29
|
```ts
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
import { readFile } from 'node:fs/promises';
|
|
31
|
+
import {
|
|
32
|
+
appendCleanupWaveNote,
|
|
33
|
+
appendSemanticCleanupSummaryNote,
|
|
34
|
+
type BuildRecordInput,
|
|
35
|
+
buildChangeLogRecord,
|
|
36
|
+
buildValidatorResultRecord,
|
|
37
|
+
deriveFilesChangedFromPatch,
|
|
38
|
+
openLedger,
|
|
39
|
+
} from 'ushman-ledger';
|
|
40
|
+
|
|
41
|
+
const workspaceRoot = '/path/to/workspace';
|
|
42
|
+
const ledger = await openLedger(workspaceRoot);
|
|
43
|
+
const patchText = await readFile('/tmp/change.patch', 'utf8');
|
|
33
44
|
|
|
34
45
|
await ledger.record({
|
|
35
46
|
emitter: { tool: 'ushman-cli', version: '1.0.0' },
|
|
@@ -48,6 +59,21 @@ await ledger.record({
|
|
|
48
59
|
summary: 'Apply websocket shim',
|
|
49
60
|
});
|
|
50
61
|
|
|
62
|
+
await ledger.record(
|
|
63
|
+
buildChangeLogRecord({
|
|
64
|
+
emitter: { tool: 'ushman-cli', version: '1.0.0' },
|
|
65
|
+
filesChanged: deriveFilesChangedFromPatch(patchText),
|
|
66
|
+
hypothesis: 'Smaller schema modules keep the public API stable.',
|
|
67
|
+
kind: 'change-log',
|
|
68
|
+
parityStatus: 'green',
|
|
69
|
+
phase: 'cleanup',
|
|
70
|
+
rollbackPlan: 'Revert the schema split.',
|
|
71
|
+
smokeResult: 'pass',
|
|
72
|
+
subkind: 'semantic-cleanup',
|
|
73
|
+
summary: 'Split schema modules',
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
|
|
51
77
|
await ledger.record(
|
|
52
78
|
buildValidatorResultRecord({
|
|
53
79
|
emitter: { tool: 'ushman-doctor', version: '1.0.0' },
|
|
@@ -58,16 +84,32 @@ await ledger.record(
|
|
|
58
84
|
}),
|
|
59
85
|
);
|
|
60
86
|
|
|
87
|
+
await appendCleanupWaveNote(workspaceRoot, {
|
|
88
|
+
body: 'Split schema definitions out of the monolithic entry module.',
|
|
89
|
+
phase: 'cleanup',
|
|
90
|
+
summary: 'Wave 1',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await appendSemanticCleanupSummaryNote(workspaceRoot, {
|
|
94
|
+
body: 'The latest semantic cleanup summary shown in workspace-narrative-md.',
|
|
95
|
+
phase: 'migration',
|
|
96
|
+
summary: 'Latest summary',
|
|
97
|
+
});
|
|
98
|
+
|
|
61
99
|
await ledger.render({ to: 'retro' });
|
|
62
100
|
await ledger.render({ to: 'migration-log-md' });
|
|
101
|
+
await ledger.renderTo({ to: 'migration-log-md', out: '/tmp/migration-log.md' });
|
|
63
102
|
await ledger.archive('/tmp/ledger.tgz');
|
|
64
103
|
```
|
|
65
104
|
|
|
105
|
+
Advanced builder wrappers can reuse the exported `BuildRecordInput<T>` helper type when they want the same input contract the built-in builders accept.
|
|
106
|
+
|
|
66
107
|
## CLI
|
|
67
108
|
|
|
68
109
|
```bash
|
|
69
110
|
ushman-ledger record --workspace=<ws> --kind=tool-invocation --phase=capture --summary="capture started"
|
|
70
111
|
ushman-ledger record --workspace=<ws> --kind=agent-patch --phase=cleanup --summary="capture git diff" --rationale="track working tree change" --diff-from-git=HEAD
|
|
112
|
+
ushman-ledger record --workspace=<ws> --kind=change-log --subkind=smoke --phase=cleanup --summary="scope git diff" --diff-from-git=HEAD --git-paths=src/main.ts,src/cli.ts --git-diff-timeout-ms=10000 --git-diff-max-buffer-bytes=20971520
|
|
71
113
|
ushman-ledger record --workspace=<ws> --kind=operator-decision --phase=cleanup --summary="manual override" --action=ledger-hand-edit --check-id=manual-review --rationale="operator edited the ledger after audit"
|
|
72
114
|
ushman-ledger record --workspace=<ws> --kind=change-log --subkind=semantic-cleanup --phase=cleanup --summary="split schema modules" --diff=/tmp/change.patch --hypothesis="smaller schema modules keep the public API stable" --commands=$'bun test\nbun run typecheck' --smoke-result=pass --parity-status=green --rollback-plan="revert the schema split"
|
|
73
115
|
ushman-ledger note regression --workspace=<ws> --phase=cleanup --summary="runtime drift" --body=/tmp/note.md
|
|
@@ -99,7 +141,15 @@ Valid render targets: `retro`, `jsonl`, `timeline-html`, `dependency-graph`, `mi
|
|
|
99
141
|
- optional narrative fields: `hypothesis`, `commandsRun`, `smokeResult`, `smokeNotes`, `parityStatus`, `rollbackPlan`
|
|
100
142
|
- `rollsBack`: required when `subkind=rollback`
|
|
101
143
|
|
|
144
|
+
`deriveFilesChangedFromPatch()` is exported for library callers that want the same diff-to-file summary behavior as the CLI. Known limits:
|
|
145
|
+
|
|
146
|
+
- It expects `diff --git` headers to delimit files.
|
|
147
|
+
- It counts only textual hunk `+`/`-` lines, so binary or mode-only diffs can report zero line counts.
|
|
148
|
+
- It keeps the post-image path when available and rejects diff blocks that cannot be mapped to normalized workspace-relative paths.
|
|
149
|
+
|
|
102
150
|
`migration-log-md` renders only `change-log` entries. `workspace-narrative-md` renders only the dedicated narrative note subkinds.
|
|
151
|
+
`render()` still returns a string for in-memory callers. `renderTo()` is the bounded-output alternative for large ledgers and can write to a callback or the canonical render file on disk.
|
|
152
|
+
`renderTo()` writers receive sequential chunks, can return synchronously or asynchronously, and should throw only when the render should abort.
|
|
103
153
|
|
|
104
154
|
## Workspace prerequisite
|
|
105
155
|
|
|
@@ -167,7 +217,9 @@ Valid render targets: `retro`, `jsonl`, `timeline-html`, `dependency-graph`, `mi
|
|
|
167
217
|
## Git diff capture
|
|
168
218
|
|
|
169
219
|
- `--diff-from-git=<ref>` runs `git diff <ref>` against the workspace working tree.
|
|
170
|
-
-
|
|
220
|
+
- `--git-paths=<csv>` safely scopes `git diff` to literal normalized workspace-relative paths. The CLI passes them after `--`, so pathspec magic and shell token splitting are intentionally not supported here.
|
|
221
|
+
- `--git-diff-timeout-ms=<ms>` and `--git-diff-max-buffer-bytes=<bytes>` override the default 30 second timeout and 10 MiB stdout buffer for the git diff helper.
|
|
222
|
+
- The CLI reports usage errors for missing `git`, timed out git diffs, invalid scoped paths, and buffer overflows instead of surfacing raw child-process failures.
|
|
171
223
|
- This is the only CLI feature that depends on an external binary. `git` must be available in `PATH`.
|
|
172
224
|
|
|
173
225
|
## Storage shape
|
|
@@ -200,5 +252,5 @@ bun run bench:scale
|
|
|
200
252
|
|
|
201
253
|
## Scale benchmark
|
|
202
254
|
|
|
203
|
-
- `bun run bench:scale` creates a temporary workspace and benchmarks large-ledger paths for population, limited reads, repeated coverage,
|
|
255
|
+
- `bun run bench:scale` creates a temporary workspace and benchmarks large-ledger paths for population, limited reads, repeated coverage, repeated doctor runs, and markdown render paths.
|
|
204
256
|
- Override the defaults with environment variables such as `LEDGER_BENCH_ENTRY_COUNT=100000` and `LEDGER_BENCH_CANDIDATE_FILE_COUNT=10000`.
|
package/dist/builders.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type LedgerRecord } from './schema/entry.ts';
|
|
2
|
-
type BuildRecordInput<TRecord extends LedgerRecord> = Omit<TRecord, 'kind'> & {
|
|
2
|
+
export type BuildRecordInput<TRecord extends LedgerRecord> = Omit<TRecord, 'kind'> & {
|
|
3
3
|
readonly kind?: TRecord['kind'];
|
|
4
4
|
};
|
|
5
5
|
/** Build an `operator-decision` record with a validated structured payload. */
|
|
@@ -182,5 +182,4 @@ export declare const buildStripDecisionRevertedRecord: (input: BuildRecordInput<
|
|
|
182
182
|
phase: "capture" | "intake" | "seed" | "vendor-extract" | "cleanup" | "parity" | "characterize" | "equiv" | "analyze" | "recover" | "ship" | "migration";
|
|
183
183
|
summary: string;
|
|
184
184
|
};
|
|
185
|
-
export {};
|
|
186
185
|
//# sourceMappingURL=builders.d.ts.map
|
package/dist/builders.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AACA,OAAO,EAGH,KAAK,YAAY,EAIpB,MAAM,mBAAmB,CAAC;AAE3B,
|
|
1
|
+
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AACA,OAAO,EAGH,KAAK,YAAY,EAIpB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,gBAAgB,CAAC,OAAO,SAAS,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG;IACjF,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,2BAA2B,GACpC,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAM3E,CAAC;AAEP,yCAAyC;AACzC,eAAO,MAAM,0BAA0B,GACnC,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAK1E,CAAC;AAEP,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,GAAI,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CAItG,CAAC;AAEP,mCAAmC;AACnC,eAAO,MAAM,oBAAoB,GAAI,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAIrG,CAAC;AAEP,gDAAgD;AAChD,eAAO,MAAM,gCAAgC,GACzC,OAAO,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,yBAAyB,CAAA;CAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAKjF,CAAC"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAuDA,KAAK,UAAU,GAAG;IACd,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE;QACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG,aAAa,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC1C,CAAC;AAi5BF,eAAO,MAAM,YAAY,GAAU,MAAM,SAAS,MAAM,EAAE,EAAE,UAAS,OAAO,CAAC,UAAU,CAAM,KAAG,OAAO,CAAC,MAAM,CAqC7G,CAAC;AAEF,eAAO,MAAM,IAAI,GAAU,OAAM,SAAS,MAAM,EAA0B,KAAG,OAAO,CAAC,MAAM,CAE1F,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -12,8 +12,8 @@ import { ChangeLogParityStatusSchema, ChangeLogSmokeResultSchema, ChangeLogSubki
|
|
|
12
12
|
import { NoteSubkindSchema } from "./schema/note.js";
|
|
13
13
|
import { LEDGER_LIBRARY_VERSION } from "./version.js";
|
|
14
14
|
const execFileAsync = promisify(execFile);
|
|
15
|
-
const
|
|
16
|
-
const
|
|
15
|
+
const DEFAULT_GIT_DIFF_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
16
|
+
const DEFAULT_GIT_DIFF_TIMEOUT_MS = 30_000;
|
|
17
17
|
const RENDER_TARGETS = [
|
|
18
18
|
'retro',
|
|
19
19
|
'jsonl',
|
|
@@ -22,6 +22,7 @@ const RENDER_TARGETS = [
|
|
|
22
22
|
'migration-log-md',
|
|
23
23
|
'workspace-narrative-md',
|
|
24
24
|
];
|
|
25
|
+
const GIT_DIFF_FLAG_NAMES = ['git-diff-max-buffer-bytes', 'git-diff-timeout-ms', 'git-paths'];
|
|
25
26
|
const CHANGE_LOG_RECORD_ONLY_FLAGS = [
|
|
26
27
|
'commands',
|
|
27
28
|
'commands-from',
|
|
@@ -34,6 +35,7 @@ const CHANGE_LOG_RECORD_ONLY_FLAGS = [
|
|
|
34
35
|
'smoke-result',
|
|
35
36
|
'subkind',
|
|
36
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]`;
|
|
37
39
|
class CliUsageError extends Error {
|
|
38
40
|
}
|
|
39
41
|
const DEFAULT_CONTEXT = {
|
|
@@ -55,7 +57,7 @@ const renderValidValues = () => `Valid values:
|
|
|
55
57
|
const renderHelp = (commandName) => `${commandName}
|
|
56
58
|
|
|
57
59
|
Commands:
|
|
58
|
-
${commandName}
|
|
60
|
+
${renderRecordUsage(commandName)}
|
|
59
61
|
${commandName} note <subkind> [--workspace=<ws>] --phase=<phase> --summary="..." [--body=<markdown-file>] [--from-stdin]
|
|
60
62
|
${commandName} list [--workspace=<ws>] [--phase=<phase>] [--kind=<kind>] [--since=<iso>] [--limit=<n>] [--json]
|
|
61
63
|
${commandName} show [--workspace=<ws>] <entry-id>
|
|
@@ -69,7 +71,7 @@ ${renderValidValues()}`;
|
|
|
69
71
|
const renderCommandHelp = (commandName, command) => {
|
|
70
72
|
switch (command) {
|
|
71
73
|
case 'record':
|
|
72
|
-
return `${commandName}
|
|
74
|
+
return `${renderRecordUsage(commandName)}
|
|
73
75
|
|
|
74
76
|
${renderValidValues()}`;
|
|
75
77
|
case 'note':
|
|
@@ -159,13 +161,64 @@ const ensureFileExists = async (filePath, flagName) => {
|
|
|
159
161
|
throw error;
|
|
160
162
|
}
|
|
161
163
|
};
|
|
162
|
-
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, }) => {
|
|
163
215
|
let tempDir;
|
|
164
216
|
try {
|
|
165
|
-
const
|
|
217
|
+
const gitArgs = gitOptions.scopedPaths.length === 0 ? ['diff', gitRef] : ['diff', gitRef, '--', ...gitOptions.scopedPaths];
|
|
218
|
+
const { stdout } = await execFileAsync('git', gitArgs, {
|
|
166
219
|
cwd: workspaceRoot,
|
|
167
|
-
maxBuffer:
|
|
168
|
-
timeout:
|
|
220
|
+
maxBuffer: gitOptions.maxBufferBytes,
|
|
221
|
+
timeout: gitOptions.timeoutMs,
|
|
169
222
|
});
|
|
170
223
|
tempDir = await mkdtemp(path.join(os.tmpdir(), 'ushman-ledger-git-diff-'));
|
|
171
224
|
const patchPath = path.join(tempDir, 'patch.diff');
|
|
@@ -176,11 +229,14 @@ const materializeGitDiff = async (workspaceRoot, gitRef) => {
|
|
|
176
229
|
if (tempDir) {
|
|
177
230
|
await rm(tempDir, { force: true, recursive: true });
|
|
178
231
|
}
|
|
179
|
-
if (error
|
|
232
|
+
if (getErrorCode(error) === 'ENOENT') {
|
|
180
233
|
throw new CliUsageError('git is required for --diff-from-git and was not found in PATH.');
|
|
181
234
|
}
|
|
182
|
-
if (error
|
|
183
|
-
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.`);
|
|
184
240
|
}
|
|
185
241
|
throw error;
|
|
186
242
|
}
|
|
@@ -196,16 +252,13 @@ const parseJsonInput = (text, sourceLabel) => {
|
|
|
196
252
|
const print = (context, text) => {
|
|
197
253
|
context.stdout.write(text.endsWith('\n') ? text : `${text}\n`);
|
|
198
254
|
};
|
|
199
|
-
const parseLimit = (flags) =>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
return Number.parseInt(raw, 10);
|
|
208
|
-
};
|
|
255
|
+
const parseLimit = (flags) => getFlag(flags, 'limit')
|
|
256
|
+
? parsePositiveIntegerFlag({
|
|
257
|
+
defaultValue: 0,
|
|
258
|
+
flagName: 'limit',
|
|
259
|
+
flags,
|
|
260
|
+
})
|
|
261
|
+
: undefined;
|
|
209
262
|
const parseOptionalKind = (flags) => {
|
|
210
263
|
const kind = getFlag(flags, 'kind');
|
|
211
264
|
if (!kind) {
|
|
@@ -287,25 +340,30 @@ const parseOptionalChangeLogPicklist = ({ flagName, flags, options, }) => {
|
|
|
287
340
|
}
|
|
288
341
|
return value;
|
|
289
342
|
};
|
|
290
|
-
const parseFilesChangedCsv = (raw) => {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
+
};
|
|
309
367
|
};
|
|
310
368
|
const readCommandLines = async (flags) => {
|
|
311
369
|
const inlineCommands = getFlag(flags, 'commands');
|
|
@@ -323,32 +381,65 @@ const readCommandLines = async (flags) => {
|
|
|
323
381
|
}
|
|
324
382
|
return undefined;
|
|
325
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
|
+
];
|
|
326
410
|
const resolveDiffInput = async ({ flags, kind, workspaceRoot, }) => {
|
|
327
411
|
const diffPath = getFlag(flags, 'diff');
|
|
328
412
|
const diffFromGit = getFlag(flags, 'diff-from-git');
|
|
329
|
-
|
|
413
|
+
const diffFromGitProvided = hasFlag(flags, 'diff-from-git');
|
|
414
|
+
if (diffPath && diffFromGitProvided) {
|
|
330
415
|
throw new CliUsageError('Use either --diff or --diff-from-git, not both.');
|
|
331
416
|
}
|
|
332
|
-
if (
|
|
333
|
-
|
|
417
|
+
if (diffFromGitProvided && diffFromGit === '') {
|
|
418
|
+
throw new CliUsageError('--diff-from-git must not be empty.');
|
|
334
419
|
}
|
|
335
|
-
|
|
336
|
-
|
|
420
|
+
validateGitDiffFlagUsage(flags, diffFromGitProvided);
|
|
421
|
+
if (!diffPath && !diffFromGitProvided) {
|
|
422
|
+
return {};
|
|
337
423
|
}
|
|
424
|
+
assertDiffRecordKindSupported(kind);
|
|
338
425
|
if (diffPath) {
|
|
339
426
|
const resolvedDiffPath = path.resolve(diffPath);
|
|
340
427
|
await ensureFileExists(resolvedDiffPath, '--diff');
|
|
341
428
|
return {
|
|
342
429
|
diffPath: resolvedDiffPath,
|
|
343
|
-
patchText:
|
|
430
|
+
patchText: await readPatchTextForRecordKind(kind, resolvedDiffPath),
|
|
344
431
|
};
|
|
345
432
|
}
|
|
346
|
-
const materialized = await materializeGitDiff(
|
|
433
|
+
const materialized = await materializeGitDiff({
|
|
434
|
+
gitOptions: parseGitDiffOptions(flags),
|
|
435
|
+
gitRef: diffFromGit ?? '',
|
|
436
|
+
workspaceRoot,
|
|
437
|
+
});
|
|
347
438
|
return {
|
|
348
439
|
cleanupTempDir: materialized.tempDir,
|
|
349
440
|
diffPath: materialized.patchPath,
|
|
350
441
|
gitRef: diffFromGit,
|
|
351
|
-
patchText:
|
|
442
|
+
patchText: await readPatchTextForRecordKind(kind, materialized.patchPath),
|
|
352
443
|
};
|
|
353
444
|
};
|
|
354
445
|
const validateRecordStdinFlags = (flags) => {
|
|
@@ -361,6 +452,9 @@ const validateRecordStdinFlags = (flags) => {
|
|
|
361
452
|
'diff',
|
|
362
453
|
'diff-from-git',
|
|
363
454
|
'files-changed',
|
|
455
|
+
'git-diff-max-buffer-bytes',
|
|
456
|
+
'git-diff-timeout-ms',
|
|
457
|
+
'git-paths',
|
|
364
458
|
'hypothesis',
|
|
365
459
|
'idempotency-key',
|
|
366
460
|
'kind',
|
|
@@ -446,10 +540,16 @@ const applyPatchInputToRecord = async ({ kind, patchInput, record, }) => {
|
|
|
446
540
|
diffPath: patchInput.diffPath,
|
|
447
541
|
links: patchInput.gitRef
|
|
448
542
|
? {
|
|
543
|
+
affectedFiles: patchInput.patchText ? deriveAffectedFiles(patchInput.patchText) : undefined,
|
|
449
544
|
...record.links,
|
|
450
545
|
gitRef: patchInput.gitRef,
|
|
451
546
|
}
|
|
452
|
-
:
|
|
547
|
+
: patchInput.patchText
|
|
548
|
+
? {
|
|
549
|
+
affectedFiles: deriveAffectedFiles(patchInput.patchText),
|
|
550
|
+
...record.links,
|
|
551
|
+
}
|
|
552
|
+
: record.links,
|
|
453
553
|
},
|
|
454
554
|
};
|
|
455
555
|
};
|
|
@@ -640,16 +740,19 @@ const runTailCli = async (parsed, context) => runListCli({
|
|
|
640
740
|
const runRenderCli = async (parsed, context) => {
|
|
641
741
|
const target = parseRenderTarget(parsed.flags);
|
|
642
742
|
const ledger = await openLedger(getWorkspaceRoot(parsed.flags));
|
|
643
|
-
const
|
|
743
|
+
const renderOptions = {
|
|
644
744
|
limit: parseLimit(parsed.flags),
|
|
645
745
|
out: getFlag(parsed.flags, 'out'),
|
|
646
746
|
phase: parseOptionalPhase(parsed.flags),
|
|
647
747
|
since: parseSince(parsed.flags),
|
|
648
748
|
to: target,
|
|
649
|
-
}
|
|
650
|
-
if (
|
|
651
|
-
|
|
749
|
+
};
|
|
750
|
+
if (renderOptions.out) {
|
|
751
|
+
await ledger.renderTo(renderOptions);
|
|
752
|
+
return 0;
|
|
652
753
|
}
|
|
754
|
+
const content = await ledger.render(renderOptions);
|
|
755
|
+
print(context, content);
|
|
653
756
|
return 0;
|
|
654
757
|
};
|
|
655
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"}
|
package/dist/doctor.js
CHANGED
|
@@ -200,8 +200,8 @@ const collectDoctorState = async (workspaceRoot, manifest, readIndex) => {
|
|
|
200
200
|
inspectDoctorEntry({
|
|
201
201
|
blobChecks,
|
|
202
202
|
checkpointEntries,
|
|
203
|
-
entry: resolvedEntry.entry,
|
|
204
203
|
entriesByIdempotencyKey,
|
|
204
|
+
entry: resolvedEntry.entry,
|
|
205
205
|
issues,
|
|
206
206
|
latestByPhase,
|
|
207
207
|
manifest,
|
package/dist/handle.d.ts
CHANGED
|
@@ -4,6 +4,30 @@ import { type LedgerFilter } from './list.ts';
|
|
|
4
4
|
import { appendNote, type NoteBody } from './note.ts';
|
|
5
5
|
import type { LedgerEntry, LedgerPhase } from './schema/entry.ts';
|
|
6
6
|
export type RenderTarget = 'dependency-graph' | 'jsonl' | 'migration-log-md' | 'retro' | 'timeline-html' | 'workspace-narrative-md';
|
|
7
|
+
/**
|
|
8
|
+
* Chunk writer used by `renderTo()` for bounded render emission.
|
|
9
|
+
*
|
|
10
|
+
* Writers may complete synchronously or asynchronously. Throwing rejects the render and causes any in-progress
|
|
11
|
+
* atomic output file to be aborted.
|
|
12
|
+
*/
|
|
13
|
+
export type RenderWriter = (chunk: string) => Promise<void> | void;
|
|
14
|
+
/** Options for `render()`, which returns the rendered text and optionally mirrors it to disk. */
|
|
15
|
+
export type LedgerRenderOptions = {
|
|
16
|
+
readonly limit?: number;
|
|
17
|
+
readonly out?: string;
|
|
18
|
+
readonly phase?: LedgerPhase;
|
|
19
|
+
readonly since?: string;
|
|
20
|
+
readonly to: RenderTarget;
|
|
21
|
+
};
|
|
22
|
+
/** Options for `renderTo()`, which writes to a callback and/or canonical render file without buffering the full output. */
|
|
23
|
+
export type LedgerRenderToOptions = {
|
|
24
|
+
readonly limit?: number;
|
|
25
|
+
readonly out?: string;
|
|
26
|
+
readonly phase?: LedgerPhase;
|
|
27
|
+
readonly since?: string;
|
|
28
|
+
readonly to: RenderTarget;
|
|
29
|
+
readonly write?: RenderWriter;
|
|
30
|
+
};
|
|
7
31
|
export type LedgerHandle = {
|
|
8
32
|
readonly archive: (outPath: string) => Promise<{
|
|
9
33
|
integrityHash: string;
|
|
@@ -17,14 +41,10 @@ export type LedgerHandle = {
|
|
|
17
41
|
readonly record: (entry: unknown) => Promise<{
|
|
18
42
|
id: string;
|
|
19
43
|
}>;
|
|
20
|
-
readonly render: (options:
|
|
21
|
-
|
|
22
|
-
limit?: number;
|
|
23
|
-
phase?: LedgerPhase;
|
|
24
|
-
since?: string;
|
|
25
|
-
to: RenderTarget;
|
|
26
|
-
}) => Promise<string>;
|
|
44
|
+
readonly render: (options: LedgerRenderOptions) => Promise<string>;
|
|
45
|
+
readonly renderTo: (options: LedgerRenderToOptions) => Promise<void>;
|
|
27
46
|
readonly show: (entryId: string) => Promise<LedgerEntry | null>;
|
|
28
47
|
};
|
|
48
|
+
/** Open a workspace ledger handle after reconciling any pending crash-recovery state. */
|
|
29
49
|
export declare const openLedger: (workspaceRoot: string) => Promise<LedgerHandle>;
|
|
30
50
|
//# sourceMappingURL=handle.d.ts.map
|
package/dist/handle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAA8B,KAAK,YAAY,EAAe,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAStD,OAAO,KAAK,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAA8B,KAAK,YAAY,EAAe,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAStD,OAAO,KAAK,EAAE,WAAW,EAAc,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG9E,MAAM,MAAM,YAAY,GAClB,kBAAkB,GAClB,OAAO,GACP,kBAAkB,GAClB,OAAO,GACP,eAAe,GACf,wBAAwB,CAAC;AAE/B;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEnE,iGAAiG;AACjG,MAAM,MAAM,mBAAmB,GAAG;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,2HAA2H;AAC3H,MAAM,MAAM,qBAAqB,GAAG;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AA4GF,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1E,QAAQ,CAAC,eAAe,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC;IACrF,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC;IAC5E,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,YAAY,KAAK,aAAa,CAAC,WAAW,CAAC,CAAC;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtG,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACnE,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,UAAU,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,YAAY,CAyF5E,CAAC"}
|