ushman-ledger 1.2.0 → 1.2.2
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/ARCHITECTURE.md +79 -0
- package/README.md +144 -5
- package/TROUBLESHOOTING.md +170 -0
- package/dist/blobs.d.ts +3 -0
- package/dist/blobs.d.ts.map +1 -1
- package/dist/blobs.js +41 -15
- 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 +231 -70
- package/dist/coverage.d.ts.map +1 -1
- package/dist/coverage.js +3 -2
- package/dist/doctor.d.ts +17 -4
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +225 -58
- package/dist/handle.d.ts +27 -7
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +96 -20
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +23 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/list.d.ts +3 -2
- package/dist/list.d.ts.map +1 -1
- package/dist/list.js +24 -12
- 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 +205 -53
- package/dist/read-index.d.ts.map +1 -1
- package/dist/read-index.js +6 -5
- package/dist/record.d.ts.map +1 -1
- package/dist/record.js +3 -3
- 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/runtime-config.d.ts +12 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +83 -0
- 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 +8 -0
- package/dist/storage/filesystem.d.ts.map +1 -1
- package/dist/storage/filesystem.js +110 -5
- package/dist/text-lines.d.ts +8 -0
- package/dist/text-lines.d.ts.map +1 -0
- package/dist/text-lines.js +20 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -1
- package/package.json +5 -3
package/dist/helpers.js
CHANGED
|
@@ -36,3 +36,26 @@ export const waitForFile = async (filePath, timeoutMs = 1_000) => {
|
|
|
36
36
|
}
|
|
37
37
|
throw new Error(`Timed out waiting for file: ${filePath}`);
|
|
38
38
|
};
|
|
39
|
+
export const withEnvOverrides = async (overrides, action) => {
|
|
40
|
+
const previousValues = new Map();
|
|
41
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
42
|
+
previousValues.set(key, process.env[key]);
|
|
43
|
+
if (value === undefined) {
|
|
44
|
+
delete process.env[key];
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
process.env[key] = value;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
return await action();
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
for (const [key, value] of previousValues.entries()) {
|
|
54
|
+
if (value === undefined) {
|
|
55
|
+
delete process.env[key];
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
process.env[key] = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
export { buildChangeLogRecord, buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from './builders.ts';
|
|
1
|
+
export { type BuildRecordInput, buildChangeLogRecord, buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from './builders.ts';
|
|
2
2
|
export { runLedgerCli } from './cli.ts';
|
|
3
3
|
export { type CoverageReport, computeCoverage } from './coverage.ts';
|
|
4
|
-
export { type
|
|
4
|
+
export { DOCTOR_FINDING_CODES, type DoctorFinding, type DoctorFindingCode, type DoctorFindingMetadataValue, type DoctorReport, runLedgerDoctor, } from './doctor.ts';
|
|
5
|
+
export { type LedgerHandle, type LedgerRenderOptions, type LedgerRenderToOptions, openLedger, type RenderTarget, type RenderWriter, } from './handle.ts';
|
|
5
6
|
export type { LedgerFilter } from './list.ts';
|
|
7
|
+
export { type LedgerRuntimeConfig, getLedgerRuntimeConfig, validateLedgerRuntimeConfig } from './runtime-config.ts';
|
|
6
8
|
export { appendCleanupWaveNote, appendDecompositionWaveNote, appendNote, appendOpenIssueNote, appendSemanticCleanupSummaryNote, appendVerifiedFlowNote, type NoteBody, } from './note.ts';
|
|
7
|
-
export {
|
|
9
|
+
export { deriveFilesChangedFromPatch } from './patch-resolver.ts';
|
|
10
|
+
export { type AgentPatchDiff, AgentPatchDiffSchema, AgentPatchEntrySchema, AgentPatchRecordSchema, type ChangeLogEntry, ChangeLogEntrySchema, type ChangeLogFileChange, ChangeLogFileChangeSchema, type ChangeLogParityStatus, ChangeLogParityStatusSchema, type ChangeLogRecord, ChangeLogRecordSchema, type ChangeLogSmokeResult, ChangeLogSmokeResultSchema, type ChangeLogSubkind, ChangeLogSubkindSchema, CorrectionEntrySchema, CorrectionRecordSchema, EmitterSchema, type LedgerEntry, LedgerEntrySchema, type LedgerKind, type LedgerLinks, LedgerLinksSchema, type LedgerPhase, LedgerPhaseSchema, type LedgerRecord, LedgerRecordSchema, type NoteEntry, NoteEntrySchema, type OperatorDecisionAction, OperatorDecisionActionSchema, OperatorDecisionEntrySchema, type OperatorDecisionPayload, OperatorDecisionPayloadSchema, OperatorDecisionRecordSchema, OperatorPatchEntrySchema, OperatorPatchRecordSchema, parseLedgerEntry, parseLedgerRecord, RuntimeEventEntrySchema, StripDecisionRevertedEntrySchema, type StripDecisionRevertedPayload, StripDecisionRevertedPayloadSchema, StripDecisionRevertedRecordSchema, ToolInvocationEntrySchema, ToolInvocationRecordSchema, ValidatorResultEntrySchema, ValidatorResultRecordSchema, } from './schema/entry.ts';
|
|
8
11
|
export { type NoteSubkind, NoteSubkindSchema } from './schema/note.ts';
|
|
9
12
|
export { resolveLedgerPaths } from './storage/filesystem.ts';
|
|
10
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,oBAAoB,EACpB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,0BAA0B,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,gBAAgB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,0BAA0B,GAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,KAAK,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EACH,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,EAC/B,KAAK,YAAY,EACjB,eAAe,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,mBAAmB,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,EACH,qBAAqB,EACrB,2BAA2B,EAC3B,UAAU,EACV,mBAAmB,EACnB,gCAAgC,EAChC,sBAAsB,EACtB,KAAK,QAAQ,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACH,KAAK,cAAc,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,KAAK,cAAc,EACnB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,YAAY,EACjB,kBAAkB,EAClB,KAAK,SAAS,EACd,eAAe,EACf,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,2BAA2B,EAC3B,KAAK,uBAAuB,EAC5B,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,gCAAgC,EAChC,KAAK,4BAA4B,EACjC,kCAAkC,EAClC,iCAAiC,EACjC,yBAAyB,EACzB,0BAA0B,EAC1B,0BAA0B,EAC1B,2BAA2B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
export { buildChangeLogRecord, buildCorrectionRecord, buildOperatorDecisionRecord, buildStripDecisionRevertedRecord, buildValidatorResultRecord, } from "./builders.js";
|
|
2
2
|
export { runLedgerCli } from "./cli.js";
|
|
3
3
|
export { computeCoverage } from "./coverage.js";
|
|
4
|
-
export {
|
|
4
|
+
export { DOCTOR_FINDING_CODES, runLedgerDoctor, } from "./doctor.js";
|
|
5
|
+
export { openLedger, } from "./handle.js";
|
|
6
|
+
export { getLedgerRuntimeConfig, validateLedgerRuntimeConfig } from "./runtime-config.js";
|
|
5
7
|
export { appendCleanupWaveNote, appendDecompositionWaveNote, appendNote, appendOpenIssueNote, appendSemanticCleanupSummaryNote, appendVerifiedFlowNote, } from "./note.js";
|
|
8
|
+
export { deriveFilesChangedFromPatch } from "./patch-resolver.js";
|
|
6
9
|
export { AgentPatchDiffSchema, AgentPatchEntrySchema, AgentPatchRecordSchema, ChangeLogEntrySchema, ChangeLogFileChangeSchema, ChangeLogParityStatusSchema, ChangeLogRecordSchema, ChangeLogSmokeResultSchema, ChangeLogSubkindSchema, CorrectionEntrySchema, CorrectionRecordSchema, EmitterSchema, LedgerEntrySchema, LedgerLinksSchema, LedgerPhaseSchema, LedgerRecordSchema, NoteEntrySchema, OperatorDecisionActionSchema, OperatorDecisionEntrySchema, OperatorDecisionPayloadSchema, OperatorDecisionRecordSchema, OperatorPatchEntrySchema, OperatorPatchRecordSchema, parseLedgerEntry, parseLedgerRecord, RuntimeEventEntrySchema, StripDecisionRevertedEntrySchema, StripDecisionRevertedPayloadSchema, StripDecisionRevertedRecordSchema, ToolInvocationEntrySchema, ToolInvocationRecordSchema, ValidatorResultEntrySchema, ValidatorResultRecordSchema, } from "./schema/entry.js";
|
|
7
10
|
export { NoteSubkindSchema } from "./schema/note.js";
|
|
8
11
|
export { resolveLedgerPaths } from "./storage/filesystem.js";
|
package/dist/list.d.ts
CHANGED
|
@@ -8,9 +8,10 @@ export type LedgerFilter = {
|
|
|
8
8
|
readonly since?: string;
|
|
9
9
|
};
|
|
10
10
|
export declare const getOrderedEntryLocations: (manifest: LedgerManifest, readIndex: LedgerReadIndex, filter: LedgerFilter, direction?: "asc" | "desc") => ManifestEntryLocation[];
|
|
11
|
-
export declare const readManifestEntryBatch: ({ allowMissing, entryLocations, workspaceRoot, }: {
|
|
11
|
+
export declare const readManifestEntryBatch: ({ allowMissing, entryLocations, entryReadConcurrency, workspaceRoot, }: {
|
|
12
12
|
readonly allowMissing?: boolean;
|
|
13
13
|
readonly entryLocations: readonly ManifestEntryLocation[];
|
|
14
|
+
readonly entryReadConcurrency?: number;
|
|
14
15
|
readonly workspaceRoot: string;
|
|
15
16
|
}) => Promise<({
|
|
16
17
|
entry: {
|
|
@@ -377,7 +378,7 @@ export declare const readManifestEntryBatch: ({ allowMissing, entryLocations, wo
|
|
|
377
378
|
sequence: number;
|
|
378
379
|
};
|
|
379
380
|
})[]>;
|
|
380
|
-
export declare const iterateEntriesFromManifest: (workspaceRoot: string, manifest: LedgerManifest, readIndex: LedgerReadIndex, filter?: LedgerFilter) => AsyncIterable<LedgerEntry>;
|
|
381
|
+
export declare const iterateEntriesFromManifest: (workspaceRoot: string, manifest: LedgerManifest, readIndex: LedgerReadIndex, filter?: LedgerFilter, direction?: "asc" | "desc") => AsyncIterable<LedgerEntry>;
|
|
381
382
|
export declare const listEntries: (workspaceRoot: string, filter?: LedgerFilter) => AsyncIterable<LedgerEntry>;
|
|
382
383
|
export declare const readAllEntries: (workspaceRoot: string, filter?: LedgerFilter) => Promise<LedgerEntry[]>;
|
|
383
384
|
//# sourceMappingURL=list.d.ts.map
|
package/dist/list.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAG7B,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAG7B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAC1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAgDF,eAAO,MAAM,wBAAwB,GACjC,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,QAAQ,YAAY,EACpB,YAAW,KAAK,GAAG,MAAc,4BAgBpC,CAAC;AAiCF,eAAO,MAAM,sBAAsB,GAAU,wEAK1C;IACC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC1D,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAQI,CAAC;AAsDN,eAAO,MAAM,0BAA0B,GACnC,eAAe,MAAM,EACrB,UAAU,cAAc,EACxB,WAAW,eAAe,EAC1B,SAAQ,YAAiB,EACzB,YAAW,KAAK,GAAG,MAAc,KAClC,aAAa,CAAC,WAAW,CAoC3B,CAAC;AAEF,eAAO,MAAM,WAAW,GACpB,eAAe,MAAM,EACrB,SAAQ,YAAiB,KAC1B,aAAa,CAAC,WAAW,CAG3B,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,eAAe,MAAM,EAAE,SAAQ,YAAiB,KAAG,OAAO,CAAC,WAAW,EAAE,CAM5G,CAAC"}
|
package/dist/list.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mapWithConcurrencyLimit } from "./async.js";
|
|
2
2
|
import { matchesReadIndexFilter, } from "./read-index.js";
|
|
3
|
+
import { getLedgerRuntimeConfig } from "./runtime-config.js";
|
|
3
4
|
import { loadLedgerState } from "./recovery.js";
|
|
4
5
|
import { parseLedgerEntry } from "./schema/entry.js";
|
|
5
6
|
import { readPhaseEntryText } from "./storage/filesystem.js";
|
|
@@ -15,8 +16,6 @@ const matchesFilter = (entry, filter) => {
|
|
|
15
16
|
}
|
|
16
17
|
return true;
|
|
17
18
|
};
|
|
18
|
-
const ENTRY_READ_BATCH_SIZE = 32;
|
|
19
|
-
const ENTRY_READ_CONCURRENCY = 16;
|
|
20
19
|
const getFilteredReadIndexEntries = ({ direction, filter, manifest, readIndex, }) => {
|
|
21
20
|
const sourceEntries = direction === 'asc' ? readIndex.entries : [...readIndex.entries].reverse();
|
|
22
21
|
const filteredEntries = [];
|
|
@@ -71,20 +70,21 @@ const readManifestEntry = async ({ allowMissing, entryId, location, workspaceRoo
|
|
|
71
70
|
throw error;
|
|
72
71
|
}
|
|
73
72
|
};
|
|
74
|
-
export const readManifestEntryBatch = async ({ allowMissing = false, entryLocations, workspaceRoot, }) => mapWithConcurrencyLimit(entryLocations,
|
|
73
|
+
export const readManifestEntryBatch = async ({ allowMissing = false, entryLocations, entryReadConcurrency = getLedgerRuntimeConfig().scanConcurrency, workspaceRoot, }) => mapWithConcurrencyLimit(entryLocations, entryReadConcurrency, async ([entryId, location]) => readManifestEntry({
|
|
75
74
|
allowMissing,
|
|
76
75
|
entryId,
|
|
77
76
|
location,
|
|
78
77
|
workspaceRoot,
|
|
79
78
|
}));
|
|
80
|
-
const collectLimitedEntries = async ({ filter, manifest, readIndex, workspaceRoot, }) => {
|
|
79
|
+
const collectLimitedEntries = async ({ batchSize, direction, entryReadConcurrency, filter, manifest, readIndex, workspaceRoot, }) => {
|
|
81
80
|
const collectedEntries = [];
|
|
82
81
|
const orderedEntries = getOrderedEntryLocations(manifest, readIndex, filter, 'desc');
|
|
83
82
|
const limit = filter.limit ?? 0;
|
|
84
|
-
for (let index = 0; index < orderedEntries.length && collectedEntries.length < limit; index +=
|
|
85
|
-
const batch = orderedEntries.slice(index, index +
|
|
83
|
+
for (let index = 0; index < orderedEntries.length && collectedEntries.length < limit; index += batchSize) {
|
|
84
|
+
const batch = orderedEntries.slice(index, index + batchSize);
|
|
86
85
|
const resolvedEntries = await readManifestEntryBatch({
|
|
87
86
|
entryLocations: batch,
|
|
87
|
+
entryReadConcurrency,
|
|
88
88
|
workspaceRoot,
|
|
89
89
|
});
|
|
90
90
|
for (const resolvedEntry of resolvedEntries) {
|
|
@@ -99,21 +99,33 @@ const collectLimitedEntries = async ({ filter, manifest, readIndex, workspaceRoo
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
|
|
102
|
+
if (direction === 'asc') {
|
|
103
|
+
collectedEntries.reverse();
|
|
104
|
+
}
|
|
103
105
|
return collectedEntries;
|
|
104
106
|
};
|
|
105
|
-
export const iterateEntriesFromManifest = async function* (workspaceRoot, manifest, readIndex, filter = {}) {
|
|
107
|
+
export const iterateEntriesFromManifest = async function* (workspaceRoot, manifest, readIndex, filter = {}, direction = 'asc') {
|
|
108
|
+
const { scanBatchSize, scanConcurrency } = getLedgerRuntimeConfig();
|
|
106
109
|
if (filter.limit) {
|
|
107
|
-
for (const entry of await collectLimitedEntries({
|
|
110
|
+
for (const entry of await collectLimitedEntries({
|
|
111
|
+
batchSize: scanBatchSize,
|
|
112
|
+
direction,
|
|
113
|
+
entryReadConcurrency: scanConcurrency,
|
|
114
|
+
filter,
|
|
115
|
+
manifest,
|
|
116
|
+
readIndex,
|
|
117
|
+
workspaceRoot,
|
|
118
|
+
})) {
|
|
108
119
|
yield entry;
|
|
109
120
|
}
|
|
110
121
|
return;
|
|
111
122
|
}
|
|
112
|
-
const orderedEntries = getOrderedEntryLocations(manifest, readIndex, filter);
|
|
113
|
-
for (let index = 0; index < orderedEntries.length; index +=
|
|
114
|
-
const batch = orderedEntries.slice(index, index +
|
|
123
|
+
const orderedEntries = getOrderedEntryLocations(manifest, readIndex, filter, direction);
|
|
124
|
+
for (let index = 0; index < orderedEntries.length; index += scanBatchSize) {
|
|
125
|
+
const batch = orderedEntries.slice(index, index + scanBatchSize);
|
|
115
126
|
const resolvedEntries = await readManifestEntryBatch({
|
|
116
127
|
entryLocations: batch,
|
|
128
|
+
entryReadConcurrency: scanConcurrency,
|
|
117
129
|
workspaceRoot,
|
|
118
130
|
});
|
|
119
131
|
for (const resolvedEntry of resolvedEntries) {
|
package/dist/note.d.ts
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
import { appendRecord } from './record.ts';
|
|
2
2
|
import type { LedgerPhase } from './schema/entry.ts';
|
|
3
3
|
import type { NoteSubkind } from './schema/note.ts';
|
|
4
|
+
/** Input shared by the narrative and note helper wrappers. */
|
|
4
5
|
export type NoteBody = {
|
|
5
6
|
readonly body: string;
|
|
6
7
|
readonly phase: LedgerPhase;
|
|
7
8
|
readonly summary: string;
|
|
8
9
|
};
|
|
10
|
+
/** Append a typed note entry using the library emitter metadata. */
|
|
9
11
|
export declare const appendNote: (workspaceRoot: string, subkind: NoteSubkind, noteBody: NoteBody) => Promise<{
|
|
10
12
|
entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
|
|
11
13
|
id: string;
|
|
12
14
|
}>;
|
|
15
|
+
/** Append a `cleanup-wave` narrative note. */
|
|
13
16
|
export declare const appendCleanupWaveNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
|
|
14
17
|
entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
|
|
15
18
|
id: string;
|
|
16
19
|
}>;
|
|
20
|
+
/** Append a `verified-flow` narrative note. */
|
|
17
21
|
export declare const appendVerifiedFlowNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
|
|
18
22
|
entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
|
|
19
23
|
id: string;
|
|
20
24
|
}>;
|
|
25
|
+
/** Append an `open-issue` narrative note. */
|
|
21
26
|
export declare const appendOpenIssueNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
|
|
22
27
|
entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
|
|
23
28
|
id: string;
|
|
24
29
|
}>;
|
|
30
|
+
/** Append a `decomposition-wave` narrative note. */
|
|
25
31
|
export declare const appendDecompositionWaveNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
|
|
26
32
|
entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
|
|
27
33
|
id: string;
|
|
28
34
|
}>;
|
|
35
|
+
/** Append the current `semantic-cleanup-summary` note for workspace narrative rendering. */
|
|
29
36
|
export declare const appendSemanticCleanupSummaryNote: (workspaceRoot: string, noteBody: NoteBody) => Promise<{
|
|
30
37
|
entry: Awaited<ReturnType<typeof appendRecord>>["entry"];
|
|
31
38
|
id: string;
|
package/dist/note.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"note.d.ts","sourceRoot":"","sources":["../src/note.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,MAAM,QAAQ,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,UAAU,GACnB,eAAe,MAAM,EACrB,SAAS,WAAW,EACpB,UAAU,QAAQ,KACnB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAYlF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;
|
|
1
|
+
{"version":3,"file":"note.d.ts","sourceRoot":"","sources":["../src/note.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,8DAA8D;AAC9D,MAAM,MAAM,QAAQ,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,UAAU,GACnB,eAAe,MAAM,EACrB,SAAS,WAAW,EACpB,UAAU,QAAQ,KACnB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAYlF,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,qBAAqB,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WAf3D,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EAgB1B,CAAC;AAExD,+CAA+C;AAC/C,eAAO,MAAM,sBAAsB,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WAnB5D,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EAoBzB,CAAC;AAEzD,6CAA6C;AAC7C,eAAO,MAAM,mBAAmB,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WAvBzD,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EAwB5B,CAAC;AAEtD,oDAAoD;AACpD,eAAO,MAAM,2BAA2B,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WA3BjE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EA4BpB,CAAC;AAE9D,4FAA4F;AAC5F,eAAO,MAAM,gCAAgC,GAAI,eAAe,MAAM,EAAE,UAAU,QAAQ;WA/BtE,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;QAAM,MAAM;EAgCd,CAAC"}
|
package/dist/note.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { appendRecord } from "./record.js";
|
|
2
2
|
import { LEDGER_LIBRARY_VERSION } from "./version.js";
|
|
3
|
+
/** Append a typed note entry using the library emitter metadata. */
|
|
3
4
|
export const appendNote = async (workspaceRoot, subkind, noteBody) => {
|
|
4
5
|
return appendRecord(workspaceRoot, {
|
|
5
6
|
body: noteBody.body,
|
|
@@ -13,8 +14,13 @@ export const appendNote = async (workspaceRoot, subkind, noteBody) => {
|
|
|
13
14
|
summary: noteBody.summary,
|
|
14
15
|
});
|
|
15
16
|
};
|
|
17
|
+
/** Append a `cleanup-wave` narrative note. */
|
|
16
18
|
export const appendCleanupWaveNote = (workspaceRoot, noteBody) => appendNote(workspaceRoot, 'cleanup-wave', noteBody);
|
|
19
|
+
/** Append a `verified-flow` narrative note. */
|
|
17
20
|
export const appendVerifiedFlowNote = (workspaceRoot, noteBody) => appendNote(workspaceRoot, 'verified-flow', noteBody);
|
|
21
|
+
/** Append an `open-issue` narrative note. */
|
|
18
22
|
export const appendOpenIssueNote = (workspaceRoot, noteBody) => appendNote(workspaceRoot, 'open-issue', noteBody);
|
|
23
|
+
/** Append a `decomposition-wave` narrative note. */
|
|
19
24
|
export const appendDecompositionWaveNote = (workspaceRoot, noteBody) => appendNote(workspaceRoot, 'decomposition-wave', noteBody);
|
|
25
|
+
/** Append the current `semantic-cleanup-summary` note for workspace narrative rendering. */
|
|
20
26
|
export const appendSemanticCleanupSummaryNote = (workspaceRoot, noteBody) => appendNote(workspaceRoot, 'semantic-cleanup-summary', noteBody);
|
package/dist/patch-resolver.d.ts
CHANGED
|
@@ -14,6 +14,18 @@ type ResolvedOperatorPatchRecord = Omit<OperatorPatchRecord, 'diffPath' | 'diffT
|
|
|
14
14
|
readonly links: LedgerLinks;
|
|
15
15
|
};
|
|
16
16
|
export type ResolvedPatchRecord = ResolvedAgentPatchRecord | ResolvedOperatorPatchRecord;
|
|
17
|
+
/**
|
|
18
|
+
* Derive `change-log.filesChanged` metadata from a git-style unified diff.
|
|
19
|
+
*
|
|
20
|
+
* Example:
|
|
21
|
+
* `deriveFilesChangedFromPatch(await readFile('/tmp/change.patch', 'utf8'))`
|
|
22
|
+
*
|
|
23
|
+
* Known limits:
|
|
24
|
+
* - Requires `diff --git` headers to identify file boundaries.
|
|
25
|
+
* - Counts only textual hunk `+` and `-` lines, so binary or mode-only diffs can yield zero line counts.
|
|
26
|
+
* - Uses the post-image path when available and rejects diff blocks that cannot be mapped to a normalized
|
|
27
|
+
* workspace-relative path.
|
|
28
|
+
*/
|
|
17
29
|
export declare const deriveFilesChangedFromPatch: (patchText: string) => ChangeLogFileChange[];
|
|
18
30
|
export declare function resolvePatchRecord(args: {
|
|
19
31
|
readonly record: AgentPatchRecord;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patch-resolver.d.ts","sourceRoot":"","sources":["../src/patch-resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"patch-resolver.d.ts","sourceRoot":"","sources":["../src/patch-resolver.ts"],"names":[],"mappings":"AASA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,YAAY,EAEpB,MAAM,mBAAmB,CAAC;AAK3B,KAAK,gBAAgB,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;AACvE,KAAK,mBAAmB,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAAC,CAAC;AAQ7E,KAAK,wBAAwB,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,GAAG;IACxF,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC/B,CAAC;AACF,KAAK,2BAA2B,GAAG,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,GAAG;IAC9F,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GAAG,2BAA2B,CAAC;AAsTzF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,GAAI,WAAW,MAAM,KAAG,mBAAmB,EAuClF,CAAC;AAmBF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;AACtC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAClC,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC"}
|
package/dist/patch-resolver.js
CHANGED
|
@@ -1,28 +1,94 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import * as v from 'valibot';
|
|
4
|
-
import { resolveBlobPath, storePatchBlob } from "./blobs.js";
|
|
4
|
+
import { assertPatchTextWithinLimit, readPatchTextFromFile, resolveBlobPath, storePatchBlob, } from "./blobs.js";
|
|
5
5
|
import { WorkspaceRelativePathSchema, } from "./schema/entry.js";
|
|
6
|
+
import { forEachLine } from "./text-lines.js";
|
|
6
7
|
const readBlobText = async (workspaceRoot, blobSha256) => readFile(resolveBlobPath(workspaceRoot, blobSha256), 'utf8');
|
|
7
8
|
const formatRecordContext = (record) => `${record.kind} "${record.summary}" [${record.phase}]`;
|
|
8
9
|
const normalizeWorkspaceRelativePath = (value) => v.parse(WorkspaceRelativePathSchema, value.replaceAll('\\', '/'));
|
|
9
|
-
const
|
|
10
|
+
const normalizeDiffPathToken = ({ headerPrefixes, side, value, }) => {
|
|
10
11
|
const normalized = value.trim().replace(/^"([^"]*)"$/u, '$1');
|
|
11
12
|
if (normalized === '/dev/null') {
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const expectedPrefix = side === 'old' ? headerPrefixes?.oldPrefix : headerPrefixes?.newPrefix;
|
|
16
|
+
if (expectedPrefix && normalized.startsWith(expectedPrefix)) {
|
|
17
|
+
return normalized.slice(expectedPrefix.length);
|
|
16
18
|
}
|
|
17
19
|
return normalized;
|
|
18
20
|
};
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
21
|
+
const splitPrefixCandidate = (value) => {
|
|
22
|
+
const slashIndex = value.indexOf('/');
|
|
23
|
+
if (slashIndex <= 0) {
|
|
22
24
|
return null;
|
|
23
25
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
return {
|
|
27
|
+
prefix: value.slice(0, slashIndex + 1),
|
|
28
|
+
suffix: value.slice(slashIndex + 1),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const readDiffHeaderToken = (headerBody, startIndex) => {
|
|
32
|
+
let index = startIndex;
|
|
33
|
+
while (headerBody[index] === ' ') {
|
|
34
|
+
index += 1;
|
|
35
|
+
}
|
|
36
|
+
if (index >= headerBody.length) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (headerBody[index] === '"') {
|
|
40
|
+
const endIndex = headerBody.indexOf('"', index + 1);
|
|
41
|
+
if (endIndex === -1) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
nextIndex: endIndex + 1,
|
|
46
|
+
token: headerBody.slice(index + 1, endIndex),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
let endIndex = index;
|
|
50
|
+
while (endIndex < headerBody.length && headerBody[endIndex] !== ' ') {
|
|
51
|
+
endIndex += 1;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
nextIndex: endIndex + 1,
|
|
55
|
+
token: headerBody.slice(index, endIndex),
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const resolveHeaderPrefixes = (oldPath, newPath) => {
|
|
59
|
+
if (oldPath.startsWith('a/') && newPath.startsWith('b/')) {
|
|
60
|
+
return {
|
|
61
|
+
newPrefix: 'b/',
|
|
62
|
+
oldPrefix: 'a/',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const oldCandidate = splitPrefixCandidate(oldPath);
|
|
66
|
+
const newCandidate = splitPrefixCandidate(newPath);
|
|
67
|
+
if (!oldCandidate ||
|
|
68
|
+
!newCandidate ||
|
|
69
|
+
oldCandidate.prefix === newCandidate.prefix ||
|
|
70
|
+
oldCandidate.suffix !== newCandidate.suffix) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
newPrefix: newCandidate.prefix,
|
|
75
|
+
oldPrefix: oldCandidate.prefix,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
const parseDiffHeaderPaths = (line) => {
|
|
79
|
+
const headerBody = line.trim().slice('diff --git '.length);
|
|
80
|
+
const firstToken = readDiffHeaderToken(headerBody, 0);
|
|
81
|
+
const secondToken = firstToken ? readDiffHeaderToken(headerBody, firstToken.nextIndex) : null;
|
|
82
|
+
if (!firstToken?.token || !secondToken?.token) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const oldPath = firstToken.token;
|
|
86
|
+
const newPath = secondToken.token;
|
|
87
|
+
return {
|
|
88
|
+
headerPrefixes: resolveHeaderPrefixes(oldPath, newPath),
|
|
89
|
+
newPath,
|
|
90
|
+
oldPath,
|
|
91
|
+
};
|
|
26
92
|
};
|
|
27
93
|
const finalizeFileChange = (current, filesChanged) => {
|
|
28
94
|
if (!current) {
|
|
@@ -38,19 +104,33 @@ const finalizeFileChange = (current, filesChanged) => {
|
|
|
38
104
|
removed: current.removed > 0 ? current.removed : undefined,
|
|
39
105
|
});
|
|
40
106
|
};
|
|
41
|
-
const createDiffFileAccumulator = (line) =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
107
|
+
const createDiffFileAccumulator = (line) => {
|
|
108
|
+
const headerPaths = parseDiffHeaderPaths(line);
|
|
109
|
+
return {
|
|
110
|
+
added: 0,
|
|
111
|
+
headerPath: headerPaths
|
|
112
|
+
? normalizeDiffPathToken({
|
|
113
|
+
headerPrefixes: headerPaths.headerPrefixes,
|
|
114
|
+
side: 'new',
|
|
115
|
+
value: headerPaths.newPath,
|
|
116
|
+
})
|
|
117
|
+
: null,
|
|
118
|
+
headerPrefixes: headerPaths?.headerPrefixes ?? null,
|
|
119
|
+
minusPath: null,
|
|
120
|
+
plusPath: null,
|
|
121
|
+
removed: 0,
|
|
122
|
+
};
|
|
123
|
+
};
|
|
48
124
|
const applyPatchMetadataLine = (current, line) => {
|
|
49
125
|
if (line.startsWith('+++ ')) {
|
|
50
126
|
return {
|
|
51
127
|
next: {
|
|
52
128
|
...current,
|
|
53
|
-
plusPath:
|
|
129
|
+
plusPath: normalizeDiffPathToken({
|
|
130
|
+
headerPrefixes: current.headerPrefixes,
|
|
131
|
+
side: 'new',
|
|
132
|
+
value: line.slice(4),
|
|
133
|
+
}),
|
|
54
134
|
},
|
|
55
135
|
};
|
|
56
136
|
}
|
|
@@ -58,7 +138,35 @@ const applyPatchMetadataLine = (current, line) => {
|
|
|
58
138
|
return {
|
|
59
139
|
next: {
|
|
60
140
|
...current,
|
|
61
|
-
minusPath:
|
|
141
|
+
minusPath: normalizeDiffPathToken({
|
|
142
|
+
headerPrefixes: current.headerPrefixes,
|
|
143
|
+
side: 'old',
|
|
144
|
+
value: line.slice(4),
|
|
145
|
+
}),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (line.startsWith('rename from ')) {
|
|
150
|
+
return {
|
|
151
|
+
next: {
|
|
152
|
+
...current,
|
|
153
|
+
minusPath: normalizeDiffPathToken({
|
|
154
|
+
headerPrefixes: null,
|
|
155
|
+
side: 'old',
|
|
156
|
+
value: line.slice('rename from '.length),
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (line.startsWith('rename to ')) {
|
|
162
|
+
return {
|
|
163
|
+
next: {
|
|
164
|
+
...current,
|
|
165
|
+
plusPath: normalizeDiffPathToken({
|
|
166
|
+
headerPrefixes: null,
|
|
167
|
+
side: 'new',
|
|
168
|
+
value: line.slice('rename to '.length),
|
|
169
|
+
}),
|
|
62
170
|
},
|
|
63
171
|
};
|
|
64
172
|
}
|
|
@@ -74,16 +182,12 @@ const applyPatchMetadataLine = (current, line) => {
|
|
|
74
182
|
};
|
|
75
183
|
const applyPatchHunkLine = (current, line) => {
|
|
76
184
|
if (line.startsWith('+')) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
added: current.added + 1,
|
|
80
|
-
};
|
|
185
|
+
current.added += 1;
|
|
186
|
+
return current;
|
|
81
187
|
}
|
|
82
188
|
if (line.startsWith('-')) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
removed: current.removed + 1,
|
|
86
|
-
};
|
|
189
|
+
current.removed += 1;
|
|
190
|
+
return current;
|
|
87
191
|
}
|
|
88
192
|
return current;
|
|
89
193
|
};
|
|
@@ -91,71 +195,103 @@ const mergeBlobLink = (links, blobSha256) => ({
|
|
|
91
195
|
...links,
|
|
92
196
|
blobs: [...new Set([...(links?.blobs ?? []), blobSha256])],
|
|
93
197
|
});
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
198
|
+
const resolvePatchSourceFromFile = async (record, workspaceRoot) => {
|
|
199
|
+
const resolvedPath = path.resolve(workspaceRoot, record.diffPath);
|
|
200
|
+
try {
|
|
97
201
|
return {
|
|
98
|
-
patchText: await
|
|
202
|
+
patchText: await readPatchTextFromFile(resolvedPath),
|
|
99
203
|
source: 'file',
|
|
100
204
|
sourceLabel: resolvedPath,
|
|
101
205
|
};
|
|
102
206
|
}
|
|
103
|
-
|
|
207
|
+
catch (error) {
|
|
208
|
+
throw new Error(`Failed to read diff file ${resolvedPath} for ${formatRecordContext(record)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const resolvePatchSourceFromInlineText = (record) => {
|
|
212
|
+
try {
|
|
213
|
+
assertPatchTextWithinLimit(record.diffText, 'inline diff text');
|
|
104
214
|
return {
|
|
105
215
|
patchText: record.diffText,
|
|
106
216
|
source: 'inline',
|
|
107
217
|
sourceLabel: 'inline diff text',
|
|
108
218
|
};
|
|
109
219
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
220
|
+
catch (error) {
|
|
221
|
+
throw new Error(`Failed to validate inline diff text for ${formatRecordContext(record)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const resolvePatchSourceFromStoredBlob = async (record, workspaceRoot) => {
|
|
225
|
+
try {
|
|
226
|
+
return {
|
|
227
|
+
patchText: await readBlobText(workspaceRoot, record.diff?.blobSha256),
|
|
228
|
+
source: 'blob',
|
|
229
|
+
sourceLabel: `blob ${record.diff?.blobSha256}`,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
if (error.code === 'ENOENT') {
|
|
234
|
+
throw new Error(`Patch blob ${record.diff?.blobSha256} was not found for ${formatRecordContext(record)}. Store it first or use diffPath.`);
|
|
123
235
|
}
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
const resolvePatchSource = async ({ record, workspaceRoot, }) => {
|
|
240
|
+
if (record.diffPath) {
|
|
241
|
+
return resolvePatchSourceFromFile(record, workspaceRoot);
|
|
242
|
+
}
|
|
243
|
+
if (record.diffText) {
|
|
244
|
+
return resolvePatchSourceFromInlineText(record);
|
|
245
|
+
}
|
|
246
|
+
if (record.diff) {
|
|
247
|
+
return resolvePatchSourceFromStoredBlob(record, workspaceRoot);
|
|
124
248
|
}
|
|
125
249
|
throw new Error(`${record.kind} records require diff, diffPath, or diffText.`);
|
|
126
250
|
};
|
|
251
|
+
/**
|
|
252
|
+
* Derive `change-log.filesChanged` metadata from a git-style unified diff.
|
|
253
|
+
*
|
|
254
|
+
* Example:
|
|
255
|
+
* `deriveFilesChangedFromPatch(await readFile('/tmp/change.patch', 'utf8'))`
|
|
256
|
+
*
|
|
257
|
+
* Known limits:
|
|
258
|
+
* - Requires `diff --git` headers to identify file boundaries.
|
|
259
|
+
* - Counts only textual hunk `+` and `-` lines, so binary or mode-only diffs can yield zero line counts.
|
|
260
|
+
* - Uses the post-image path when available and rejects diff blocks that cannot be mapped to a normalized
|
|
261
|
+
* workspace-relative path.
|
|
262
|
+
*/
|
|
127
263
|
export const deriveFilesChangedFromPatch = (patchText) => {
|
|
128
264
|
const filesChanged = [];
|
|
129
265
|
let current;
|
|
130
266
|
let insideHunk = false;
|
|
131
|
-
|
|
267
|
+
forEachLine(patchText, (line) => {
|
|
132
268
|
if (line.startsWith('diff --git ')) {
|
|
133
269
|
finalizeFileChange(current, filesChanged);
|
|
134
270
|
current = createDiffFileAccumulator(line);
|
|
135
271
|
insideHunk = false;
|
|
136
|
-
|
|
272
|
+
return;
|
|
137
273
|
}
|
|
138
274
|
if (!current) {
|
|
139
|
-
|
|
275
|
+
return;
|
|
140
276
|
}
|
|
141
277
|
if (insideHunk) {
|
|
142
278
|
if (line.startsWith('@@')) {
|
|
143
|
-
|
|
279
|
+
return;
|
|
144
280
|
}
|
|
145
281
|
current = applyPatchHunkLine(current, line);
|
|
146
|
-
|
|
282
|
+
return;
|
|
147
283
|
}
|
|
148
284
|
const metadataUpdate = applyPatchMetadataLine(current, line);
|
|
149
285
|
current = metadataUpdate.next;
|
|
150
286
|
if (metadataUpdate.startHunk) {
|
|
151
287
|
insideHunk = true;
|
|
152
|
-
|
|
288
|
+
return;
|
|
153
289
|
}
|
|
154
290
|
if (!insideHunk) {
|
|
155
|
-
|
|
291
|
+
return;
|
|
156
292
|
}
|
|
157
293
|
current = applyPatchHunkLine(current, line);
|
|
158
|
-
}
|
|
294
|
+
});
|
|
159
295
|
finalizeFileChange(current, filesChanged);
|
|
160
296
|
return filesChanged;
|
|
161
297
|
};
|
|
@@ -168,6 +304,22 @@ const buildResolvedPatchRecord = ({ links, record, storedDiff, }) => {
|
|
|
168
304
|
};
|
|
169
305
|
};
|
|
170
306
|
export async function resolvePatchRecord({ record, workspaceRoot, }) {
|
|
307
|
+
if (record.diff && !record.diffPath && !record.diffText) {
|
|
308
|
+
try {
|
|
309
|
+
await stat(resolveBlobPath(workspaceRoot, record.diff.blobSha256));
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
if (error.code === 'ENOENT') {
|
|
313
|
+
throw new Error(`Patch blob ${record.diff.blobSha256} was not found for ${formatRecordContext(record)}. Store it first or use diffPath.`);
|
|
314
|
+
}
|
|
315
|
+
throw new Error(`Failed to read patch blob ${record.diff.blobSha256} for ${formatRecordContext(record)}: ${error instanceof Error ? error.message : String(error)}`);
|
|
316
|
+
}
|
|
317
|
+
return buildResolvedPatchRecord({
|
|
318
|
+
links: mergeBlobLink(record.links, record.diff.blobSha256),
|
|
319
|
+
record,
|
|
320
|
+
storedDiff: record.diff,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
171
323
|
const { patchText, sourceLabel } = await resolvePatchSource({
|
|
172
324
|
record,
|
|
173
325
|
workspaceRoot,
|