@astralyn/pi 0.84.0 → 0.84.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/CHANGELOG.md +30 -1
- package/dist/cli/args.js +3 -3
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-check.d.ts +19 -0
- package/dist/cli/auth-check.d.ts.map +1 -0
- package/dist/cli/auth-check.js +50 -0
- package/dist/cli/auth-check.js.map +1 -0
- package/dist/cli/auth-command.d.ts +24 -0
- package/dist/cli/auth-command.d.ts.map +1 -0
- package/dist/cli/auth-command.js +104 -0
- package/dist/cli/auth-command.js.map +1 -0
- package/dist/cli/credential-print.d.ts +4 -14
- package/dist/cli/credential-print.d.ts.map +1 -1
- package/dist/cli/credential-print.js +38 -79
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/core/auth-storage.d.ts +10 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +76 -1
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/extensions/types.d.ts +5 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts +8 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +3 -1
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/tools/bash.d.ts +4 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +6 -4
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts +4 -0
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +11 -7
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +4 -0
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +5 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +4 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +5 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts +4 -0
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +4 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +6 -2
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +4 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +6 -2
- package/dist/core/tools/write.js.map +1 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +0 -4
- package/dist/extensions/index.js.map +1 -1
- package/dist/extensions/todo/schema.d.ts +1 -1
- package/dist/extensions/todo/state.js +5 -5
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +57 -21
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +48 -41
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +11 -3
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/server/create-harness.d.ts +26 -0
- package/dist/server/create-harness.d.ts.map +1 -0
- package/dist/server/create-harness.js +104 -0
- package/dist/server/create-harness.js.map +1 -0
- package/dist/utils/coalescing-terminal.d.ts +19 -4
- package/dist/utils/coalescing-terminal.d.ts.map +1 -1
- package/dist/utils/coalescing-terminal.js +33 -5
- package/dist/utils/coalescing-terminal.js.map +1 -1
- package/docs/bundled/README.md +0 -2
- package/docs/docs.json +0 -8
- package/docs/extensions.md +3 -2
- package/docs/keybindings.md +9 -5
- package/docs/providers.md +8 -1
- package/docs/usage.md +0 -1
- package/examples/extensions/README.md +2 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +5 -5
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/plan-mode/README.md +66 -0
- package/examples/extensions/plan-mode/index.ts +390 -0
- package/examples/extensions/plan-mode/utils.ts +168 -0
- package/examples/extensions/sandbox/package-lock.json +3 -3
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +175 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +126 -0
- package/examples/extensions/subagent/index.ts +1009 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/examples/extensions/with-deps/package-lock.json +4 -4
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +26 -26
- package/package.json +6 -6
- package/dist/extensions/biu/index.d.ts +0 -19
- package/dist/extensions/biu/index.d.ts.map +0 -1
- package/dist/extensions/biu/index.js +0 -350
- package/dist/extensions/biu/index.js.map +0 -1
- package/dist/extensions/biu/prompts.d.ts +0 -26
- package/dist/extensions/biu/prompts.d.ts.map +0 -1
- package/dist/extensions/biu/prompts.js +0 -260
- package/dist/extensions/biu/prompts.js.map +0 -1
- package/dist/extensions/biu/workspace.d.ts +0 -122
- package/dist/extensions/biu/workspace.d.ts.map +0 -1
- package/dist/extensions/biu/workspace.js +0 -404
- package/dist/extensions/biu/workspace.js.map +0 -1
- package/dist/extensions/rewind/anchor.d.ts +0 -29
- package/dist/extensions/rewind/anchor.d.ts.map +0 -1
- package/dist/extensions/rewind/anchor.js +0 -35
- package/dist/extensions/rewind/anchor.js.map +0 -1
- package/dist/extensions/rewind/config.d.ts +0 -16
- package/dist/extensions/rewind/config.d.ts.map +0 -1
- package/dist/extensions/rewind/config.js +0 -69
- package/dist/extensions/rewind/config.js.map +0 -1
- package/dist/extensions/rewind/engine.d.ts +0 -93
- package/dist/extensions/rewind/engine.d.ts.map +0 -1
- package/dist/extensions/rewind/engine.js +0 -778
- package/dist/extensions/rewind/engine.js.map +0 -1
- package/dist/extensions/rewind/gc.d.ts +0 -35
- package/dist/extensions/rewind/gc.d.ts.map +0 -1
- package/dist/extensions/rewind/gc.js +0 -169
- package/dist/extensions/rewind/gc.js.map +0 -1
- package/dist/extensions/rewind/index.d.ts +0 -3
- package/dist/extensions/rewind/index.d.ts.map +0 -1
- package/dist/extensions/rewind/index.js +0 -287
- package/dist/extensions/rewind/index.js.map +0 -1
- package/dist/extensions/rewind/menu.d.ts +0 -13
- package/dist/extensions/rewind/menu.d.ts.map +0 -1
- package/dist/extensions/rewind/menu.js +0 -123
- package/dist/extensions/rewind/menu.js.map +0 -1
- package/dist/extensions/rewind/paths.d.ts +0 -4
- package/dist/extensions/rewind/paths.d.ts.map +0 -1
- package/dist/extensions/rewind/paths.js +0 -15
- package/dist/extensions/rewind/paths.js.map +0 -1
- package/dist/extensions/rewind/restore.d.ts +0 -54
- package/dist/extensions/rewind/restore.d.ts.map +0 -1
- package/dist/extensions/rewind/restore.js +0 -94
- package/dist/extensions/rewind/restore.js.map +0 -1
- package/dist/extensions/rewind/snapshot.d.ts +0 -45
- package/dist/extensions/rewind/snapshot.d.ts.map +0 -1
- package/dist/extensions/rewind/snapshot.js +0 -21
- package/dist/extensions/rewind/snapshot.js.map +0 -1
- package/dist/extensions/rewind/storage.d.ts +0 -12
- package/dist/extensions/rewind/storage.d.ts.map +0 -1
- package/dist/extensions/rewind/storage.js +0 -33
- package/dist/extensions/rewind/storage.js.map +0 -1
- package/dist/extensions/rewind/text.d.ts +0 -7
- package/dist/extensions/rewind/text.d.ts.map +0 -1
- package/dist/extensions/rewind/text.js +0 -19
- package/dist/extensions/rewind/text.js.map +0 -1
- package/dist/extensions/rewind/tool-path.d.ts +0 -3
- package/dist/extensions/rewind/tool-path.d.ts.map +0 -1
- package/dist/extensions/rewind/tool-path.js +0 -9
- package/dist/extensions/rewind/tool-path.js.map +0 -1
- package/docs/bundled/extensions/biu.md +0 -63
- package/docs/bundled/extensions/rewind.md +0 -86
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* restore.ts — map a /tree navigation target to the snapshot that should be
|
|
3
|
-
* restored and apply it via the engine.
|
|
4
|
-
*
|
|
5
|
-
* Snapshot semantics: a frame anchored at user entry U records the work tree
|
|
6
|
-
* at the moment U's run STARTED (see engine.ts beginTurn). The selection here
|
|
7
|
-
* must mirror AgentSession.navigateTree's leaf rules:
|
|
8
|
-
*
|
|
9
|
-
* - user / custom_message target: navigateTree removes the target's turn
|
|
10
|
-
* from the branch (leaf = parent, text returns to the editor) → restore
|
|
11
|
-
* to the state BEFORE that turn = the snapshot anchored at the target.
|
|
12
|
-
* - any other target: the target's turn STAYS in the conversation
|
|
13
|
-
* (leaf = target) → restore to the state AFTER that turn = the first
|
|
14
|
-
* chronological snapshot anchored at a descendant turn (it recorded the
|
|
15
|
-
* work tree when the next run began). No such snapshot → nothing was
|
|
16
|
-
* recorded after the target, so offer no restore at all — falling back to
|
|
17
|
-
* an ancestor frame would roll back the target turn's own edits and leave
|
|
18
|
-
* the work tree older than the conversation.
|
|
19
|
-
*/
|
|
20
|
-
import { applySnapshot, collectChangeDiffStats, collectChanges, } from "./engine.js";
|
|
21
|
-
/** Cycle guard for parent-chain walks (ids are acyclic in practice). */
|
|
22
|
-
const ANCESTOR_WALK_GUARD = 100_000;
|
|
23
|
-
/** True when `id`'s parent chain passes through `ancestorId` (strict descent). */
|
|
24
|
-
function isDescendantOf(view, id, ancestorId) {
|
|
25
|
-
let cur = view.getEntry(id)?.parentId ?? null;
|
|
26
|
-
let guard = 0;
|
|
27
|
-
while (cur !== null && guard++ < ANCESTOR_WALK_GUARD) {
|
|
28
|
-
if (cur === ancestorId)
|
|
29
|
-
return true;
|
|
30
|
-
cur = view.getEntry(cur)?.parentId ?? null;
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* First (chronological) snapshot anchored at a strict descendant of `entryId`
|
|
36
|
-
* — the run that started right after `entryId` existed, so its frame records
|
|
37
|
-
* the work tree state "just after" the entry.
|
|
38
|
-
*/
|
|
39
|
-
function snapshotAfterEntry(snapshots, view, entryId) {
|
|
40
|
-
for (const snap of snapshots) {
|
|
41
|
-
if (!snap.userEntryId)
|
|
42
|
-
continue;
|
|
43
|
-
if (isDescendantOf(view, snap.userEntryId, entryId))
|
|
44
|
-
return snap;
|
|
45
|
-
}
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* The snapshot that matches navigating to `targetId`, per the rules in the
|
|
50
|
-
* module header. Undefined when no recorded state corresponds to the target
|
|
51
|
-
* (then the work tree should be left alone).
|
|
52
|
-
*/
|
|
53
|
-
export function snapshotForEntry(snapshots, view, targetId) {
|
|
54
|
-
const target = view.getEntry(targetId);
|
|
55
|
-
if (!target)
|
|
56
|
-
return undefined;
|
|
57
|
-
if (target.isTurnAnchor) {
|
|
58
|
-
// Exact anchor match: the frame recording the state right before this
|
|
59
|
-
// turn ran. Chronologically last wins if duplicate anchors exist.
|
|
60
|
-
let exact;
|
|
61
|
-
for (const snap of snapshots) {
|
|
62
|
-
if (snap.userEntryId === targetId)
|
|
63
|
-
exact = snap;
|
|
64
|
-
}
|
|
65
|
-
if (exact)
|
|
66
|
-
return exact;
|
|
67
|
-
// No frame for this turn (it recorded no changes): the state before it
|
|
68
|
-
// equals the state after its parent.
|
|
69
|
-
return target.parentId === null ? undefined : snapshotAfterEntry(snapshots, view, target.parentId);
|
|
70
|
-
}
|
|
71
|
-
return snapshotAfterEntry(snapshots, view, targetId);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Absolute file paths restoring to this snapshot would change on disk (empty =
|
|
75
|
-
* none). Callers use the length for the count and the paths for the preview.
|
|
76
|
-
*/
|
|
77
|
-
export function snapshotChangedPaths(sessionId, snapshot) {
|
|
78
|
-
return collectChanges(sessionId, snapshot);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Coarse +N/−M line stats for the confirm dialog (paths must already be known
|
|
82
|
-
* changed — typically from snapshotChangedPaths).
|
|
83
|
-
*/
|
|
84
|
-
export function snapshotChangeDiffStats(sessionId, snapshot, changedPaths) {
|
|
85
|
-
return collectChangeDiffStats(sessionId, snapshot, changedPaths);
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Restore the work tree to `snapshot` and return changed absolute paths.
|
|
89
|
-
* Pass `onlyPaths` from a prior snapshotChangedPaths call to skip re-compare.
|
|
90
|
-
*/
|
|
91
|
-
export async function restoreToSnapshot(sessionId, snapshot, opts) {
|
|
92
|
-
return applySnapshot(sessionId, snapshot, opts);
|
|
93
|
-
}
|
|
94
|
-
//# sourceMappingURL=restore.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../src/extensions/rewind/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAEN,aAAa,EAEb,sBAAsB,EACtB,cAAc,GACd,MAAM,aAAa,CAAC;AAgBrB,wEAAwE;AACxE,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAEpC,kFAAkF;AAClF,SAAS,cAAc,CAAC,IAAmB,EAAE,EAAU,EAAE,UAAkB;IAC1E,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,KAAK,IAAI,IAAI,KAAK,EAAE,GAAG,mBAAmB,EAAE,CAAC;QACtD,IAAI,GAAG,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC;QACpC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC5C,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAC1B,SAAgC,EAChC,IAAmB,EACnB,OAAe;IAEf,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,SAAS;QAChC,IAAI,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC/B,SAAgC,EAChC,IAAmB,EACnB,QAAgB;IAEhB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,sEAAsE;QACtE,kEAAkE;QAClE,IAAI,KAAsC,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,WAAW,KAAK,QAAQ;gBAAE,KAAK,GAAG,IAAI,CAAC;QACjD,CAAC;QACD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,uEAAuE;QACvE,qCAAqC;QACrC,OAAO,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACpG,CAAC;IAED,OAAO,kBAAkB,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,QAA6B;IACpF,OAAO,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACtC,SAAiB,EACjB,QAA6B,EAC7B,YAA+B;IAE/B,OAAO,sBAAsB,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,SAAiB,EACjB,QAA6B,EAC7B,IAA2B;IAE3B,OAAO,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC","sourcesContent":["/**\n * restore.ts — map a /tree navigation target to the snapshot that should be\n * restored and apply it via the engine.\n *\n * Snapshot semantics: a frame anchored at user entry U records the work tree\n * at the moment U's run STARTED (see engine.ts beginTurn). The selection here\n * must mirror AgentSession.navigateTree's leaf rules:\n *\n * - user / custom_message target: navigateTree removes the target's turn\n * from the branch (leaf = parent, text returns to the editor) → restore\n * to the state BEFORE that turn = the snapshot anchored at the target.\n * - any other target: the target's turn STAYS in the conversation\n * (leaf = target) → restore to the state AFTER that turn = the first\n * chronological snapshot anchored at a descendant turn (it recorded the\n * work tree when the next run began). No such snapshot → nothing was\n * recorded after the target, so offer no restore at all — falling back to\n * an ancestor frame would roll back the target turn's own edits and leave\n * the work tree older than the conversation.\n */\nimport {\n\ttype ApplySnapshotOptions,\n\tapplySnapshot,\n\ttype CoarseDiffStats,\n\tcollectChangeDiffStats,\n\tcollectChanges,\n} from \"./engine.ts\";\nimport type { FileHistorySnapshot } from \"./snapshot.ts\";\n\n/** Minimal per-entry view needed to walk the tree and classify targets. */\nexport interface EntryView {\n\tid: string;\n\tparentId: string | null;\n\t/** True for entries navigateTree treats as turn starts (user / custom_message → leaf = parent). */\n\tisTurnAnchor: boolean;\n}\n\n/** Minimal session view we need to walk the entry tree. */\nexport interface EntryTreeView {\n\tgetEntry(id: string): EntryView | undefined;\n}\n\n/** Cycle guard for parent-chain walks (ids are acyclic in practice). */\nconst ANCESTOR_WALK_GUARD = 100_000;\n\n/** True when `id`'s parent chain passes through `ancestorId` (strict descent). */\nfunction isDescendantOf(view: EntryTreeView, id: string, ancestorId: string): boolean {\n\tlet cur = view.getEntry(id)?.parentId ?? null;\n\tlet guard = 0;\n\twhile (cur !== null && guard++ < ANCESTOR_WALK_GUARD) {\n\t\tif (cur === ancestorId) return true;\n\t\tcur = view.getEntry(cur)?.parentId ?? null;\n\t}\n\treturn false;\n}\n\n/**\n * First (chronological) snapshot anchored at a strict descendant of `entryId`\n * — the run that started right after `entryId` existed, so its frame records\n * the work tree state \"just after\" the entry.\n */\nfunction snapshotAfterEntry(\n\tsnapshots: FileHistorySnapshot[],\n\tview: EntryTreeView,\n\tentryId: string,\n): FileHistorySnapshot | undefined {\n\tfor (const snap of snapshots) {\n\t\tif (!snap.userEntryId) continue;\n\t\tif (isDescendantOf(view, snap.userEntryId, entryId)) return snap;\n\t}\n\treturn undefined;\n}\n\n/**\n * The snapshot that matches navigating to `targetId`, per the rules in the\n * module header. Undefined when no recorded state corresponds to the target\n * (then the work tree should be left alone).\n */\nexport function snapshotForEntry(\n\tsnapshots: FileHistorySnapshot[],\n\tview: EntryTreeView,\n\ttargetId: string,\n): FileHistorySnapshot | undefined {\n\tconst target = view.getEntry(targetId);\n\tif (!target) return undefined;\n\n\tif (target.isTurnAnchor) {\n\t\t// Exact anchor match: the frame recording the state right before this\n\t\t// turn ran. Chronologically last wins if duplicate anchors exist.\n\t\tlet exact: FileHistorySnapshot | undefined;\n\t\tfor (const snap of snapshots) {\n\t\t\tif (snap.userEntryId === targetId) exact = snap;\n\t\t}\n\t\tif (exact) return exact;\n\t\t// No frame for this turn (it recorded no changes): the state before it\n\t\t// equals the state after its parent.\n\t\treturn target.parentId === null ? undefined : snapshotAfterEntry(snapshots, view, target.parentId);\n\t}\n\n\treturn snapshotAfterEntry(snapshots, view, targetId);\n}\n\n/**\n * Absolute file paths restoring to this snapshot would change on disk (empty =\n * none). Callers use the length for the count and the paths for the preview.\n */\nexport function snapshotChangedPaths(sessionId: string, snapshot: FileHistorySnapshot): Promise<string[]> {\n\treturn collectChanges(sessionId, snapshot);\n}\n\n/**\n * Coarse +N/−M line stats for the confirm dialog (paths must already be known\n * changed — typically from snapshotChangedPaths).\n */\nexport function snapshotChangeDiffStats(\n\tsessionId: string,\n\tsnapshot: FileHistorySnapshot,\n\tchangedPaths: readonly string[],\n): Promise<CoarseDiffStats> {\n\treturn collectChangeDiffStats(sessionId, snapshot, changedPaths);\n}\n\n/**\n * Restore the work tree to `snapshot` and return changed absolute paths.\n * Pass `onlyPaths` from a prior snapshotChangedPaths call to skip re-compare.\n */\nexport async function restoreToSnapshot(\n\tsessionId: string,\n\tsnapshot: FileHistorySnapshot,\n\topts?: ApplySnapshotOptions,\n): Promise<string[]> {\n\treturn applySnapshot(sessionId, snapshot, opts);\n}\n"]}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* snapshot.ts — persisted data shapes for the rewind extension's file-history
|
|
3
|
-
* engine. Pure types + tiny helpers; no Pi imports, no side effects, so the
|
|
4
|
-
* engine and its selftests can import them under plain node.
|
|
5
|
-
*
|
|
6
|
-
* A FileHistorySnapshot is written to the session JSONL (via the integration
|
|
7
|
-
* layer's appendEntry, custom type "pi-rewind-snapshot"). It is never sent to
|
|
8
|
-
* the LLM and survives reload/compaction. Each snapshot anchors to one turn and
|
|
9
|
-
* records, for every tracked file, which backup blob holds that file's contents
|
|
10
|
-
* as of the start of the turn.
|
|
11
|
-
*/
|
|
12
|
-
/** Custom session-entry type used to persist snapshots to the session JSONL. */
|
|
13
|
-
export declare const SNAPSHOT_ENTRY_TYPE = "pi-rewind-snapshot";
|
|
14
|
-
/**
|
|
15
|
-
* One file's backup at a specific version.
|
|
16
|
-
* `backupName === null` means the file did not exist at this version — rewinding
|
|
17
|
-
* to a snapshot carrying a null backup deletes the file.
|
|
18
|
-
*/
|
|
19
|
-
export interface FileBackup {
|
|
20
|
-
/** Backup blob filename (`<sha256(relpath)[:16]>@v<n>`), or null if the file did not exist. */
|
|
21
|
-
backupName: string | null;
|
|
22
|
-
/** Monotonic version for this file within the session (1-based). */
|
|
23
|
-
version: number;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* One turn's snapshot. `v` lets future readers migrate old entries.
|
|
27
|
-
* `trackedFileBackups` is keyed by the file's path *relative to cwd* when inside
|
|
28
|
-
* cwd (else the absolute path), matching the engine's path shortening.
|
|
29
|
-
*/
|
|
30
|
-
export interface FileHistorySnapshot {
|
|
31
|
-
v: 1;
|
|
32
|
-
/** Session entry id this snapshot anchors to (used to match /tree navigation). */
|
|
33
|
-
userEntryId: string;
|
|
34
|
-
/** Leaf/turn id when finalized (informational). */
|
|
35
|
-
turnId: string;
|
|
36
|
-
/** The user prompt that drove the turn (truncated; shown in the /tree sync prompt). */
|
|
37
|
-
prompt: string;
|
|
38
|
-
/** Map of tracking-path → the backup recorded for that file at the start of this turn. */
|
|
39
|
-
trackedFileBackups: Record<string, FileBackup>;
|
|
40
|
-
/** ISO timestamp the snapshot was finalized. */
|
|
41
|
-
timestamp: string;
|
|
42
|
-
}
|
|
43
|
-
/** Narrow an arbitrary session-entry payload to a FileHistorySnapshot. */
|
|
44
|
-
export declare function isSnapshot(data: unknown): data is FileHistorySnapshot;
|
|
45
|
-
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,+FAA+F;IAC/F,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC,CAAC,EAAE,CAAC,CAAC;IACL,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,mBAAmB,CAMrE"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* snapshot.ts — persisted data shapes for the rewind extension's file-history
|
|
3
|
-
* engine. Pure types + tiny helpers; no Pi imports, no side effects, so the
|
|
4
|
-
* engine and its selftests can import them under plain node.
|
|
5
|
-
*
|
|
6
|
-
* A FileHistorySnapshot is written to the session JSONL (via the integration
|
|
7
|
-
* layer's appendEntry, custom type "pi-rewind-snapshot"). It is never sent to
|
|
8
|
-
* the LLM and survives reload/compaction. Each snapshot anchors to one turn and
|
|
9
|
-
* records, for every tracked file, which backup blob holds that file's contents
|
|
10
|
-
* as of the start of the turn.
|
|
11
|
-
*/
|
|
12
|
-
/** Custom session-entry type used to persist snapshots to the session JSONL. */
|
|
13
|
-
export const SNAPSHOT_ENTRY_TYPE = "pi-rewind-snapshot";
|
|
14
|
-
/** Narrow an arbitrary session-entry payload to a FileHistorySnapshot. */
|
|
15
|
-
export function isSnapshot(data) {
|
|
16
|
-
if (!data || typeof data !== "object")
|
|
17
|
-
return false;
|
|
18
|
-
const s = data;
|
|
19
|
-
return (typeof s.userEntryId === "string" && typeof s.trackedFileBackups === "object" && s.trackedFileBackups !== null);
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=snapshot.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/extensions/rewind/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AAiCxD,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,IAAa;IACvC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,CAAC,GAAG,IAAoC,CAAC;IAC/C,OAAO,CACN,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,kBAAkB,KAAK,QAAQ,IAAI,CAAC,CAAC,kBAAkB,KAAK,IAAI,CAC9G,CAAC;AACH,CAAC","sourcesContent":["/**\n * snapshot.ts — persisted data shapes for the rewind extension's file-history\n * engine. Pure types + tiny helpers; no Pi imports, no side effects, so the\n * engine and its selftests can import them under plain node.\n *\n * A FileHistorySnapshot is written to the session JSONL (via the integration\n * layer's appendEntry, custom type \"pi-rewind-snapshot\"). It is never sent to\n * the LLM and survives reload/compaction. Each snapshot anchors to one turn and\n * records, for every tracked file, which backup blob holds that file's contents\n * as of the start of the turn.\n */\n\n/** Custom session-entry type used to persist snapshots to the session JSONL. */\nexport const SNAPSHOT_ENTRY_TYPE = \"pi-rewind-snapshot\";\n\n/**\n * One file's backup at a specific version.\n * `backupName === null` means the file did not exist at this version — rewinding\n * to a snapshot carrying a null backup deletes the file.\n */\nexport interface FileBackup {\n\t/** Backup blob filename (`<sha256(relpath)[:16]>@v<n>`), or null if the file did not exist. */\n\tbackupName: string | null;\n\t/** Monotonic version for this file within the session (1-based). */\n\tversion: number;\n}\n\n/**\n * One turn's snapshot. `v` lets future readers migrate old entries.\n * `trackedFileBackups` is keyed by the file's path *relative to cwd* when inside\n * cwd (else the absolute path), matching the engine's path shortening.\n */\nexport interface FileHistorySnapshot {\n\tv: 1;\n\t/** Session entry id this snapshot anchors to (used to match /tree navigation). */\n\tuserEntryId: string;\n\t/** Leaf/turn id when finalized (informational). */\n\tturnId: string;\n\t/** The user prompt that drove the turn (truncated; shown in the /tree sync prompt). */\n\tprompt: string;\n\t/** Map of tracking-path → the backup recorded for that file at the start of this turn. */\n\ttrackedFileBackups: Record<string, FileBackup>;\n\t/** ISO timestamp the snapshot was finalized. */\n\ttimestamp: string;\n}\n\n/** Narrow an arbitrary session-entry payload to a FileHistorySnapshot. */\nexport function isSnapshot(data: unknown): data is FileHistorySnapshot {\n\tif (!data || typeof data !== \"object\") return false;\n\tconst s = data as Partial<FileHistorySnapshot>;\n\treturn (\n\t\ttypeof s.userEntryId === \"string\" && typeof s.trackedFileBackups === \"object\" && s.trackedFileBackups !== null\n\t);\n}\n"]}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/** Bind the on-disk roots. Called once by index.ts (or by selftests). */
|
|
2
|
-
export declare function configureStorage(opts: {
|
|
3
|
-
backupsRoot: string;
|
|
4
|
-
sessionsRoot: string;
|
|
5
|
-
}): void;
|
|
6
|
-
/** Root holding every session's backup directory. */
|
|
7
|
-
export declare function backupsRootDir(): string;
|
|
8
|
-
/** A single session's backup directory. */
|
|
9
|
-
export declare function backupsDir(sessionId: string): string;
|
|
10
|
-
/** Root holding Pi's session JSONL files (for orphan detection). */
|
|
11
|
-
export declare function sessionsRootDir(): string;
|
|
12
|
-
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/storage.ts"],"names":[],"mappings":"AAiBA,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG1F;AAED,qDAAqD;AACrD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,oEAAoE;AACpE,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* storage.ts — injected storage roots for the rewind engine.
|
|
3
|
-
*
|
|
4
|
-
* engine.ts and gc.ts must stay loadable under plain node (for offline
|
|
5
|
-
* selftests), so they cannot statically import paths.ts (which imports
|
|
6
|
-
* getAgentDir from @astralyn/pi — unresolvable outside Pi).
|
|
7
|
-
* Instead they read the roots from here, and the integration layer (index.ts)
|
|
8
|
-
* calls configureStorage() with the real paths at startup. Selftests call it with
|
|
9
|
-
* temp directories.
|
|
10
|
-
*
|
|
11
|
-
* No Pi imports in this module.
|
|
12
|
-
*/
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
let backupsRoot = "";
|
|
15
|
-
let sessionsRoot = "";
|
|
16
|
-
/** Bind the on-disk roots. Called once by index.ts (or by selftests). */
|
|
17
|
-
export function configureStorage(opts) {
|
|
18
|
-
backupsRoot = opts.backupsRoot;
|
|
19
|
-
sessionsRoot = opts.sessionsRoot;
|
|
20
|
-
}
|
|
21
|
-
/** Root holding every session's backup directory. */
|
|
22
|
-
export function backupsRootDir() {
|
|
23
|
-
return backupsRoot;
|
|
24
|
-
}
|
|
25
|
-
/** A single session's backup directory. */
|
|
26
|
-
export function backupsDir(sessionId) {
|
|
27
|
-
return join(backupsRoot, sessionId);
|
|
28
|
-
}
|
|
29
|
-
/** Root holding Pi's session JSONL files (for orphan detection). */
|
|
30
|
-
export function sessionsRootDir() {
|
|
31
|
-
return sessionsRoot;
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=storage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/extensions/rewind/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,IAAI,WAAW,GAAG,EAAE,CAAC;AACrB,IAAI,YAAY,GAAG,EAAE,CAAC;AAEtB,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,IAAmD;IACnF,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAC/B,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;AAClC,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,cAAc;IAC7B,OAAO,WAAW,CAAC;AACpB,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC3C,OAAO,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AACrC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe;IAC9B,OAAO,YAAY,CAAC;AACrB,CAAC","sourcesContent":["/**\n * storage.ts — injected storage roots for the rewind engine.\n *\n * engine.ts and gc.ts must stay loadable under plain node (for offline\n * selftests), so they cannot statically import paths.ts (which imports\n * getAgentDir from @astralyn/pi — unresolvable outside Pi).\n * Instead they read the roots from here, and the integration layer (index.ts)\n * calls configureStorage() with the real paths at startup. Selftests call it with\n * temp directories.\n *\n * No Pi imports in this module.\n */\nimport { join } from \"node:path\";\n\nlet backupsRoot = \"\";\nlet sessionsRoot = \"\";\n\n/** Bind the on-disk roots. Called once by index.ts (or by selftests). */\nexport function configureStorage(opts: { backupsRoot: string; sessionsRoot: string }): void {\n\tbackupsRoot = opts.backupsRoot;\n\tsessionsRoot = opts.sessionsRoot;\n}\n\n/** Root holding every session's backup directory. */\nexport function backupsRootDir(): string {\n\treturn backupsRoot;\n}\n\n/** A single session's backup directory. */\nexport function backupsDir(sessionId: string): string {\n\treturn join(backupsRoot, sessionId);\n}\n\n/** Root holding Pi's session JSONL files (for orphan detection). */\nexport function sessionsRootDir(): string {\n\treturn sessionsRoot;\n}\n"]}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export interface TruncateTextOptions {
|
|
2
|
-
ellipsis?: string;
|
|
3
|
-
collapseWhitespace?: boolean;
|
|
4
|
-
}
|
|
5
|
-
export declare function truncateText(text: string, maxLength: number, options?: TruncateTextOptions): string;
|
|
6
|
-
export declare function formatSize(bytes: number): string;
|
|
7
|
-
//# sourceMappingURL=text.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/text.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAMvG;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhD"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export function truncateText(text, maxLength, options = {}) {
|
|
2
|
-
const ellipsis = options.ellipsis ?? "...";
|
|
3
|
-
const source = options.collapseWhitespace ? text.replace(/\s+/g, " ").trim() : text;
|
|
4
|
-
if (source.length <= maxLength)
|
|
5
|
-
return source;
|
|
6
|
-
if (ellipsis.length >= maxLength)
|
|
7
|
-
return ellipsis.slice(0, maxLength);
|
|
8
|
-
return `${source.slice(0, maxLength - ellipsis.length)}${ellipsis}`;
|
|
9
|
-
}
|
|
10
|
-
export function formatSize(bytes) {
|
|
11
|
-
if (bytes < 1024)
|
|
12
|
-
return `${bytes}B`;
|
|
13
|
-
if (bytes < 1024 * 1024)
|
|
14
|
-
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
15
|
-
if (bytes < 1024 * 1024 * 1024)
|
|
16
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
17
|
-
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=text.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/extensions/rewind/text.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,SAAiB,EAAE,UAA+B,EAAE;IAC9F,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,IAAI,MAAM,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,QAAQ,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtE,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,GAAG,CAAC;IACrC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACzD,CAAC","sourcesContent":["export interface TruncateTextOptions {\n\tellipsis?: string;\n\tcollapseWhitespace?: boolean;\n}\n\nexport function truncateText(text: string, maxLength: number, options: TruncateTextOptions = {}): string {\n\tconst ellipsis = options.ellipsis ?? \"...\";\n\tconst source = options.collapseWhitespace ? text.replace(/\\s+/g, \" \").trim() : text;\n\tif (source.length <= maxLength) return source;\n\tif (ellipsis.length >= maxLength) return ellipsis.slice(0, maxLength);\n\treturn `${source.slice(0, maxLength - ellipsis.length)}${ellipsis}`;\n}\n\nexport function formatSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes}B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n\tif (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n\treturn `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-path.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/tool-path.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpE"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
export function editWriteTargetPath(input) {
|
|
3
|
-
const rawPath = input?.path;
|
|
4
|
-
return typeof rawPath === "string" && rawPath.length > 0 ? rawPath : undefined;
|
|
5
|
-
}
|
|
6
|
-
export function resolveToolPath(rawPath, cwd) {
|
|
7
|
-
return path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);
|
|
8
|
-
}
|
|
9
|
-
//# sourceMappingURL=tool-path.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-path.js","sourceRoot":"","sources":["../../../src/extensions/rewind/tool-path.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,mBAAmB,CAAC,KAAc;IACjD,MAAM,OAAO,GAAI,KAAwC,EAAE,IAAI,CAAC;IAChE,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,GAAW;IAC3D,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC","sourcesContent":["import path from \"node:path\";\n\nexport function editWriteTargetPath(input: unknown): string | undefined {\n\tconst rawPath = (input as { path?: unknown } | undefined)?.path;\n\treturn typeof rawPath === \"string\" && rawPath.length > 0 ? rawPath : undefined;\n}\n\nexport function resolveToolPath(rawPath: string, cwd: string): string {\n\treturn path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);\n}\n"]}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# Biu — project development workflow
|
|
2
|
-
|
|
3
|
-
Biu is a simple, structured development workflow for the interactive TUI: `/biu` switches Biu Mode on, and inside the mode `/biu` opens its menu. One project cycle moves through soft stages — **plan** (clarify requirements into an approved `SPEC.md`), an **optional decompose** step (break a complex SPEC into task handoffs; simple SPECs are implemented directly), **execute** (implement, one task at a time when tasks exist), and **archive** (summarize and close the cycle).
|
|
4
|
-
|
|
5
|
-
There is no state machine and no workflow tool. Workflow state lives in the frontmatter of the workspace Markdown files — the SPEC's `status: draft|ready`, each task's `status: ready|in_progress|completed` and `depends_on` — and the extension derives a read-only snapshot from them each turn. The model edits these files with the normal file tools through the `biu://` scheme.
|
|
6
|
-
|
|
7
|
-
## Command
|
|
8
|
-
|
|
9
|
-
| Command | Behavior |
|
|
10
|
-
|---|---|
|
|
11
|
-
| `/biu` | When the mode is off, turn it on (no agent turn is triggered); when it is on, open the Biu menu |
|
|
12
|
-
|
|
13
|
-
`/biu` accepts no arguments. Turning the mode on with no prior cycle leaves the workflow at the plan stage — describe what you want to build in your next message and the interview starts from there. With an existing cycle, the mode resumes at the stage derived from the workspace files.
|
|
14
|
-
|
|
15
|
-
The menu keeps only the actions needed to operate the workflow:
|
|
16
|
-
|
|
17
|
-
- **Continue · stage** — send a kickoff message (collapsed to one line; expandable with the standard expand key) that starts a turn continuing the current stage.
|
|
18
|
-
- **Archive cycle** — deterministically move `SPEC.md`, `Summary.md`, and `tasks/` into `archived/YYYY-MM-DD-<shortname>/`. Requires an existing, model-drafted and user-confirmed `Summary.md`; archiving with unfinished tasks asks for confirmation first. The shortname is derived from the SPEC's `title` frontmatter (a dialog asks for one when it is missing). On failure, already-moved files are rolled back.
|
|
19
|
-
- **Exit Biu Mode** — turn the mode off. Workflow files are kept.
|
|
20
|
-
|
|
21
|
-
A subtitle summarizes the current stage; while the mode is on, the footer keeps the marker `Biu · execute 2/5` (warning-colored when workspace files have problems, such as malformed frontmatter).
|
|
22
|
-
|
|
23
|
-
Biu Mode is TUI-only. RPC, JSON, and print sessions do not enable it, inject its resident prompt, or open its menu. Opening the same branch later in the TUI restores its recorded enabled flag.
|
|
24
|
-
|
|
25
|
-
## Storage and the `biu://` scheme
|
|
26
|
-
|
|
27
|
-
Biu artifacts live under the normal Pi agent directory, grouped by working directory with the same path encoding used for sessions:
|
|
28
|
-
|
|
29
|
-
```text
|
|
30
|
-
~/.pi/agent/biu/
|
|
31
|
-
└── --encoded-working-directory--/
|
|
32
|
-
├── SPEC.md # frontmatter: title, status, execution, baseline_commit
|
|
33
|
-
├── Summary.md # frontmatter: title, head_commit; temporary while archiving
|
|
34
|
-
├── tasks/
|
|
35
|
-
│ └── TASK-<short-name>.md # frontmatter: title, status, depends_on
|
|
36
|
-
└── archived/
|
|
37
|
-
└── YYYY-MM-DD-shortname/
|
|
38
|
-
├── SPEC.md
|
|
39
|
-
├── Summary.md
|
|
40
|
-
└── tasks/
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
`PI_CODING_AGENT_DIR` is respected.
|
|
44
|
-
|
|
45
|
-
The model never sees or uses these real paths. A `tool_call` hook rewrites `biu://` paths — `biu://SPEC.md`, `biu://tasks/TASK-api.md`, `biu://Summary.md` — to the workspace of the current working directory before `read`, `write`, `edit`, `grep`, `find`, and `ls` execute. The session records keep the original `biu://` arguments, so the transcript and TUI show the short stable paths while execution uses the resolved ones. Paths that escape the workspace (`..` segments, rooted paths) are blocked. The scheme resolves regardless of whether Biu Mode is on; the mode only controls prompting, the statusline, and the menu.
|
|
46
|
-
|
|
47
|
-
## Prompting
|
|
48
|
-
|
|
49
|
-
While the mode is on, the full Biu block is injected into the system prompt each turn: the `biu://` conventions, a JSON snapshot of the workspace (derived stage, SPEC metadata, task statuses and focus, detected problems), and the playbook for the derived stage, including the relevant Markdown templates. At the execute stage the playbook follows the SPEC's `execution` field — `direct` implements straight against the SPEC, `tasks` decomposes into task files first, and a SPEC without the field falls back to a combined playbook that asks to record the choice. There is nothing to fetch on demand — the workspace files are the state, and the snapshot always reflects them.
|
|
50
|
-
|
|
51
|
-
Stage transitions are file edits, with one guarded exception: flipping the SPEC's `status` to `ready` opens an approval dialog. Approving lets the write land and the cycle move on to execute; declining blocks the write and hands your feedback to the model as the tool result, so it revises the SPEC and asks again. During planning the model also records the agreed execution path in the frontmatter (`execution: direct` by default; `tasks` when the work splits into independently verifiable chunks). Writing task files opts into decomposition, task frontmatter tracks execution, and writing `Summary.md` (or completing every task) enters archive. Moving backward — for example reopening the SPEC as `draft` when execution reveals a gap, or switching `execution` when the size estimate was wrong — is just another frontmatter edit.
|
|
52
|
-
|
|
53
|
-
## Lifecycle
|
|
54
|
-
|
|
55
|
-
The enabled flag is stored as a branch-aware custom session entry. In the TUI, `session_start` and `session_tree` replay the latest flag, so `/reload`, resume, fork, and tree navigation restore the mode. Non-TUI sessions leave that flag untouched but keep Biu inactive. The statusline refreshes whenever a `write` or `edit` lands inside the workspace.
|
|
56
|
-
|
|
57
|
-
Biu artifacts are project-global rather than conversation-branch snapshots: navigating `/tree` changes the enabled flag with the branch, but workflow files do not roll back.
|
|
58
|
-
|
|
59
|
-
## Limits
|
|
60
|
-
|
|
61
|
-
- The snapshot lists at most 100 task files; additional files are reported as a problem instead of being silently ignored.
|
|
62
|
-
- Malformed frontmatter never blocks the workflow: the affected file degrades to an `unknown` status and the problem is surfaced to the model and in the statusline.
|
|
63
|
-
- Concurrent writers in multiple Pi processes are not coordinated. Use one active Biu writer per project.
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# rewind — checkpoints & rewind
|
|
2
|
-
|
|
3
|
-
Per-edit file backups plus a `/rewind` settings menu. Instead of snapshotting the
|
|
4
|
-
whole working tree, rewind backs up **only the files Pi's `edit`/`write` tools are
|
|
5
|
-
about to change** — one `copyFile` before each edit. Cost is proportional to how
|
|
6
|
-
many files Pi changed, never to project size, so it never blocks the session
|
|
7
|
-
lifecycle (the old shadow-git design froze the UI in large multi-project
|
|
8
|
-
directories) and storage stays tiny.
|
|
9
|
-
|
|
10
|
-
## Behavior
|
|
11
|
-
|
|
12
|
-
Per turn: `before_agent_start` opens a snapshot frame (re-recording every tracked
|
|
13
|
-
file at its turn-start state, reusing the latest backup when unchanged);
|
|
14
|
-
`tool_call(edit|write)` backs up each newly edited file *before* it lands;
|
|
15
|
-
`agent_settled` persists the frame to the session JSONL as a `pi-rewind-snapshot`
|
|
16
|
-
custom entry **only when files changed**. The frame anchors to the **first** user
|
|
17
|
-
entry the run appended — the message whose start the frame recorded; steering and
|
|
18
|
-
follow-up messages consumed inside the same run append later user entries and do
|
|
19
|
-
not steal the anchor (a run that appends no user entry, e.g. custom-triggered,
|
|
20
|
-
records no frame). Using `agent_settled` (not `agent_end`)
|
|
21
|
-
keeps auto-retry, overflow compaction-retry, and queued follow-ups in one logical
|
|
22
|
-
turn — `agent_end` can fire while Pi still continues. Requires **Pi ≥ 0.80.4**
|
|
23
|
-
(when `agent_settled` was added); older hosts never fire that event, so frames
|
|
24
|
-
would not finalize. Entries survive `/reload` and compaction; the index is
|
|
25
|
-
rebuilt from them on `session_start`.
|
|
26
|
-
|
|
27
|
-
- **Scope (deliberate trade-off):** rewind covers edits made through Pi's built-in
|
|
28
|
-
`edit` and `write` tools. Files written by `bash` (redirects, codegen, `mv`) or
|
|
29
|
-
edited by hand outside Pi are **not** tracked — same boundary as Claude Code's
|
|
30
|
-
file-history. Rewind undoes *Pi's edits*, not arbitrary filesystem state.
|
|
31
|
-
- **Time-travel is via `/tree`.** Navigating to a node whose recorded state
|
|
32
|
-
differs from the work tree prompts to restore, listing the affected files
|
|
33
|
-
(cwd-relative, up to 8 then *"+N more"*) under *"Restore 3 files to this
|
|
34
|
-
point? (+120 / −40)"* when coarse line stats are available. The restore
|
|
35
|
-
target mirrors `/tree`'s own semantics: a **user message** target (whose turn
|
|
36
|
-
is removed and returned to the editor) restores to the state *before* that
|
|
37
|
-
turn ran; any **other** target (whose turn stays in the conversation)
|
|
38
|
-
restores to the state *after* that turn — the state recorded when the next
|
|
39
|
-
run began. When nothing was recorded after the target (e.g. the last turn's
|
|
40
|
-
assistant message), navigation is silent rather than rolling back edits the
|
|
41
|
-
conversation still contains. Nodes with no file changes navigate silently.
|
|
42
|
-
Only files that actually differ are rewritten. Line totals are a bag-of-lines
|
|
43
|
-
estimate (capped at 1 MB per file), not a full Myers diff.
|
|
44
|
-
- **`/rewind` is a settings + storage menu**, not a restore picker:
|
|
45
|
-
- toggle rewind on/off,
|
|
46
|
-
- set the auto-clean retention window,
|
|
47
|
-
- inspect storage and prune (clean aged + orphaned / remove orphaned / remove
|
|
48
|
-
all except current — the latter also keeps backups written to in the last
|
|
49
|
-
10 minutes, which may belong to sessions running in another Pi process).
|
|
50
|
-
- **New files** created by `write` are tracked with a "did not exist" marker, so
|
|
51
|
-
rewinding deletes them.
|
|
52
|
-
- **Resume/fork** hard-links the prior session's backup blobs into the new
|
|
53
|
-
session (falling back to copy), so rewind keeps working without duplicating
|
|
54
|
-
storage.
|
|
55
|
-
|
|
56
|
-
## Storage & cleanup
|
|
57
|
-
|
|
58
|
-
- **Layout:** `~/.pi/agent/rewind/`
|
|
59
|
-
- `config.json` — `{ enabled, retentionDays, maxSnapshots }`
|
|
60
|
-
- `backups/<sessionId>/<sha256(relpath)[:16]>@v<n>` — backup blobs (flat, hashed
|
|
61
|
-
names keep paths short on Windows; mode preserved)
|
|
62
|
-
- **Auto-clean:** at `session_start`, backup directories older than
|
|
63
|
-
`retentionDays` (default **30**, set in `/rewind`) are reclaimed, plus an
|
|
64
|
-
**orphan sweep** of directories whose session id has no session JSONL (crashed
|
|
65
|
-
sessions). The GC is time-boxed and deletion-capped so it never slows startup.
|
|
66
|
-
- The former `pi-config` storage layout is **not** read or migrated by this
|
|
67
|
-
engine. Remove any old `~/.pi/agent/pi-config/` data manually if it is no
|
|
68
|
-
longer needed.
|
|
69
|
-
|
|
70
|
-
## Limits
|
|
71
|
-
|
|
72
|
-
- Covers edits made through Pi's built-in `edit` and `write` tools only. Files written by `bash` or edited outside Pi are not tracked.
|
|
73
|
-
- A backup failure is swallowed — the edit proceeds without checkpointing rather than blocking the tool.
|
|
74
|
-
- `/rewind` on its own does not restore files. Restoration uses `/tree` — navigate to a turn and confirm the prompt.
|
|
75
|
-
- Restore is coarse: only files that differ are rewritten, but line totals are a bag-of-lines estimate (capped at 1 MB per file), not a full Myers diff.
|
|
76
|
-
|
|
77
|
-
## Implementation notes
|
|
78
|
-
|
|
79
|
-
- **Never blocks the edit.** Backup runs before the write lands; a backup failure is swallowed so the session never stalls.
|
|
80
|
-
- **Restore safety.** `applySnapshot` only rewrites files that differ and degrades gracefully on unreadable backups.
|
|
81
|
-
- **Change detection.** A file is re-backed-up only when its stat or content differs from the latest backup. Content comparison streams in 64 KiB chunks instead of buffering whole files up to the 25 MB cap.
|
|
82
|
-
- **Config is memory-cached.** `config.json` is re-read on `session_start` and updated in memory when `/rewind` saves.
|
|
83
|
-
- **Restore path is single-scan.** The `/tree` confirm pass caches changed absolute paths; IO is concurrency-capped (16).
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Architecture informed by oh-my-pi (GPL-3.0) and Claude Code's file-history; independent implementation.
|