@yagni-app/code 1.0.0 → 1.0.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 +42 -0
- package/dist/cli.js +231 -6
- package/dist/crashReport.d.ts +8 -0
- package/dist/crashReport.js +13 -1
- package/dist/doctor.d.ts +7 -0
- package/dist/doctor.js +33 -0
- package/dist/extension/askAdvisorTool.d.ts +7 -0
- package/dist/extension/askAdvisorTool.js +11 -3
- package/dist/extension/askYagniTool.js +2 -0
- package/dist/extension/branding.d.ts +15 -0
- package/dist/extension/branding.js +76 -0
- package/dist/extension/chipEditor.d.ts +22 -1
- package/dist/extension/chipEditor.js +58 -5
- package/dist/extension/condensedTools.d.ts +93 -0
- package/dist/extension/condensedTools.js +392 -0
- package/dist/extension/diffStat.d.ts +62 -0
- package/dist/extension/diffStat.js +158 -0
- package/dist/extension/footer.d.ts +2 -0
- package/dist/extension/footer.js +21 -8
- package/dist/extension/index.d.ts +6 -0
- package/dist/extension/index.js +70 -2
- package/dist/extension/permission/execPolicy.js +47 -0
- package/dist/extension/pipeline/invocation.d.ts +7 -0
- package/dist/extension/pipeline/invocation.js +7 -0
- package/dist/extension/pipeline/personas.js +4 -4
- package/dist/extension/pipeline/runner.d.ts +1 -0
- package/dist/extension/pipeline/runner.js +15 -3
- package/dist/extension/pipeline/sessionWorktree.d.ts +64 -0
- package/dist/extension/pipeline/sessionWorktree.js +225 -0
- package/dist/extension/scratchpad.d.ts +66 -0
- package/dist/extension/scratchpad.js +93 -0
- package/dist/extension/subagents.d.ts +10 -0
- package/dist/extension/subagents.js +18 -4
- package/dist/extension/todos.d.ts +1 -0
- package/dist/extension/todos.js +15 -0
- package/dist/extension/toolRuns.d.ts +92 -0
- package/dist/extension/toolRuns.js +201 -0
- package/dist/extension/webFetchTool.js +2 -0
- package/dist/extension/workingLine.d.ts +49 -0
- package/dist/extension/workingLine.js +116 -0
- package/dist/feedback.d.ts +77 -0
- package/dist/feedback.js +500 -0
- package/dist/goHeadless.d.ts +3 -0
- package/dist/goHeadless.js +13 -0
- package/dist/launch.d.ts +8 -0
- package/dist/launch.js +6 -0
- package/dist/otel.d.ts +150 -0
- package/dist/otel.js +291 -0
- package/dist/outputFormat.d.ts +83 -0
- package/dist/outputFormat.js +207 -0
- package/dist/paths.d.ts +10 -0
- package/dist/paths.js +13 -0
- package/dist/worktreeArgs.d.ts +43 -0
- package/dist/worktreeArgs.js +96 -0
- package/package.json +3 -2
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run model for the condensed tool transcript (Claude Code-style).
|
|
3
|
+
*
|
|
4
|
+
* A "run" is a maximal stretch of consecutive QUIET tool rows (reads, searches,
|
|
5
|
+
* listings, successful shell commands, scratchpad edits) uninterrupted by an
|
|
6
|
+
* assistant/user message or a visible row (errors, project writes/edits, image
|
|
7
|
+
* reads). Collapsed, every row in a completed run renders zero lines except the
|
|
8
|
+
* run's tail, which paints one summary line: "Read 2 files, ran 2 shell
|
|
9
|
+
* commands". Expanded (ctrl+o) bypasses this model entirely.
|
|
10
|
+
*
|
|
11
|
+
* The tracker is built purely from render calls (idempotent upserts keyed by
|
|
12
|
+
* toolCallId, in first-render order, which matches display order both live and
|
|
13
|
+
* on session replay). Message boundaries arrive via {@link ToolRunTracker.markBreak}
|
|
14
|
+
* from live `message_start` events only — after a resume, runs that were
|
|
15
|
+
* separated by prose may merge into one summary. That is a deliberate trade:
|
|
16
|
+
* render-derived state is the only state that survives replay.
|
|
17
|
+
*
|
|
18
|
+
* Everything here is PURE (no pi imports) so tests run against plain objects;
|
|
19
|
+
* the pi wiring lives in condensedTools.ts.
|
|
20
|
+
*/
|
|
21
|
+
const KIND_BY_TOOL = {
|
|
22
|
+
read: "read",
|
|
23
|
+
bash: "shell",
|
|
24
|
+
grep: "search",
|
|
25
|
+
find: "list",
|
|
26
|
+
ls: "list",
|
|
27
|
+
write: "write",
|
|
28
|
+
edit: "edit",
|
|
29
|
+
};
|
|
30
|
+
/** Aggregation kind for a built-in tool name (undefined for non-built-ins). */
|
|
31
|
+
export function kindForTool(toolName) {
|
|
32
|
+
return KIND_BY_TOOL[toolName];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Whether a row folds into a run summary. Reads, searches, listings, and
|
|
36
|
+
* successful shell commands always do; writes/edits only when they target the
|
|
37
|
+
* scratchpad (project mutations must stay visible). Errors and image-bearing
|
|
38
|
+
* results are always visible.
|
|
39
|
+
*/
|
|
40
|
+
export function isQuiet(row) {
|
|
41
|
+
if (row.error || row.images)
|
|
42
|
+
return false;
|
|
43
|
+
if (row.kind === "write" || row.kind === "edit")
|
|
44
|
+
return row.scratchpad;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function plural(n, singular, pluralForm = `${singular}s`) {
|
|
48
|
+
return n === 1 ? singular : pluralForm;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* One summary line for a completed run, phrases in first-occurrence order:
|
|
52
|
+
* "Read 2 files, ran 2 shell commands, made 1 scratchpad edit +20".
|
|
53
|
+
*/
|
|
54
|
+
export function summarizeRun(rows) {
|
|
55
|
+
const order = [];
|
|
56
|
+
const counts = new Map();
|
|
57
|
+
for (const row of rows) {
|
|
58
|
+
const category = row.kind === "write" || row.kind === "edit" ? "scratch" : row.kind;
|
|
59
|
+
let entry = counts.get(category);
|
|
60
|
+
if (!entry) {
|
|
61
|
+
entry = { n: 0, added: 0 };
|
|
62
|
+
counts.set(category, entry);
|
|
63
|
+
order.push(category);
|
|
64
|
+
}
|
|
65
|
+
entry.n += 1;
|
|
66
|
+
entry.added += row.added;
|
|
67
|
+
}
|
|
68
|
+
const phrases = order.map((category) => {
|
|
69
|
+
const { n, added } = counts.get(category);
|
|
70
|
+
switch (category) {
|
|
71
|
+
case "read":
|
|
72
|
+
return `read ${n} ${plural(n, "file")}`;
|
|
73
|
+
case "shell":
|
|
74
|
+
return `ran ${n} shell ${plural(n, "command")}`;
|
|
75
|
+
case "search":
|
|
76
|
+
return `searched for ${n} ${plural(n, "pattern")}`;
|
|
77
|
+
case "list":
|
|
78
|
+
return `listed ${n} ${plural(n, "path")}`;
|
|
79
|
+
default:
|
|
80
|
+
return `made ${n} scratchpad ${plural(n, "edit")}${added > 0 ? ` +${added}` : ""}`;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
const line = phrases.join(", ");
|
|
84
|
+
return line.charAt(0).toUpperCase() + line.slice(1);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Ordered row registry. Upserts are idempotent and diff-aware: only a change
|
|
88
|
+
* invalidates the affected run's tail (the one component whose output depends
|
|
89
|
+
* on neighbors), so repaints converge instead of looping.
|
|
90
|
+
*/
|
|
91
|
+
export class ToolRunTracker {
|
|
92
|
+
rows = [];
|
|
93
|
+
indexById = new Map();
|
|
94
|
+
breakPending = true;
|
|
95
|
+
/** Record an assistant/user message boundary; the next new row starts a fresh run. */
|
|
96
|
+
markBreak() {
|
|
97
|
+
this.breakPending = true;
|
|
98
|
+
}
|
|
99
|
+
get(id) {
|
|
100
|
+
const i = this.indexById.get(id);
|
|
101
|
+
return i === undefined ? undefined : this.rows[i];
|
|
102
|
+
}
|
|
103
|
+
upsert(id, patch) {
|
|
104
|
+
const existingIndex = this.indexById.get(id);
|
|
105
|
+
if (existingIndex === undefined) {
|
|
106
|
+
const row = {
|
|
107
|
+
id,
|
|
108
|
+
kind: patch.kind ?? "shell",
|
|
109
|
+
scratchpad: patch.scratchpad ?? false,
|
|
110
|
+
error: patch.error ?? false,
|
|
111
|
+
images: patch.images ?? false,
|
|
112
|
+
final: patch.final ?? false,
|
|
113
|
+
added: patch.added ?? 0,
|
|
114
|
+
breakBefore: this.breakPending,
|
|
115
|
+
...(patch.invalidate ? { invalidate: patch.invalidate } : {}),
|
|
116
|
+
};
|
|
117
|
+
this.breakPending = false;
|
|
118
|
+
this.indexById.set(id, this.rows.length);
|
|
119
|
+
this.rows.push(row);
|
|
120
|
+
// The previous row may have been its run's tail (painting a summary);
|
|
121
|
+
// now that the run extends past it, repaint it as a hidden member.
|
|
122
|
+
// (invalidateTailOf(prev) would be wrong here: prev's run now includes
|
|
123
|
+
// this new row, so its tail is the row currently painting, not prev.)
|
|
124
|
+
const prev = this.rows[this.rows.length - 2];
|
|
125
|
+
if (prev && isQuiet(prev) && !row.breakBefore)
|
|
126
|
+
this.scheduleInvalidate(prev);
|
|
127
|
+
return row;
|
|
128
|
+
}
|
|
129
|
+
const row = this.rows[existingIndex];
|
|
130
|
+
if (patch.invalidate)
|
|
131
|
+
row.invalidate = patch.invalidate;
|
|
132
|
+
let changed = false;
|
|
133
|
+
for (const key of ["kind", "scratchpad", "error", "images", "final", "added"]) {
|
|
134
|
+
const next = patch[key];
|
|
135
|
+
if (next !== undefined && row[key] !== next) {
|
|
136
|
+
row[key] = next;
|
|
137
|
+
changed = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (changed)
|
|
141
|
+
this.invalidateTailOf(row);
|
|
142
|
+
return row;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The one summary line for `id`, present only when `id` is the tail of a
|
|
146
|
+
* fully-final quiet run. Every other member of the run gets undefined.
|
|
147
|
+
*/
|
|
148
|
+
summaryFor(id) {
|
|
149
|
+
const row = this.get(id);
|
|
150
|
+
if (!row || !isQuiet(row) || !row.final)
|
|
151
|
+
return undefined;
|
|
152
|
+
const run = this.runOf(row);
|
|
153
|
+
if (run[run.length - 1] !== row)
|
|
154
|
+
return undefined;
|
|
155
|
+
if (run.some((r) => !r.final))
|
|
156
|
+
return undefined;
|
|
157
|
+
return summarizeRun(run);
|
|
158
|
+
}
|
|
159
|
+
/** The contiguous quiet run containing `row` (just `[row]` when visible). */
|
|
160
|
+
runOf(row) {
|
|
161
|
+
if (!isQuiet(row))
|
|
162
|
+
return [row];
|
|
163
|
+
const i = this.indexById.get(row.id);
|
|
164
|
+
let start = i;
|
|
165
|
+
while (start > 0 && !this.rows[start].breakBefore && isQuiet(this.rows[start - 1]))
|
|
166
|
+
start--;
|
|
167
|
+
let end = i;
|
|
168
|
+
while (end < this.rows.length - 1 &&
|
|
169
|
+
!this.rows[end + 1].breakBefore &&
|
|
170
|
+
isQuiet(this.rows[end + 1])) {
|
|
171
|
+
end++;
|
|
172
|
+
}
|
|
173
|
+
return this.rows.slice(start, end + 1);
|
|
174
|
+
}
|
|
175
|
+
invalidateTailOf(row) {
|
|
176
|
+
const run = this.runOf(row);
|
|
177
|
+
const tail = run[run.length - 1];
|
|
178
|
+
if (tail)
|
|
179
|
+
this.scheduleInvalidate(tail);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Defer a row's repaint to a microtask, deduped per row. Upserts run INSIDE
|
|
183
|
+
* pi's synchronous `updateDisplay` pass (renderers call them), and the
|
|
184
|
+
* component's `invalidate()` re-enters `updateDisplay` immediately — a
|
|
185
|
+
* synchronous call from a renderer would rebuild the container while the
|
|
186
|
+
* outer frame is still appending to it, stacking duplicate children.
|
|
187
|
+
* Deferring means every repaint runs as its own clean top-level pass; it
|
|
188
|
+
* still cannot loop, because the diff-aware upsert only schedules on an
|
|
189
|
+
* actual change.
|
|
190
|
+
*/
|
|
191
|
+
scheduleInvalidate(row) {
|
|
192
|
+
if (row.invalidatePending)
|
|
193
|
+
return;
|
|
194
|
+
row.invalidatePending = true;
|
|
195
|
+
queueMicrotask(() => {
|
|
196
|
+
row.invalidatePending = false;
|
|
197
|
+
row.invalidate?.();
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=toolRuns.js.map
|
|
@@ -44,6 +44,8 @@ export function makeWebFetchTool(opts) {
|
|
|
44
44
|
"Use web_fetch to read a URL's content instead of bash + curl when you need a page or a summary of it.",
|
|
45
45
|
],
|
|
46
46
|
parameters,
|
|
47
|
+
// Self-framed: the condensed transcript look has no tinted tool boxes.
|
|
48
|
+
renderShell: "self",
|
|
47
49
|
renderCall(args, theme) {
|
|
48
50
|
const t = theme;
|
|
49
51
|
const url = clipLine(args?.url ?? "…", 80);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Claude Code-style streaming status line: `Shaping… (12m 54s · ↓ 47.5k
|
|
3
|
+
* tokens)` in place of pi's static "Working...".
|
|
4
|
+
*
|
|
5
|
+
* One manager owns `ctx.ui.setWorkingMessage` for the whole session so the
|
|
6
|
+
* verb, the elapsed clock, and the token counter never fight the subagent /
|
|
7
|
+
* advisor progress text: those tools publish their activity line through
|
|
8
|
+
* {@link WorkingLineHandle.setActivity} (instead of calling setWorkingMessage
|
|
9
|
+
* directly), and the manager splices it in as the head of the same composed
|
|
10
|
+
* message. Elapsed time spans the whole agent loop (agent_start → agent_end);
|
|
11
|
+
* the token counter accumulates assistant output tokens across the loop's
|
|
12
|
+
* messages (message_end), which is when pi learns usage — subagent tokens live
|
|
13
|
+
* in the subagent's own activity text, not this counter.
|
|
14
|
+
*
|
|
15
|
+
* TUI-only by the agent_start guard; a headless /go child or desktop surface
|
|
16
|
+
* never gets a working line. Everything is fail-soft: a status line must never
|
|
17
|
+
* break a turn.
|
|
18
|
+
*/
|
|
19
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
20
|
+
/** What subagents/advisor publish instead of calling setWorkingMessage. */
|
|
21
|
+
export interface WorkingLineHandle {
|
|
22
|
+
setActivity(text?: string): void;
|
|
23
|
+
}
|
|
24
|
+
/** A no-op handle for callers wired without a manager (tests, children). */
|
|
25
|
+
export declare const NULL_WORKING_LINE: WorkingLineHandle;
|
|
26
|
+
/**
|
|
27
|
+
* The verb pool. Neutral gerunds — one is picked per agent loop, so long
|
|
28
|
+
* sessions read as a person at work rather than a stuck spinner.
|
|
29
|
+
*/
|
|
30
|
+
export declare const WORKING_VERBS: readonly string[];
|
|
31
|
+
/** Pulse frames for the streaming indicator (pi renders them verbatim). */
|
|
32
|
+
export declare const WORKING_INDICATOR_FRAMES: string[];
|
|
33
|
+
export interface ComposeWorkingOpts {
|
|
34
|
+
/** Override head from a running subagent/advisor (verb used when absent). */
|
|
35
|
+
activity?: string | undefined;
|
|
36
|
+
verb: string;
|
|
37
|
+
elapsedMs: number;
|
|
38
|
+
outputTokens: number;
|
|
39
|
+
}
|
|
40
|
+
/** `Shaping… (12m 54s · ↓ 47.5k tokens)` — pure, exported for tests. */
|
|
41
|
+
export declare function composeWorkingMessage(opts: ComposeWorkingOpts): string;
|
|
42
|
+
export interface RegisterWorkingLineDeps {
|
|
43
|
+
now?: () => number;
|
|
44
|
+
pickVerb?: (verbs: readonly string[]) => string;
|
|
45
|
+
/** Refresh cadence for the elapsed clock. */
|
|
46
|
+
tickMs?: number;
|
|
47
|
+
}
|
|
48
|
+
export declare function registerWorkingLine(pi: ExtensionAPI, deps?: RegisterWorkingLineDeps): WorkingLineHandle;
|
|
49
|
+
//# sourceMappingURL=workingLine.d.ts.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Claude Code-style streaming status line: `Shaping… (12m 54s · ↓ 47.5k
|
|
3
|
+
* tokens)` in place of pi's static "Working...".
|
|
4
|
+
*
|
|
5
|
+
* One manager owns `ctx.ui.setWorkingMessage` for the whole session so the
|
|
6
|
+
* verb, the elapsed clock, and the token counter never fight the subagent /
|
|
7
|
+
* advisor progress text: those tools publish their activity line through
|
|
8
|
+
* {@link WorkingLineHandle.setActivity} (instead of calling setWorkingMessage
|
|
9
|
+
* directly), and the manager splices it in as the head of the same composed
|
|
10
|
+
* message. Elapsed time spans the whole agent loop (agent_start → agent_end);
|
|
11
|
+
* the token counter accumulates assistant output tokens across the loop's
|
|
12
|
+
* messages (message_end), which is when pi learns usage — subagent tokens live
|
|
13
|
+
* in the subagent's own activity text, not this counter.
|
|
14
|
+
*
|
|
15
|
+
* TUI-only by the agent_start guard; a headless /go child or desktop surface
|
|
16
|
+
* never gets a working line. Everything is fail-soft: a status line must never
|
|
17
|
+
* break a turn.
|
|
18
|
+
*/
|
|
19
|
+
import { usageFromMessage } from "./costHud.js";
|
|
20
|
+
import { formatDuration, formatTokens } from "./subagentRender.js";
|
|
21
|
+
/** A no-op handle for callers wired without a manager (tests, children). */
|
|
22
|
+
export const NULL_WORKING_LINE = { setActivity: () => { } };
|
|
23
|
+
/**
|
|
24
|
+
* The verb pool. Neutral gerunds — one is picked per agent loop, so long
|
|
25
|
+
* sessions read as a person at work rather than a stuck spinner.
|
|
26
|
+
*/
|
|
27
|
+
export const WORKING_VERBS = [
|
|
28
|
+
"Working",
|
|
29
|
+
"Thinking",
|
|
30
|
+
"Exploring",
|
|
31
|
+
"Tracing",
|
|
32
|
+
"Shaping",
|
|
33
|
+
"Wiring",
|
|
34
|
+
"Weighing",
|
|
35
|
+
"Sketching",
|
|
36
|
+
"Assembling",
|
|
37
|
+
"Distilling",
|
|
38
|
+
"Untangling",
|
|
39
|
+
"Polishing",
|
|
40
|
+
];
|
|
41
|
+
/** Pulse frames for the streaming indicator (pi renders them verbatim). */
|
|
42
|
+
export const WORKING_INDICATOR_FRAMES = ["·", "✢", "✳", "✶", "✳", "✢"];
|
|
43
|
+
/** `Shaping… (12m 54s · ↓ 47.5k tokens)` — pure, exported for tests. */
|
|
44
|
+
export function composeWorkingMessage(opts) {
|
|
45
|
+
const head = opts.activity ?? `${opts.verb}…`;
|
|
46
|
+
const stats = [
|
|
47
|
+
formatDuration(opts.elapsedMs),
|
|
48
|
+
opts.outputTokens > 0 ? `↓ ${formatTokens(opts.outputTokens)} tokens` : undefined,
|
|
49
|
+
]
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.join(" · ");
|
|
52
|
+
return `${head} (${stats})`;
|
|
53
|
+
}
|
|
54
|
+
export function registerWorkingLine(pi, deps = {}) {
|
|
55
|
+
const now = deps.now ?? (() => Date.now());
|
|
56
|
+
const pickVerb = deps.pickVerb ?? ((verbs) => verbs[Math.floor(Math.random() * verbs.length)]);
|
|
57
|
+
const tickMs = deps.tickMs ?? 1_000;
|
|
58
|
+
let ui;
|
|
59
|
+
let timer;
|
|
60
|
+
let startedAt;
|
|
61
|
+
let outputTokens = 0;
|
|
62
|
+
let verb = WORKING_VERBS[0];
|
|
63
|
+
let activity;
|
|
64
|
+
const refresh = () => {
|
|
65
|
+
if (!ui || startedAt === undefined)
|
|
66
|
+
return;
|
|
67
|
+
try {
|
|
68
|
+
ui.setWorkingMessage?.(composeWorkingMessage({ activity, verb, elapsedMs: now() - startedAt, outputTokens }));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// The status line must never break a turn.
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const stop = () => {
|
|
75
|
+
if (timer)
|
|
76
|
+
clearInterval(timer);
|
|
77
|
+
timer = undefined;
|
|
78
|
+
startedAt = undefined;
|
|
79
|
+
activity = undefined;
|
|
80
|
+
try {
|
|
81
|
+
ui?.setWorkingMessage?.();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Restoring the default label is best-effort.
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
pi.on("agent_start", (_event, ctx) => {
|
|
88
|
+
if (ctx.mode !== "tui" || !ctx.hasUI)
|
|
89
|
+
return;
|
|
90
|
+
ui = ctx.ui;
|
|
91
|
+
startedAt = now();
|
|
92
|
+
outputTokens = 0;
|
|
93
|
+
activity = undefined;
|
|
94
|
+
verb = pickVerb(WORKING_VERBS);
|
|
95
|
+
refresh();
|
|
96
|
+
if (timer)
|
|
97
|
+
clearInterval(timer);
|
|
98
|
+
timer = setInterval(refresh, tickMs);
|
|
99
|
+
timer.unref?.();
|
|
100
|
+
});
|
|
101
|
+
pi.on("agent_end", () => stop());
|
|
102
|
+
pi.on("message_end", (event) => {
|
|
103
|
+
const message = event.message;
|
|
104
|
+
if (message?.role !== "assistant")
|
|
105
|
+
return;
|
|
106
|
+
outputTokens += usageFromMessage(message).output;
|
|
107
|
+
refresh();
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
setActivity(text) {
|
|
111
|
+
activity = text;
|
|
112
|
+
refresh();
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=workingLine.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yagni feedback [sessionId]` — file a bug report from the shell (YAG-592).
|
|
3
|
+
*
|
|
4
|
+
* A shortcut to trigger what `/feedback` does inside a session, but from
|
|
5
|
+
* outside the TUI. Two modes:
|
|
6
|
+
*
|
|
7
|
+
* Case A (no session arg): list the 10 most recent sessions for the current
|
|
8
|
+
* cwd, let the user pick, prompt for a description, confirm, submit.
|
|
9
|
+
* Case B (session ID provided): skip the list, go straight to description
|
|
10
|
+
* prompt → confirm → submit.
|
|
11
|
+
*
|
|
12
|
+
* Self-contained: no cross-package imports. The scrub (`scrubSecrets`) and the
|
|
13
|
+
* error-trail reader (`readSessionTrail`) are local copies of the extension's
|
|
14
|
+
* logic — keep in sync with `pi-extension-yagni/src/pipeline/scrubSecrets.ts`
|
|
15
|
+
* and `pi-extension-yagni/src/errorSink.ts`. The backend re-scrubs server-side
|
|
16
|
+
* (`backend/src/yagniCode/feedback.ts`), so the client-side scrub is the first
|
|
17
|
+
* line of defense, not the only one.
|
|
18
|
+
*/
|
|
19
|
+
export interface FeedbackDeps {
|
|
20
|
+
loadCredentials?: () => Promise<{
|
|
21
|
+
token?: string;
|
|
22
|
+
baseUrl: string;
|
|
23
|
+
name: string;
|
|
24
|
+
}>;
|
|
25
|
+
fetchImpl?: typeof fetch;
|
|
26
|
+
env?: NodeJS.ProcessEnv;
|
|
27
|
+
cwd?: string;
|
|
28
|
+
/** Override the agent dir (sessions live under `<agentDir>/sessions/...`). */
|
|
29
|
+
agentDirPath?: string;
|
|
30
|
+
/** Override the state dir (error sink lives under `<stateDir>/logs/...`). */
|
|
31
|
+
stateDir?: string;
|
|
32
|
+
writeOut?: (line: string) => void;
|
|
33
|
+
writeErr?: (line: string) => void;
|
|
34
|
+
/** Seam for readline — tests inject a fake that returns scripted answers. */
|
|
35
|
+
readline?: {
|
|
36
|
+
question: (q: string) => Promise<string>;
|
|
37
|
+
close: () => void;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface SessionInfo {
|
|
41
|
+
id: string;
|
|
42
|
+
filePath: string;
|
|
43
|
+
startTime: string;
|
|
44
|
+
durationMs: number | null;
|
|
45
|
+
firstMessage: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Encode a cwd into pi's session directory name format:
|
|
49
|
+
* `/Users/foo/bar` → `--Users-foo-bar--`
|
|
50
|
+
* Mirrors pi's `migrations.js`: `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`
|
|
51
|
+
*/
|
|
52
|
+
export declare function encodeCwd(cwd: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* List the most recent sessions for the current cwd.
|
|
55
|
+
* Scans `<agentDir>/sessions/<encoded-cwd>/*.jsonl`, sorted by start time desc.
|
|
56
|
+
*/
|
|
57
|
+
export declare function listRecentSessions(agentDirPath: string, cwd: string, limit?: number, termCols?: number): SessionInfo[];
|
|
58
|
+
/**
|
|
59
|
+
* Find a session file by UUID across all project dirs.
|
|
60
|
+
*/
|
|
61
|
+
export declare function findSessionById(agentDirPath: string, sessionId: string): {
|
|
62
|
+
filePath: string;
|
|
63
|
+
startTime: string;
|
|
64
|
+
} | null;
|
|
65
|
+
/**
|
|
66
|
+
* Read the durable transcript, clamped by byte size. Returns empty on any
|
|
67
|
+
* failure or when too large. Mirrors the extension's `readTranscript`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function readTranscript(sessionFile: string | undefined): string;
|
|
70
|
+
/**
|
|
71
|
+
* Read the session-scoped error trail from today's error sink file.
|
|
72
|
+
* Filters by sessionId and excludes debug-level entries. Scrubs each line.
|
|
73
|
+
* Mirrors the extension's `readSessionTrail` — keep in sync.
|
|
74
|
+
*/
|
|
75
|
+
export declare function readSessionTrail(sessionId: string, stateDir: string, maxBytes?: number): string;
|
|
76
|
+
export declare function feedbackCommand(args: string[], deps?: FeedbackDeps, cliVersion?: string): Promise<number>;
|
|
77
|
+
//# sourceMappingURL=feedback.d.ts.map
|