one-code-extension 0.1.3 → 0.3.0
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/LICENSE +20 -0
- package/README.md +200 -172
- package/agents/explore.md +9 -6
- package/agents/general-purpose.md +1 -1
- package/agents/plan.md +6 -4
- package/docs/guide/README.md +52 -0
- package/docs/guide/bring-your-claude-code-setup.md +126 -0
- package/docs/guide/configuration.md +171 -0
- package/docs/guide/differences-from-claude-code.md +150 -0
- package/docs/guide/doctor.md +86 -0
- package/docs/guide/hooks.md +109 -0
- package/docs/guide/installation.md +124 -0
- package/docs/guide/permissions-modes-and-auto-mode.md +450 -0
- package/docs/guide/providers-and-models.md +194 -0
- package/docs/guide/reference.md +224 -0
- package/docs/guide/sessions-and-context.md +190 -0
- package/docs/guide/skills-plugins-and-mcp.md +176 -0
- package/docs/guide/subagents-and-workflows.md +193 -0
- package/docs/guide/tasks-and-background-work.md +111 -0
- package/docs/guide/terminal-interface.md +155 -0
- package/docs/guide/tools.md +175 -0
- package/docs/guide/troubleshooting.md +175 -0
- package/extensions/ask-user/index.ts +65 -71
- package/extensions/ask-user/questions.ts +15 -28
- package/extensions/ask-user/widget.ts +533 -0
- package/extensions/auto-mode/actions.ts +9 -26
- package/extensions/auto-mode/classifier-prompt.ts +127 -0
- package/extensions/auto-mode/classifier.ts +305 -227
- package/extensions/auto-mode/config.ts +279 -77
- package/extensions/auto-mode/defaults.ts +17 -60
- package/extensions/auto-mode/instructions.ts +2 -1
- package/extensions/auto-mode/model-picker.ts +53 -31
- package/extensions/auto-mode/model-select.ts +153 -82
- package/extensions/auto-mode/paths.ts +13 -1
- package/extensions/auto-mode/pause.ts +8 -0
- package/extensions/auto-mode/prompt.ts +205 -238
- package/extensions/auto-mode/recoverability.ts +229 -0
- package/extensions/auto-mode/rules.ts +92 -75
- package/extensions/auto-mode/safety-floor.ts +43 -12
- package/extensions/auto-mode/sensitive.ts +11 -0
- package/extensions/auto-mode/setup-run.ts +245 -0
- package/extensions/auto-mode/setup.ts +235 -0
- package/extensions/auto-mode/shell-analysis.ts +332 -26
- package/extensions/auto-mode/transcript.ts +102 -0
- package/extensions/background/index.ts +256 -43
- package/extensions/background/monitor-batch.ts +58 -0
- package/extensions/background/registry.ts +9 -0
- package/extensions/background/wakeup.ts +45 -0
- package/extensions/bash/background.ts +47 -22
- package/extensions/bash/guards.ts +229 -0
- package/extensions/bash/index.ts +81 -19
- package/extensions/branding/index.ts +44 -18
- package/extensions/branding/startup.ts +23 -8
- package/extensions/claude-compat/index.ts +17 -2
- package/extensions/claude-context/index.ts +41 -12
- package/extensions/clear/index.ts +27 -0
- package/extensions/compaction/fit.ts +121 -0
- package/extensions/compaction/index.ts +176 -71
- package/extensions/compaction/prompt.ts +21 -1
- package/extensions/context-budget/budget.ts +61 -0
- package/extensions/context-budget/index.ts +66 -0
- package/extensions/context-management/index.ts +40 -16
- package/extensions/doctor/build.ts +226 -0
- package/extensions/doctor/cli.ts +99 -0
- package/extensions/doctor/compat.ts +532 -0
- package/extensions/doctor/dependencies.ts +186 -0
- package/extensions/doctor/fix-prompt.ts +125 -0
- package/extensions/doctor/index.ts +308 -0
- package/extensions/doctor/models.ts +231 -0
- package/extensions/doctor/presets.ts +208 -0
- package/extensions/doctor/report.ts +189 -0
- package/extensions/doctor/update-lookup.ts +46 -0
- package/extensions/doctor/viewer.ts +124 -0
- package/extensions/effort/index.ts +41 -4
- package/extensions/effort/slider.ts +33 -2
- package/extensions/file-tracker/index.ts +143 -20
- package/extensions/file-tracker/replay.ts +46 -0
- package/extensions/file-tracker/tracker.ts +51 -4
- package/extensions/footer/footer-line.ts +207 -0
- package/extensions/footer/index.ts +152 -0
- package/extensions/footer/pr.ts +53 -0
- package/extensions/hooks/executor.ts +57 -20
- package/extensions/hooks/formatter-notice.ts +76 -0
- package/extensions/hooks/index.ts +314 -73
- package/extensions/hooks/matcher.ts +35 -1
- package/extensions/hooks/plugin-hooks.ts +3 -3
- package/extensions/hooks/protocol.ts +58 -0
- package/extensions/hooks/subagent-bridge.ts +77 -0
- package/extensions/hooks/trust.ts +15 -4
- package/extensions/init/index.ts +26 -0
- package/extensions/init/prompt.ts +40 -0
- package/extensions/interrupted/index.ts +35 -0
- package/extensions/interrupted/line.ts +9 -0
- package/extensions/lib/abort.ts +15 -0
- package/extensions/lib/agent-loader.ts +183 -0
- package/extensions/lib/anthropic-payload.ts +15 -0
- package/extensions/lib/atomic-write.ts +56 -0
- package/extensions/lib/bridge-watch.ts +20 -0
- package/extensions/lib/cache-retention.ts +60 -0
- package/extensions/lib/capability-index.ts +466 -0
- package/extensions/lib/claude-context.ts +318 -27
- package/extensions/lib/claude-settings.ts +64 -0
- package/extensions/lib/consent-preview.ts +30 -0
- package/extensions/lib/deferred.ts +144 -13
- package/extensions/lib/favorites.ts +43 -0
- package/extensions/lib/fetch-timeout.ts +15 -0
- package/extensions/lib/frontmatter.ts +31 -0
- package/extensions/lib/git.ts +44 -2
- package/extensions/lib/hook-gate.ts +90 -0
- package/extensions/lib/interrupt.ts +94 -0
- package/extensions/lib/mcp-overrides.ts +61 -0
- package/extensions/lib/mcp-share.ts +25 -0
- package/extensions/lib/mcp-status.ts +34 -0
- package/extensions/lib/memory.ts +41 -6
- package/extensions/lib/model-facts.generated.json +2480 -0
- package/extensions/lib/model-facts.ts +162 -0
- package/extensions/lib/model-policy.ts +145 -189
- package/extensions/lib/model-tier.ts +480 -64
- package/extensions/lib/model-unusable.ts +37 -0
- package/extensions/lib/notifications.ts +320 -1
- package/extensions/lib/one-code-settings.ts +63 -0
- package/extensions/lib/original-command.ts +71 -0
- package/extensions/lib/package-version.ts +31 -0
- package/extensions/lib/paths.ts +39 -4
- package/extensions/lib/permission-gate.ts +192 -0
- package/extensions/lib/persisted-output.ts +18 -0
- package/extensions/lib/pi-ai-estimate.ts +160 -0
- package/extensions/lib/pi-version.ts +1 -1
- package/extensions/lib/plan-mode-channels.ts +11 -0
- package/extensions/lib/plugin-overrides.ts +24 -0
- package/extensions/lib/plugin-root.ts +44 -0
- package/extensions/lib/plugins.ts +233 -54
- package/extensions/lib/prefix-warm-gate.ts +157 -0
- package/extensions/lib/process-tree.ts +56 -0
- package/extensions/lib/reminders.ts +321 -68
- package/extensions/lib/scratchpad.ts +11 -8
- package/extensions/lib/session-lifecycle.ts +28 -0
- package/extensions/lib/settings-channels.ts +14 -0
- package/extensions/lib/shell-tasks.ts +54 -0
- package/extensions/lib/skill-overrides.ts +86 -0
- package/extensions/lib/skill-scan.ts +110 -0
- package/extensions/lib/text-width.ts +267 -0
- package/extensions/lib/tui-render.ts +281 -54
- package/extensions/lib/usage-bus.ts +75 -0
- package/extensions/lib/usage-tracker.ts +67 -0
- package/extensions/lib/worktree-isolation.ts +170 -0
- package/extensions/lsp/client.ts +69 -12
- package/extensions/lsp/format.ts +12 -2
- package/extensions/lsp/index.ts +273 -84
- package/extensions/lsp/install-hints.ts +43 -0
- package/extensions/lsp/keep-alive.ts +19 -0
- package/extensions/lsp/plugin-servers.ts +331 -0
- package/extensions/lsp/watcher.ts +157 -0
- package/extensions/mcp/announce.ts +99 -0
- package/extensions/mcp/client.ts +181 -22
- package/extensions/mcp/config.ts +55 -24
- package/extensions/mcp/index.ts +586 -86
- package/extensions/mcp/oauth/browser.ts +26 -0
- package/extensions/mcp/oauth/callback.ts +104 -0
- package/extensions/mcp/oauth/flow.ts +68 -0
- package/extensions/mcp/oauth/provider.ts +147 -0
- package/extensions/mcp/oauth/store.ts +86 -0
- package/extensions/mcp/panel/keys.ts +34 -0
- package/extensions/mcp/panel/model.ts +62 -0
- package/extensions/mcp/panel/render.ts +141 -0
- package/extensions/mcp/panel/state.ts +108 -0
- package/extensions/mcp/schema.ts +83 -6
- package/extensions/mcp/trust.ts +340 -0
- package/extensions/memory/entries.ts +163 -0
- package/extensions/memory/index.ts +119 -2
- package/extensions/memory/open-external.ts +97 -0
- package/extensions/memory/panel.ts +124 -0
- package/extensions/model-default/index.ts +74 -0
- package/extensions/notebook/index.ts +36 -18
- package/extensions/notebook/notebook.ts +110 -17
- package/extensions/permissions/index.ts +1012 -247
- package/extensions/permissions/matcher.ts +488 -57
- package/extensions/permissions/modes.ts +55 -7
- package/extensions/permissions/project-trust.ts +81 -0
- package/extensions/permissions/protected-paths.ts +16 -3
- package/extensions/permissions/rule-prose.ts +79 -0
- package/extensions/permissions/session-grant.ts +91 -0
- package/extensions/permissions/settings.ts +112 -33
- package/extensions/permissions/subagent-gate.ts +57 -0
- package/extensions/plan-mode/index.ts +143 -33
- package/extensions/plan-mode/reminder.ts +16 -19
- package/extensions/plan-mode/viewer.ts +40 -31
- package/extensions/plugins/counts.ts +68 -0
- package/extensions/plugins/index.ts +496 -32
- package/extensions/plugins/install/install.ts +167 -0
- package/extensions/plugins/install/paths.ts +23 -0
- package/extensions/plugins/install/registry.ts +72 -0
- package/extensions/plugins/marketplace/git.ts +103 -0
- package/extensions/plugins/marketplace/official.ts +36 -0
- package/extensions/plugins/marketplace/parse.ts +94 -0
- package/extensions/plugins/marketplace/registry.ts +60 -0
- package/extensions/plugins/marketplace/sync.ts +146 -0
- package/extensions/plugins/marketplace/types.ts +128 -0
- package/extensions/plugins/panel/keys.ts +52 -0
- package/extensions/plugins/panel/render.ts +315 -0
- package/extensions/plugins/panel/rows.ts +230 -0
- package/extensions/plugins/panel/state.ts +333 -0
- package/extensions/recap/index.ts +215 -0
- package/extensions/recap/prompt.ts +44 -0
- package/extensions/recap/scheduler.ts +87 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/search-tools/tools.ts +7 -5
- package/extensions/skill/index.ts +360 -34
- package/extensions/skill/invoke.ts +185 -0
- package/extensions/skill/listing.ts +21 -0
- package/extensions/skill/panel/keys.ts +41 -0
- package/extensions/skill/panel/render.ts +91 -0
- package/extensions/skill/panel/state.ts +115 -0
- package/extensions/spinner/index.ts +110 -0
- package/extensions/spinner/line.ts +51 -0
- package/extensions/spinner/verbs.ts +201 -0
- package/extensions/subagents/agents.ts +88 -44
- package/extensions/subagents/default-model.ts +62 -32
- package/extensions/subagents/delegation-steer.ts +23 -0
- package/extensions/subagents/hand-back-review.ts +67 -0
- package/extensions/subagents/index.ts +1545 -346
- package/extensions/subagents/live-runs.ts +293 -0
- package/extensions/subagents/model-select.ts +252 -82
- package/extensions/subagents/outcome.ts +90 -0
- package/extensions/subagents/panel-keys.ts +41 -0
- package/extensions/subagents/panel-render.ts +357 -0
- package/extensions/subagents/panel-widget.ts +330 -0
- package/extensions/subagents/pending-claim.ts +45 -0
- package/extensions/subagents/prose.ts +105 -0
- package/extensions/subagents/runner.ts +674 -0
- package/extensions/subagents/runs.ts +68 -1
- package/extensions/subagents/send-to-main-tool.ts +55 -0
- package/extensions/subagents/session-turns.ts +138 -0
- package/extensions/subagents/shell-panel.ts +198 -0
- package/extensions/subagents/usage.ts +16 -4
- package/extensions/subagents/worktree.ts +13 -0
- package/extensions/system-prompt/environment.ts +5 -3
- package/extensions/system-prompt/git-status.ts +93 -0
- package/extensions/system-prompt/index.ts +23 -1
- package/extensions/system-prompt/template.ts +34 -4
- package/extensions/system-prompt/tiers/common.ts +31 -6
- package/extensions/system-prompt/tiers/low.ts +13 -3
- package/extensions/system-prompt/tiers/mid.ts +21 -8
- package/extensions/system-reminder/index.ts +53 -9
- package/extensions/tasks/index.ts +65 -16
- package/extensions/tasks/store.ts +76 -3
- package/extensions/tool-search/announce.ts +42 -0
- package/extensions/tool-search/index.ts +136 -13
- package/extensions/turn-duration/index.ts +60 -0
- package/extensions/turn-duration/line.ts +22 -0
- package/extensions/turn-duration/span.ts +41 -0
- package/extensions/turn-duration/verbs.ts +24 -0
- package/extensions/web/backends.ts +421 -0
- package/extensions/web/index.ts +178 -21
- package/extensions/web-fetch/index.ts +74 -18
- package/extensions/web-fetch/summarize.ts +1 -1
- package/extensions/workflow/agent-session.ts +182 -112
- package/extensions/workflow/globals.ts +129 -106
- package/extensions/workflow/index.ts +343 -28
- package/extensions/workflow/records.ts +118 -0
- package/extensions/workflow/run-manager.ts +80 -31
- package/extensions/workflow/saved-workflows.ts +2 -1
- package/extensions/workflow/script-worker.mjs +110 -0
- package/extensions/workflow/types.ts +74 -8
- package/extensions/workflow/viewer.ts +562 -0
- package/extensions/workflow/vm-runtime.ts +118 -22
- package/extensions/workflow/widget.ts +126 -18
- package/extensions/worktree/guards.ts +240 -0
- package/extensions/worktree/index.ts +30 -5
- package/extensions/worktree/rewrite.ts +32 -16
- package/package.json +20 -10
- package/skills/code-review/SKILL.md +165 -0
- package/skills/fewer-permission-prompts/SKILL.md +61 -0
- package/skills/security-review/SKILL.md +178 -0
- package/skills/simplify/SKILL.md +70 -0
- package/themes/{one-code-light.json → onecode-light.json} +6 -1
- package/themes/{one-code.json → onecode.json} +6 -1
- package/types/app.d.ts +8 -1
- package/types/community-packages.d.ts +45 -2
- package/docs/decisions/auto-mode.md +0 -526
- package/docs/decisions/branding.md +0 -127
- package/docs/decisions/code-review-remediation.md +0 -106
- package/docs/decisions/compaction.md +0 -140
- package/docs/decisions/distribution.md +0 -168
- package/docs/decisions/hooks.md +0 -64
- package/docs/decisions/lsp.md +0 -58
- package/docs/decisions/mcp.md +0 -104
- package/docs/decisions/memory-state.md +0 -170
- package/docs/decisions/model-policy.md +0 -126
- package/docs/decisions/modes.md +0 -178
- package/docs/decisions/skills-plugins.md +0 -56
- package/docs/decisions/subagents-workflows.md +0 -225
- package/docs/decisions/system-prompt.md +0 -98
- package/docs/decisions/tools.md +0 -407
- package/docs/decisions/tui.md +0 -202
- package/docs/decisions.md +0 -148
- package/extensions/subagents/child.ts +0 -414
- package/extensions/subagents/rpc-turns.ts +0 -123
- package/extensions/todo/index.ts +0 -133
- package/extensions/workflow/permission-gate.ts +0 -71
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RPC child protocol state (pure) — line-wise parsing for a `pi --mode rpc`
|
|
3
|
-
* child. RPC events are the same objects json mode emits (shared toJsonEvent),
|
|
4
|
-
* interleaved with `response` acks and `extension_ui_request` dialogs. This
|
|
5
|
-
* tracker accumulates per-turn output and cumulative stats, and tells the
|
|
6
|
-
* caller when it must act (answer a UI request, settle a pending turn).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { type ChildAction, recordAction } from "../auto-mode/actions.ts";
|
|
10
|
-
import { addUsage, emptyUsage, type UsageTotals } from "./usage.ts";
|
|
11
|
-
|
|
12
|
-
export type RpcAction =
|
|
13
|
-
| { kind: "ui_request"; id: string }
|
|
14
|
-
| { kind: "stream_start" }
|
|
15
|
-
| { kind: "turn_end" }
|
|
16
|
-
| { kind: "progress" }
|
|
17
|
-
| { kind: "message_to_main"; message: string; summary?: string };
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* A child's send_message {to: "main"} call needs no IPC of its own: the tool
|
|
21
|
-
* returns the message in its details, and the parent already reads every
|
|
22
|
-
* tool_execution_end in the child's event stream. This extracts it.
|
|
23
|
-
*/
|
|
24
|
-
export function toMainMessage(event: unknown): { message: string; summary?: string } | undefined {
|
|
25
|
-
const e = event as {
|
|
26
|
-
type?: string;
|
|
27
|
-
toolName?: string;
|
|
28
|
-
result?: { details?: { toMain?: boolean; message?: unknown; summary?: unknown } };
|
|
29
|
-
};
|
|
30
|
-
if (e?.type !== "tool_execution_end" || e.toolName !== "send_message") return undefined;
|
|
31
|
-
const details = e.result?.details;
|
|
32
|
-
if (!details?.toMain || typeof details.message !== "string") return undefined;
|
|
33
|
-
return { message: details.message, summary: typeof details.summary === "string" ? details.summary : undefined };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export class RpcTurnTracker {
|
|
37
|
-
toolCalls = 0;
|
|
38
|
-
/** What the child did this turn, for auto mode's return review. */
|
|
39
|
-
actions: ChildAction[] = [];
|
|
40
|
-
readonly usage: UsageTotals = emptyUsage();
|
|
41
|
-
/** Final assistant text of the turn in progress (or the last finished one). */
|
|
42
|
-
turnText = "";
|
|
43
|
-
/**
|
|
44
|
-
* Set when the turn's LAST assistant message ended with stopReason "error"
|
|
45
|
-
* (provider failure: auth, billing, rate limit) — the only place such a
|
|
46
|
-
* failure surfaces; stderr stays empty. A later successful assistant
|
|
47
|
-
* message (pi retried) clears it.
|
|
48
|
-
*/
|
|
49
|
-
providerError: string | undefined;
|
|
50
|
-
/** Every turn's final text, for task_output on a resident agent. */
|
|
51
|
-
transcript = "";
|
|
52
|
-
/** A prompt has been sent and its agent_end not yet seen. */
|
|
53
|
-
busy = false;
|
|
54
|
-
/**
|
|
55
|
-
* The child's agent loop is actually running (its agent_start seen). Between
|
|
56
|
-
* a prompt being sent and this flipping true, the child is still booting —
|
|
57
|
-
* a steer sent in that window would miss the turn.
|
|
58
|
-
*/
|
|
59
|
-
streaming = false;
|
|
60
|
-
|
|
61
|
-
beginTurn(): void {
|
|
62
|
-
this.busy = true;
|
|
63
|
-
this.turnText = "";
|
|
64
|
-
this.providerError = undefined;
|
|
65
|
-
// Per turn, not per session: the review that reads this judges the work of
|
|
66
|
-
// the turn that just finished.
|
|
67
|
-
this.actions = [];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
process(line: string): RpcAction | undefined {
|
|
71
|
-
if (!line.trim()) return undefined;
|
|
72
|
-
let event: {
|
|
73
|
-
type?: string;
|
|
74
|
-
id?: string;
|
|
75
|
-
message?: { role?: string; content?: unknown; usage?: unknown; stopReason?: string; errorMessage?: string };
|
|
76
|
-
};
|
|
77
|
-
try {
|
|
78
|
-
event = JSON.parse(line);
|
|
79
|
-
} catch {
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
switch (event.type) {
|
|
84
|
-
case "extension_ui_request":
|
|
85
|
-
return typeof event.id === "string" ? { kind: "ui_request", id: event.id } : undefined;
|
|
86
|
-
case "agent_start":
|
|
87
|
-
this.streaming = true;
|
|
88
|
-
return { kind: "stream_start" };
|
|
89
|
-
case "tool_execution_start": {
|
|
90
|
-
this.toolCalls++;
|
|
91
|
-
const start = event as { toolName?: string; args?: unknown };
|
|
92
|
-
if (start.toolName) recordAction(this.actions, start.toolName, start.args);
|
|
93
|
-
return { kind: "progress" };
|
|
94
|
-
}
|
|
95
|
-
case "message_end": {
|
|
96
|
-
if (event.message?.role !== "assistant") return undefined;
|
|
97
|
-
addUsage(this.usage, event.message.usage);
|
|
98
|
-
this.providerError =
|
|
99
|
-
event.message.stopReason === "error" ? event.message.errorMessage || "unknown provider error" : undefined;
|
|
100
|
-
const blocks = Array.isArray(event.message.content) ? event.message.content : [];
|
|
101
|
-
const text = blocks
|
|
102
|
-
.filter((b): b is { type: string; text: string } => (b as { type?: string }).type === "text")
|
|
103
|
-
.map((b) => b.text)
|
|
104
|
-
.join("");
|
|
105
|
-
if (text.trim()) this.turnText = text;
|
|
106
|
-
return { kind: "progress" };
|
|
107
|
-
}
|
|
108
|
-
case "tool_execution_end": {
|
|
109
|
-
const toMain = toMainMessage(event);
|
|
110
|
-
return toMain ? { kind: "message_to_main", ...toMain } : undefined;
|
|
111
|
-
}
|
|
112
|
-
case "agent_end":
|
|
113
|
-
this.busy = false;
|
|
114
|
-
this.streaming = false;
|
|
115
|
-
if (this.turnText.trim()) {
|
|
116
|
-
this.transcript = this.transcript ? `${this.transcript}\n\n---\n\n${this.turnText}` : this.turnText;
|
|
117
|
-
}
|
|
118
|
-
return { kind: "turn_end" };
|
|
119
|
-
default:
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
package/extensions/todo/index.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* todo extension — Claude Code's TodoWrite: stateless whole-list replacement.
|
|
3
|
-
*
|
|
4
|
-
* Schema matches Claude Code (content / status / activeForm per item). State
|
|
5
|
-
* lives in tool-result details, so branching and resuming a session restores
|
|
6
|
-
* the list that was current at that point in history (pattern from pi's
|
|
7
|
-
* official todo example). A widget under the editor shows live progress.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { StringEnum } from "@earendil-works/pi-ai";
|
|
11
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import { Type } from "typebox";
|
|
13
|
-
import { restoreLatestDetails } from "../lib/branch-restore.ts";
|
|
14
|
-
import { REMINDER_CHANNEL } from "../lib/reminders.ts";
|
|
15
|
-
import { ccToolRenderers } from "../lib/tui-render.ts";
|
|
16
|
-
|
|
17
|
-
interface TodoItem {
|
|
18
|
-
content: string;
|
|
19
|
-
status: "pending" | "in_progress" | "completed";
|
|
20
|
-
activeForm: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface TodoDetails {
|
|
24
|
-
todos: TodoItem[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const TodoWriteParams = Type.Object({
|
|
28
|
-
todos: Type.Array(
|
|
29
|
-
Type.Object({
|
|
30
|
-
content: Type.String({ description: "The task, in imperative form (e.g. 'Fix the login bug')" }),
|
|
31
|
-
status: StringEnum(["pending", "in_progress", "completed"] as const),
|
|
32
|
-
activeForm: Type.String({ description: "Present-continuous form shown while in progress (e.g. 'Fixing the login bug')" }),
|
|
33
|
-
}),
|
|
34
|
-
{ description: "The complete todo list; this REPLACES the previous list entirely" },
|
|
35
|
-
),
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
function formatTodos(todos: TodoItem[]): string {
|
|
39
|
-
if (todos.length === 0) return "Todo list cleared.";
|
|
40
|
-
const mark = (t: TodoItem) => (t.status === "completed" ? "x" : t.status === "in_progress" ? "▸" : " ");
|
|
41
|
-
return todos.map((t) => `[${mark(t)}] ${t.content}`).join("\n");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Claude Code nudges the model when the task tools have gone unused for a while.
|
|
46
|
-
* A todo tool nobody remembers to call is decoration, so we do the same — quietly
|
|
47
|
-
* and rarely, based on session state.
|
|
48
|
-
*/
|
|
49
|
-
const NUDGE_AFTER_TURNS = 8;
|
|
50
|
-
const TODO_TOOLS = new Set(["todo_write"]);
|
|
51
|
-
|
|
52
|
-
export default function todoExtension(pi: ExtensionAPI) {
|
|
53
|
-
let todos: TodoItem[] = [];
|
|
54
|
-
let turnsSinceTodoUse = 0;
|
|
55
|
-
|
|
56
|
-
const updateWidget = (ctx: ExtensionContext) => {
|
|
57
|
-
if (!ctx.hasUI) return;
|
|
58
|
-
if (todos.length === 0) {
|
|
59
|
-
ctx.ui.setWidget("cc-todos", undefined);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const done = todos.filter((t) => t.status === "completed").length;
|
|
63
|
-
const active = todos.find((t) => t.status === "in_progress");
|
|
64
|
-
const line = ` todos ${done}/${todos.length}${active ? ` · ${active.activeForm}` : ""}`;
|
|
65
|
-
ctx.ui.setWidget("cc-todos", [line]);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const reconstructState = (ctx: ExtensionContext) => {
|
|
69
|
-
const details = restoreLatestDetails<TodoDetails>(ctx.sessionManager.getBranch(), TODO_TOOLS, (d) => Boolean(d?.todos));
|
|
70
|
-
todos = details?.todos ?? [];
|
|
71
|
-
updateWidget(ctx);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
pi.on("session_start", (_event, ctx) => reconstructState(ctx));
|
|
75
|
-
pi.on("session_tree", (_event, ctx) => reconstructState(ctx));
|
|
76
|
-
|
|
77
|
-
pi.on("turn_end", () => {
|
|
78
|
-
turnsSinceTodoUse++;
|
|
79
|
-
if (turnsSinceTodoUse < NUDGE_AFTER_TURNS) return;
|
|
80
|
-
turnsSinceTodoUse = 0;
|
|
81
|
-
|
|
82
|
-
if (todos.length === 0) {
|
|
83
|
-
pi.events.emit(REMINDER_CHANNEL, {
|
|
84
|
-
text: "The todo tool has not been used recently. If the current work has several steps, track it with todo_write so progress is visible; skip this if the task is simple.",
|
|
85
|
-
});
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const active = todos.filter((t) => t.status === "in_progress");
|
|
90
|
-
const done = todos.filter((t) => t.status === "completed").length;
|
|
91
|
-
if (active.length === 0 && done < todos.length) {
|
|
92
|
-
pi.events.emit(REMINDER_CHANNEL, {
|
|
93
|
-
text: `The todo list has ${todos.length - done} unfinished item(s) and none marked in_progress. Update it with todo_write to reflect what you are actually doing, or clear it if it is stale.`,
|
|
94
|
-
});
|
|
95
|
-
} else if (active.length > 1) {
|
|
96
|
-
pi.events.emit(REMINDER_CHANNEL, {
|
|
97
|
-
text: `${active.length} todo items are marked in_progress. Exactly one should be in progress at a time — update the list with todo_write.`,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
pi.registerTool({
|
|
103
|
-
name: "todo_write",
|
|
104
|
-
label: "Todos",
|
|
105
|
-
...ccToolRenderers<{ todos?: unknown[] }>("Todos", {
|
|
106
|
-
title: (a) => (a?.todos ? `${a.todos.length} items` : undefined),
|
|
107
|
-
maxCollapsedLines: 12,
|
|
108
|
-
}),
|
|
109
|
-
description:
|
|
110
|
-
"Create and manage a structured task list for the current session. Each call REPLACES the entire list. Use for multi-step tasks (3+ steps): mark exactly one item in_progress before starting it, mark it completed immediately when done, and keep the list current rather than batching updates.",
|
|
111
|
-
promptSnippet: "Track multi-step task progress with a todo list",
|
|
112
|
-
promptGuidelines: [
|
|
113
|
-
"For tasks with 3+ steps, track progress with todo_write; keep exactly one item in_progress",
|
|
114
|
-
],
|
|
115
|
-
parameters: TodoWriteParams,
|
|
116
|
-
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
117
|
-
todos = params.todos as TodoItem[];
|
|
118
|
-
turnsSinceTodoUse = 0;
|
|
119
|
-
updateWidget(ctx);
|
|
120
|
-
return {
|
|
121
|
-
content: [{ type: "text", text: formatTodos(todos) }],
|
|
122
|
-
details: { todos: [...todos] } satisfies TodoDetails,
|
|
123
|
-
};
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
pi.registerCommand("todos", {
|
|
128
|
-
description: "Show the agent's current todo list",
|
|
129
|
-
handler: async (_args, ctx) => {
|
|
130
|
-
ctx.ui.notify(todos.length ? formatTodos(todos) : "No todos.", "info");
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Permission gate for workflow subagents (pure).
|
|
3
|
-
*
|
|
4
|
-
* Workflow subagent sessions are built with `noExtensions: true`, so One Code's
|
|
5
|
-
* own permissions extension never loads inside them — without this gate every
|
|
6
|
-
* subagent bash/edit/write would run unchecked. `DefaultResourceLoader` always
|
|
7
|
-
* loads explicitly passed `extensionFactories` (even under noExtensions),
|
|
8
|
-
* which is where this inline factory attaches.
|
|
9
|
-
*
|
|
10
|
-
* Policy: deny rules always win; explicit allow rules allow; edits are
|
|
11
|
-
* auto-allowed (Claude Code runs workflow subagents in acceptEdits); anything
|
|
12
|
-
* that would normally *ask* is denied — there is no interactive prompt inside
|
|
13
|
-
* an agent() call, and fail-closed beats silently trusting the model.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import type { InlineExtension } from "@earendil-works/pi-coding-agent";
|
|
17
|
-
import { findGitRoot } from "../lib/git.ts";
|
|
18
|
-
import { memoryDir } from "../lib/memory.ts";
|
|
19
|
-
import { sessionScratchpadDir } from "../lib/scratchpad.ts";
|
|
20
|
-
import { decide, extractSubject, normalizeToolName, parseRules } from "../permissions/matcher.ts";
|
|
21
|
-
import { loadPermissionSettings } from "../permissions/settings.ts";
|
|
22
|
-
|
|
23
|
-
/** Tools the workflow runtime itself injects into subagents; never gate them. */
|
|
24
|
-
const WORKFLOW_INTERNAL_TOOLS = new Set(["structured_output"]);
|
|
25
|
-
|
|
26
|
-
export function permissionGateFactory(cwd: string, home: string): InlineExtension {
|
|
27
|
-
const settings = loadPermissionSettings(cwd, home);
|
|
28
|
-
const deny = parseRules(settings.deny);
|
|
29
|
-
const allow = parseRules(settings.allow);
|
|
30
|
-
const mode = settings.defaultMode === "bypassPermissions" ? "bypassPermissions" : "acceptEdits";
|
|
31
|
-
// Memory writes work inside agent() calls too — otherwise the protected
|
|
32
|
-
// `.claude` check turns them into "needs interactive approval" and the
|
|
33
|
-
// harness blocks its own feature (same rationale as in decide()).
|
|
34
|
-
const memoryDirPath = memoryDir(home, findGitRoot(cwd) ?? cwd);
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
name: "workflow-permission-gate",
|
|
38
|
-
hidden: true,
|
|
39
|
-
factory: (pi) => {
|
|
40
|
-
// The scratchpad embeds the *child's* session id, which does not exist
|
|
41
|
-
// until the session runs — derived on first tool call, then pinned.
|
|
42
|
-
let scratchpadDirPath: string | undefined;
|
|
43
|
-
pi.on("tool_call", (event, ctx) => {
|
|
44
|
-
if (WORKFLOW_INTERNAL_TOOLS.has(event.toolName)) return undefined;
|
|
45
|
-
const sessionId = ctx?.sessionManager?.getSessionId?.();
|
|
46
|
-
if (!scratchpadDirPath && sessionId) scratchpadDirPath = sessionScratchpadDir(cwd, sessionId);
|
|
47
|
-
const subject = extractSubject(normalizeToolName(event.toolName), event.input as Record<string, unknown>);
|
|
48
|
-
const result = decide({
|
|
49
|
-
toolName: event.toolName,
|
|
50
|
-
subject,
|
|
51
|
-
cwd,
|
|
52
|
-
mode,
|
|
53
|
-
deny,
|
|
54
|
-
ask: [],
|
|
55
|
-
allow,
|
|
56
|
-
memoryDirPath,
|
|
57
|
-
scratchpadDirPath,
|
|
58
|
-
});
|
|
59
|
-
if (result.decision === "allow") return undefined;
|
|
60
|
-
const ruleNote = result.rule ? ` (rule: ${result.rule.raw})` : "";
|
|
61
|
-
return {
|
|
62
|
-
block: true,
|
|
63
|
-
reason:
|
|
64
|
-
result.decision === "deny"
|
|
65
|
-
? `Denied by permission rules${ruleNote}.`
|
|
66
|
-
: "This action needs interactive approval, which is not available inside a workflow agent. Ask for it to be added to the allow rules, or work around it.",
|
|
67
|
-
};
|
|
68
|
-
});
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
}
|