one-code-extension 0.4.1 → 0.5.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/README.md +16 -5
- package/agents/general-purpose.md +2 -2
- package/docs/guide/README.md +2 -1
- package/docs/guide/bring-your-claude-code-setup.md +60 -9
- package/docs/guide/configuration.md +48 -14
- package/docs/guide/differences-from-claude-code.md +32 -23
- package/docs/guide/doctor.md +20 -13
- package/docs/guide/hooks.md +18 -3
- package/docs/guide/installation.md +27 -4
- package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
- package/docs/guide/providers-and-models.md +137 -54
- package/docs/guide/reference.md +27 -15
- package/docs/guide/sessions-and-context.md +69 -16
- package/docs/guide/skills-plugins-and-mcp.md +57 -20
- package/docs/guide/subagents-and-workflows.md +95 -29
- package/docs/guide/tasks-and-background-work.md +101 -26
- package/docs/guide/terminal-interface.md +24 -12
- package/docs/guide/tools.md +46 -5
- package/docs/guide/troubleshooting.md +13 -9
- package/docs/guide/windows.md +58 -31
- package/extensions/artifacts/gallery.ts +227 -0
- package/extensions/artifacts/index.ts +288 -0
- package/extensions/artifacts/store.ts +570 -0
- package/extensions/artifacts/viewer.ts +199 -0
- package/extensions/ask-user/description.ts +67 -0
- package/extensions/ask-user/dialogs.ts +80 -0
- package/extensions/ask-user/index.ts +37 -33
- package/extensions/ask-user/questions.ts +32 -7
- package/extensions/ask-user/widget.ts +45 -25
- package/extensions/auto-mode/actions.ts +6 -0
- package/extensions/auto-mode/cache.ts +88 -0
- package/extensions/auto-mode/classifier-prompt.ts +5 -4
- package/extensions/auto-mode/classifier.ts +290 -65
- package/extensions/auto-mode/config.ts +72 -20
- package/extensions/auto-mode/decision-log.ts +2 -0
- package/extensions/auto-mode/defaults.ts +1 -1
- package/extensions/auto-mode/denials.ts +123 -0
- package/extensions/auto-mode/git-checkout-programs.ts +187 -0
- package/extensions/auto-mode/git-status-meta.ts +159 -0
- package/extensions/auto-mode/history.ts +189 -0
- package/extensions/auto-mode/instructions.ts +58 -52
- package/extensions/auto-mode/model-select.ts +159 -134
- package/extensions/auto-mode/paths.ts +57 -5
- package/extensions/auto-mode/pause.ts +57 -2
- package/extensions/auto-mode/prompt.ts +37 -21
- package/extensions/auto-mode/read-only-options.ts +724 -0
- package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
- package/extensions/auto-mode/rules.ts +18 -1
- package/extensions/auto-mode/safety-floor.ts +361 -18
- package/extensions/auto-mode/setup-run.ts +20 -14
- package/extensions/auto-mode/shell-analysis.ts +764 -490
- package/extensions/auto-mode/shell-parse.ts +885 -0
- package/extensions/auto-mode/system-note.ts +33 -0
- package/extensions/auto-mode/transcript.ts +80 -51
- package/extensions/background/cron.ts +515 -0
- package/extensions/background/index.ts +514 -236
- package/extensions/background/loop-fire.ts +136 -0
- package/extensions/background/loop-preamble.ts +59 -0
- package/extensions/background/loop-skill.ts +163 -0
- package/extensions/background/monitor-batch.ts +53 -0
- package/extensions/background/noop-fold.ts +128 -0
- package/extensions/background/switch-guard.ts +52 -0
- package/extensions/background/wakeup.ts +159 -55
- package/extensions/bash/background.ts +53 -17
- package/extensions/bash/child.ts +7 -0
- package/extensions/bash/description.ts +188 -0
- package/extensions/bash/guards.ts +15 -67
- package/extensions/bash/index.ts +88 -35
- package/extensions/branding/compaction-keep.ts +63 -0
- package/extensions/branding/declined.ts +22 -0
- package/extensions/branding/index.ts +133 -9
- package/extensions/branding/prompt-editor.ts +53 -6
- package/extensions/branding/queued-edit.ts +31 -0
- package/extensions/branding/replaced-builtins.ts +68 -0
- package/extensions/branding/startup.ts +33 -71
- package/extensions/btw/index.ts +229 -71
- package/extensions/btw/panel.ts +228 -100
- package/extensions/btw/prompt.ts +41 -4
- package/extensions/claude-compat/index.ts +7 -11
- package/extensions/claude-context/index.ts +446 -58
- package/extensions/compaction/cut.ts +157 -0
- package/extensions/compaction/index.ts +216 -75
- package/extensions/compaction/kept-thinking.ts +62 -0
- package/extensions/compaction/prompt.ts +15 -12
- package/extensions/context-budget/index.ts +24 -5
- package/extensions/context-management/index.ts +25 -0
- package/extensions/doctor/build.ts +59 -8
- package/extensions/doctor/builtins.ts +84 -0
- package/extensions/doctor/cli.ts +7 -1
- package/extensions/doctor/compat.ts +88 -23
- package/extensions/doctor/dependencies.ts +41 -5
- package/extensions/doctor/fix-prompt.ts +9 -2
- package/extensions/doctor/index.ts +102 -58
- package/extensions/doctor/models.ts +102 -63
- package/extensions/doctor/presets.ts +42 -27
- package/extensions/doctor/report.ts +19 -2
- package/extensions/doctor/viewer.ts +49 -27
- package/extensions/effort/index.ts +72 -19
- package/extensions/effort/settings.ts +21 -0
- package/extensions/effort/slider.ts +24 -20
- package/extensions/exit/index.ts +32 -1
- package/extensions/file-tracker/index.ts +337 -26
- package/extensions/file-tracker/replay.ts +96 -10
- package/extensions/file-tracker/restore.ts +63 -0
- package/extensions/file-tracker/results.ts +66 -0
- package/extensions/file-tracker/shell-reads.ts +211 -0
- package/extensions/file-tracker/tracker.ts +32 -15
- package/extensions/footer/footer-line.ts +45 -24
- package/extensions/footer/index.ts +47 -2
- package/extensions/footer/pr.ts +34 -5
- package/extensions/hooks/compaction.ts +7 -0
- package/extensions/hooks/executor.ts +32 -30
- package/extensions/hooks/index.ts +198 -31
- package/extensions/hooks/matcher.ts +3 -0
- package/extensions/hooks/protocol.ts +41 -3
- package/extensions/hooks/settings.ts +50 -19
- package/extensions/hooks/subagent-bridge.ts +2 -0
- package/extensions/hooks/trust.ts +13 -5
- package/extensions/idle-compact/index.ts +119 -0
- package/extensions/idle-compact/policy.ts +143 -0
- package/extensions/init/index.ts +11 -10
- package/extensions/init/prompt.ts +25 -32
- package/extensions/interrupted/index.ts +13 -1
- package/extensions/kept-thinking/index.ts +27 -0
- package/extensions/lib/agent-cron.ts +130 -0
- package/extensions/lib/agent-loader.ts +15 -1
- package/extensions/lib/agent-view.ts +85 -0
- package/extensions/lib/anthropic-payload.ts +82 -1
- package/extensions/lib/anthropic-server-call.ts +230 -0
- package/extensions/lib/anthropic-server-tools.ts +485 -0
- package/extensions/lib/app-launch.mjs +94 -0
- package/extensions/lib/argument-hints.ts +190 -0
- package/extensions/lib/atomic-write.ts +6 -2
- package/extensions/lib/auto-memory.ts +75 -0
- package/extensions/lib/bash-parser.ts +84 -0
- package/extensions/lib/btw-fork.ts +93 -0
- package/extensions/lib/child-extensions.ts +42 -0
- package/extensions/lib/child-writes.ts +45 -0
- package/extensions/lib/claude-context.ts +515 -100
- package/extensions/lib/claude-external-includes.ts +68 -0
- package/extensions/lib/claude-id.ts +22 -0
- package/extensions/lib/claude-rules.ts +323 -0
- package/extensions/lib/claude-settings.ts +76 -4
- package/extensions/lib/compaction-boundary.ts +36 -0
- package/extensions/lib/compaction-keep.mjs +83 -0
- package/extensions/lib/config-mode.ts +112 -0
- package/extensions/lib/consent-dialogs.ts +63 -0
- package/extensions/lib/consent-preview.ts +5 -1
- package/extensions/lib/consent-stores.ts +48 -0
- package/extensions/lib/context-facts.ts +78 -0
- package/extensions/lib/context-stack.ts +81 -0
- package/extensions/lib/deferred.ts +219 -25
- package/extensions/lib/dock-rows.ts +60 -0
- package/extensions/lib/empty-tool-result.ts +27 -0
- package/extensions/lib/environment-block.ts +76 -0
- package/extensions/lib/fetch-timeout.ts +24 -6
- package/extensions/{system-prompt → lib}/git-status.ts +51 -13
- package/extensions/lib/git.ts +75 -13
- package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
- package/extensions/lib/grammars/README.md +2 -0
- package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
- package/extensions/lib/guide-docs.ts +45 -0
- package/extensions/lib/headless-output.ts +46 -0
- package/extensions/lib/hook-gate.ts +7 -7
- package/extensions/lib/interrupt.ts +20 -4
- package/extensions/lib/key-input.ts +455 -0
- package/extensions/lib/keys.ts +14 -1
- package/extensions/lib/local-command.ts +47 -13
- package/extensions/lib/mcp-overrides.ts +1 -1
- package/extensions/lib/mcp-share.ts +46 -0
- package/extensions/lib/memory-content.ts +146 -0
- package/extensions/lib/memory-index.ts +18 -0
- package/extensions/lib/memory.ts +134 -123
- package/extensions/lib/model-catalog/huggingface.json +174 -0
- package/extensions/lib/model-catalog/models-dev.json +18297 -0
- package/extensions/lib/model-catalog/openrouter.json +5252 -0
- package/extensions/lib/model-catalog-data.ts +513 -0
- package/extensions/lib/model-catalog.ts +462 -0
- package/extensions/lib/model-choice-warnings.ts +137 -0
- package/extensions/lib/model-pairings.ts +152 -0
- package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
- package/extensions/lib/model-policy.ts +112 -15
- package/extensions/lib/model-tier.ts +283 -355
- package/extensions/lib/model-unusable.ts +1 -1
- package/extensions/lib/newer-model.ts +100 -0
- package/extensions/lib/notices.ts +87 -0
- package/extensions/lib/notifications.ts +308 -64
- package/extensions/lib/one-code-settings.ts +74 -8
- package/extensions/lib/open-browser.ts +63 -0
- package/extensions/lib/openrouter-generation.ts +94 -0
- package/extensions/lib/operation-deadline.ts +30 -0
- package/extensions/lib/original-command.ts +4 -4
- package/extensions/lib/package-version.ts +1 -1
- package/extensions/lib/panel-focus.ts +134 -0
- package/extensions/lib/paths.ts +17 -2
- package/extensions/lib/permission-gate.ts +72 -6
- package/extensions/lib/persisted-output.ts +60 -8
- package/extensions/lib/pi-ai-estimate.ts +19 -3
- package/extensions/lib/pi-version.ts +21 -2
- package/extensions/lib/plan-mode-channels.ts +20 -5
- package/extensions/lib/plugins.ts +26 -17
- package/extensions/lib/powershell-parse-script.ts +208 -0
- package/extensions/lib/powershell-parser.ts +344 -0
- package/extensions/lib/process-tree.ts +131 -8
- package/extensions/lib/prompt-options.ts +82 -0
- package/extensions/lib/queued-delivery.ts +44 -0
- package/extensions/lib/reminders.ts +364 -58
- package/extensions/lib/replaced-builtins.mjs +199 -0
- package/extensions/lib/replay-call.ts +70 -0
- package/extensions/lib/request-replay.ts +439 -0
- package/extensions/lib/scratchpad.ts +81 -14
- package/extensions/lib/send-now-channel.ts +7 -0
- package/extensions/lib/session-model-tier.ts +51 -0
- package/extensions/lib/session-work.ts +64 -0
- package/extensions/lib/settings-channels.ts +4 -4
- package/extensions/lib/shell-exit-semantics.ts +170 -0
- package/extensions/lib/shell-quote.ts +11 -0
- package/extensions/lib/shell-result.ts +70 -0
- package/extensions/lib/shell-spawn.ts +70 -6
- package/extensions/lib/shell-tool.ts +75 -25
- package/extensions/lib/side-call-cache.ts +124 -0
- package/extensions/lib/side-call-run.ts +96 -0
- package/extensions/lib/side-call-usage.ts +38 -0
- package/extensions/lib/side-call.ts +13 -1
- package/extensions/lib/single-flight.ts +18 -0
- package/extensions/lib/skill-body.ts +42 -0
- package/extensions/lib/skill-scan.ts +63 -24
- package/extensions/lib/spill-file.ts +140 -0
- package/extensions/lib/subagent-handback.ts +113 -0
- package/extensions/lib/system-role.ts +156 -0
- package/extensions/lib/terminal-text.ts +114 -0
- package/extensions/lib/text-width.ts +47 -16
- package/extensions/lib/timer-ops.ts +20 -0
- package/extensions/lib/tool-additions.ts +149 -0
- package/extensions/lib/tool-call-corruption.ts +134 -0
- package/extensions/lib/tool-path.ts +66 -0
- package/extensions/lib/tool-variants.ts +69 -0
- package/extensions/lib/tui-render.ts +255 -27
- package/extensions/lib/turn-budget-layout.ts +143 -0
- package/extensions/lib/update-check.mjs +15 -2
- package/extensions/lib/usage-bus.ts +1 -1
- package/extensions/lib/user-denial.ts +38 -0
- package/extensions/lib/workspace-channel.ts +16 -0
- package/extensions/lib/worktree-channel.ts +31 -3
- package/extensions/lib/worktree-isolation.ts +40 -10
- package/extensions/lsp/client.ts +8 -9
- package/extensions/lsp/index.ts +70 -4
- package/extensions/lsp/keep-alive.ts +1 -1
- package/extensions/lsp/trust.ts +119 -0
- package/extensions/lsp/watcher.ts +1 -1
- package/extensions/mcp/announce.ts +87 -5
- package/extensions/mcp/client.ts +161 -48
- package/extensions/mcp/config.ts +132 -8
- package/extensions/mcp/discovery.ts +19 -0
- package/extensions/mcp/index.ts +209 -71
- package/extensions/mcp/oauth/browser.ts +38 -17
- package/extensions/mcp/oauth/flow.ts +25 -17
- package/extensions/mcp/oauth/provider.ts +14 -8
- package/extensions/mcp/oauth/store.ts +34 -16
- package/extensions/mcp/panel/keys.ts +12 -11
- package/extensions/mcp/panel/render.ts +9 -6
- package/extensions/mcp/schema.ts +104 -5
- package/extensions/mcp/trust.ts +151 -67
- package/extensions/memory/entries.ts +23 -14
- package/extensions/memory/index.ts +134 -28
- package/extensions/memory/open-external.ts +33 -18
- package/extensions/memory/panel.ts +135 -26
- package/extensions/newer-model/index.ts +31 -0
- package/extensions/notebook/description.ts +18 -0
- package/extensions/notebook/index.ts +6 -5
- package/extensions/notebook/notebook.ts +1 -1
- package/extensions/permissions/index.ts +1300 -381
- package/extensions/permissions/matcher.ts +425 -74
- package/extensions/permissions/modes.ts +6 -4
- package/extensions/permissions/panel/host.ts +130 -0
- package/extensions/permissions/panel/keys.ts +48 -0
- package/extensions/permissions/panel/render.ts +450 -0
- package/extensions/permissions/panel/state.ts +550 -0
- package/extensions/permissions/powershell-cmdlets.ts +198 -0
- package/extensions/permissions/powershell-paths.ts +197 -0
- package/extensions/permissions/powershell-rules.ts +207 -280
- package/extensions/permissions/powershell-tree.ts +557 -0
- package/extensions/permissions/project-trust.ts +27 -11
- package/extensions/permissions/protected-paths.ts +14 -2
- package/extensions/permissions/session-grant.ts +6 -0
- package/extensions/permissions/settings.ts +166 -31
- package/extensions/permissions/subagent-gate.ts +25 -0
- package/extensions/permissions/workspace.ts +78 -0
- package/extensions/plan-mode/index.ts +156 -69
- package/extensions/plan-mode/reminder.ts +89 -21
- package/extensions/plan-mode/texts.ts +177 -0
- package/extensions/plan-mode/viewer.ts +92 -27
- package/extensions/plugins/counts.ts +3 -3
- package/extensions/plugins/index.ts +61 -46
- package/extensions/plugins/install/install.ts +16 -0
- package/extensions/plugins/marketplace/sync.ts +9 -3
- package/extensions/plugins/marketplace/types.ts +3 -1
- package/extensions/plugins/panel/keys.ts +21 -24
- package/extensions/plugins/panel/render.ts +12 -6
- package/extensions/plugins/shell-expand.ts +107 -0
- package/extensions/plugins/template.ts +32 -15
- package/extensions/powershell/child.ts +7 -0
- package/extensions/powershell/description.ts +38 -6
- package/extensions/powershell/guards.ts +1 -1
- package/extensions/powershell/index.ts +12 -7
- package/extensions/recap/index.ts +65 -60
- package/extensions/recap/prompt.ts +2 -2
- package/extensions/recap/scheduler.ts +3 -5
- package/extensions/search-tools/grep-merge.ts +39 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/send-now/chord.ts +62 -0
- package/extensions/send-now/index.ts +133 -0
- package/extensions/session-title/index.ts +3 -0
- package/extensions/session-title/title.ts +4 -1
- package/extensions/skill/code-review.ts +97 -0
- package/extensions/skill/index.ts +421 -101
- package/extensions/skill/invoke.ts +128 -18
- package/extensions/skill/listing.ts +133 -9
- package/extensions/skill/panel/keys.ts +15 -16
- package/extensions/skill/panel/render.ts +5 -2
- package/extensions/skill/panel/state.ts +12 -1
- package/extensions/subagents/agent-args.ts +39 -0
- package/extensions/subagents/agent-description.ts +132 -0
- package/extensions/subagents/agents.ts +116 -23
- package/extensions/subagents/announce.ts +69 -0
- package/extensions/subagents/default-model.ts +2 -3
- package/extensions/subagents/delegation-steer.ts +4 -4
- package/extensions/subagents/fork-cache.ts +76 -0
- package/extensions/subagents/guide-agent.ts +189 -0
- package/extensions/subagents/hand-back-review.ts +3 -0
- package/extensions/subagents/index.ts +1493 -824
- package/extensions/subagents/live-runs.ts +26 -6
- package/extensions/subagents/model-select.ts +38 -25
- package/extensions/subagents/outcome.ts +2 -2
- package/extensions/subagents/panel-keys.ts +73 -31
- package/extensions/subagents/panel-render.ts +120 -96
- package/extensions/subagents/panel-widget.ts +60 -89
- package/extensions/subagents/runner.ts +108 -80
- package/extensions/subagents/runs.ts +39 -5
- package/extensions/subagents/send-to-main-tool.ts +1 -1
- package/extensions/subagents/session-turns.ts +32 -8
- package/extensions/subagents/shell-panel.ts +36 -178
- package/extensions/subagents/tasks-dialog.ts +170 -0
- package/extensions/subagents/viewport-mirror.ts +95 -0
- package/extensions/subagents/worktree.ts +57 -13
- package/extensions/system-prompt/environment.ts +19 -19
- package/extensions/system-prompt/idle-turn.ts +65 -0
- package/extensions/system-prompt/index.ts +131 -47
- package/extensions/system-prompt/template.ts +24 -50
- package/extensions/system-prompt/tiers/common.ts +23 -64
- package/extensions/system-prompt/tiers/frontier.ts +32 -11
- package/extensions/system-prompt/tiers/low.ts +67 -45
- package/extensions/system-prompt/tiers/mid.ts +96 -38
- package/extensions/system-reminder/index.ts +175 -13
- package/extensions/tasks/descriptions.ts +190 -0
- package/extensions/tasks/index.ts +120 -41
- package/extensions/tasks/store.ts +83 -16
- package/extensions/tool-call-corruption/child.ts +7 -0
- package/extensions/tool-call-corruption/index.ts +237 -0
- package/extensions/tool-search/announce.ts +16 -0
- package/extensions/tool-search/index.ts +189 -41
- package/extensions/tool-style/index.ts +68 -10
- package/extensions/tool-style/write-description.ts +40 -0
- package/extensions/tool-style/write-render.ts +237 -0
- package/extensions/turn-duration/index.ts +7 -3
- package/extensions/turn-duration/line.ts +47 -4
- package/extensions/web/backends.ts +6 -9
- package/extensions/web/description.ts +50 -0
- package/extensions/web/index.ts +118 -53
- package/extensions/web/native-call.ts +15 -0
- package/extensions/web/native-results.ts +41 -0
- package/extensions/web-fetch/description.ts +42 -0
- package/extensions/web-fetch/extract.ts +53 -6
- package/extensions/web-fetch/index.ts +125 -76
- package/extensions/web-fetch/page-content.ts +19 -0
- package/extensions/web-fetch/response.ts +49 -0
- package/extensions/web-fetch/summarize.ts +9 -10
- package/extensions/workflow/agent-session.ts +105 -18
- package/extensions/workflow/args.ts +24 -0
- package/extensions/workflow/description.ts +67 -0
- package/extensions/workflow/globals.ts +17 -3
- package/extensions/workflow/index.ts +145 -162
- package/extensions/workflow/records.ts +1 -0
- package/extensions/workflow/run-manager.ts +29 -6
- package/extensions/workflow/saved-workflows.ts +3 -2
- package/extensions/workflow/types.ts +22 -1
- package/extensions/workflow/viewer.ts +54 -24
- package/extensions/workflow/widget.ts +20 -0
- package/extensions/worktree/descriptions.ts +91 -0
- package/extensions/worktree/guards.ts +316 -54
- package/extensions/worktree/index.ts +121 -39
- package/extensions/worktree/powershell-guards.ts +167 -0
- package/extensions/worktree/rewrite.ts +14 -16
- package/package.json +18 -7
- package/skills/artifact-design/SKILL.md +85 -0
- package/skills/code-review/SKILL.md +20 -20
- package/skills/code-review/cells/arguments-and-flags.md +19 -0
- package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
- package/skills/code-review/cells/high-8-inline.md +122 -0
- package/skills/code-review/cells/low-1-pass-max4.md +27 -0
- package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
- package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
- package/skills/code-review/cells/medium-3x5-verify.md +132 -0
- package/skills/code-review/cells/minimal-prompt.md +32 -0
- package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
- package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
- package/skills/fewer-permission-prompts/SKILL.md +1 -1
- package/skills/loop/SKILL.md +11 -0
- package/skills/security-review/SKILL.md +2 -5
- package/skills/simplify/SKILL.md +7 -5
- package/skills/workflow-authoring/SKILL.md +143 -0
- package/themes/onecode-light.json +1 -1
- package/themes/onecode.json +1 -1
- package/types/app.d.ts +58 -0
- package/types/community-packages.d.ts +26 -1
- package/extensions/auto-mode/recoverability.ts +0 -229
- package/extensions/lib/capability-index.ts +0 -466
- package/extensions/lib/model-facts.generated.json +0 -2675
- package/extensions/lib/model-facts.ts +0 -162
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
18
|
-
import { basename, join } from "node:path";
|
|
18
|
+
import { basename, dirname, join } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
19
20
|
import { parseFrontmatterLoosely } from "../lib/frontmatter.ts";
|
|
20
|
-
import {
|
|
21
|
+
import { projectConfigDir, userConfigDir } from "../lib/config-mode.ts";
|
|
22
|
+
import { SUBAGENT_HANDBACK } from "../lib/subagent-handback.ts";
|
|
21
23
|
import { normalizeToolName } from "../permissions/matcher.ts";
|
|
22
24
|
|
|
23
25
|
export interface AgentDefinition {
|
|
@@ -66,24 +68,65 @@ function collectMarkdownFiles(dir: string, out: string[] = []): string[] {
|
|
|
66
68
|
return out;
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Split one frontmatter tool string the way Claude Code does: on commas and
|
|
73
|
+
* spaces, except inside a rule's parentheses, so `Bash(git push:*), Write` is
|
|
74
|
+
* two entries and `Agent(worker, researcher)` is one.
|
|
75
|
+
*/
|
|
76
|
+
function splitToolSpecs(text: string): string[] {
|
|
77
|
+
const specs: string[] = [];
|
|
78
|
+
let current = "";
|
|
79
|
+
let inParens = false;
|
|
80
|
+
for (const char of text) {
|
|
81
|
+
if (char === "(") inParens = true;
|
|
82
|
+
else if (char === ")") inParens = false;
|
|
83
|
+
else if ((char === "," || /\s/.test(char)) && !inParens) {
|
|
84
|
+
if (current.trim()) specs.push(current.trim());
|
|
85
|
+
current = "";
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
current += char;
|
|
89
|
+
}
|
|
90
|
+
if (current.trim()) specs.push(current.trim());
|
|
91
|
+
return specs;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The tool a spec names. A rule-shaped entry (`Bash(git push:*)`) names its
|
|
96
|
+
* whole tool: Claude Code grants or removes the entire tool for it in an agent
|
|
97
|
+
* file, never only the rule's commands, so `disallowedTools: Bash(git push:*)`
|
|
98
|
+
* removes `bash`. Anything that is not `Name(…)` is taken as a plain name.
|
|
99
|
+
*/
|
|
100
|
+
function specToolName(spec: string): string {
|
|
101
|
+
const open = spec.indexOf("(");
|
|
102
|
+
return open > 0 && spec.endsWith(")") ? spec.slice(0, open) : spec;
|
|
103
|
+
}
|
|
104
|
+
|
|
69
105
|
/**
|
|
70
106
|
* A frontmatter tool list (comma string or YAML list) as pi tool names: Claude
|
|
71
107
|
* Code spellings map through the alias table (`Glob` → `find`, `WebFetch` →
|
|
72
108
|
* `web_fetch`, `Task` → `Agent`, …), pi names and `mcp__*` pass through,
|
|
73
|
-
* duplicates collapse.
|
|
109
|
+
* rule-shaped entries reduce to their tool, duplicates collapse. A list that
|
|
110
|
+
* contains `*` returns `["*"]`; `parseAgentFile` reads that as "all tools".
|
|
111
|
+
* Exported for tests.
|
|
74
112
|
*/
|
|
75
113
|
export function parseToolList(raw: unknown): string[] | undefined {
|
|
76
|
-
const
|
|
77
|
-
typeof raw === "string"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
114
|
+
const entries =
|
|
115
|
+
typeof raw === "string" ? [raw] : Array.isArray(raw) ? raw.filter((t): t is string => typeof t === "string") : undefined;
|
|
116
|
+
const specs = entries?.flatMap(splitToolSpecs);
|
|
117
|
+
if (!specs || specs.length === 0) return undefined;
|
|
118
|
+
if (specs.includes("*")) return ["*"];
|
|
119
|
+
return [...new Set(specs.map((spec) => normalizeToolName(specToolName(spec))))];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* An agent file's `tools` allowlist. Claude Code reads a list containing `*`
|
|
124
|
+
* as "all tools", the same as no list; kept as `["*"]` the allowlist matched
|
|
125
|
+
* no tool and every spawn failed.
|
|
126
|
+
*/
|
|
127
|
+
function parseAllowlist(raw: unknown): string[] | undefined {
|
|
128
|
+
const tools = parseToolList(raw);
|
|
129
|
+
return tools?.includes("*") ? undefined : tools;
|
|
87
130
|
}
|
|
88
131
|
|
|
89
132
|
export function parseAgentFile(path: string, content: string): AgentDefinition | undefined {
|
|
@@ -98,7 +141,7 @@ export function parseAgentFile(path: string, content: string): AgentDefinition |
|
|
|
98
141
|
return {
|
|
99
142
|
name,
|
|
100
143
|
description: typeof fm.description === "string" ? fm.description : "",
|
|
101
|
-
tools:
|
|
144
|
+
tools: parseAllowlist(fm.tools),
|
|
102
145
|
excludeTools: disallowed.length > 0 ? [...new Set(disallowed)] : undefined,
|
|
103
146
|
// "inherit" is Claude Code's way of saying "use the session model".
|
|
104
147
|
model: typeof fm.model === "string" && fm.model !== "inherit" ? fm.model : undefined,
|
|
@@ -109,11 +152,25 @@ export function parseAgentFile(path: string, content: string): AgentDefinition |
|
|
|
109
152
|
|
|
110
153
|
/**
|
|
111
154
|
* Lowest to highest precedence: the catalog bundled with this package, then the
|
|
112
|
-
* user's `~/.claude/agents`, then the project's `.claude/agents
|
|
113
|
-
*
|
|
155
|
+
* user's `~/.claude/agents`, then the project's `.claude/agents` (`~/.onecode`
|
|
156
|
+
* and `.onecode` in independent mode, lib/config-mode.ts). A user or project
|
|
157
|
+
* definition with the same name replaces a bundled one.
|
|
114
158
|
*/
|
|
115
159
|
export function agentDirs(cwd: string, home: string, bundled?: string): string[] {
|
|
116
|
-
return [...(bundled ? [bundled] : []), join(
|
|
160
|
+
return [...(bundled ? [bundled] : []), join(userConfigDir(home), "agents"), join(projectConfigDir(cwd), "agents")];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The agent files the package ships (general-purpose, explore, plan, …). */
|
|
164
|
+
export const BUNDLED_AGENTS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "agents");
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Every agent-file source in rank order, as the Agent tool loads them:
|
|
168
|
+
* bundled, then plugin agents, then the user and project folders. Workflows
|
|
169
|
+
* resolve `agentType` from the same list (the code-defined guide is the Agent
|
|
170
|
+
* tool's alone).
|
|
171
|
+
*/
|
|
172
|
+
export function agentFileSources(cwd: string, home: string, pluginAgentDirs: readonly (string | AgentSource)[]): Array<string | AgentSource> {
|
|
173
|
+
return [BUNDLED_AGENTS_DIR, ...pluginAgentDirs, ...agentDirs(cwd, home)];
|
|
117
174
|
}
|
|
118
175
|
|
|
119
176
|
/** A directory whose agents are exposed as `<namespace>:<name>` (plugins). */
|
|
@@ -122,10 +179,19 @@ export interface AgentSource {
|
|
|
122
179
|
namespace?: string;
|
|
123
180
|
}
|
|
124
181
|
|
|
182
|
+
/** Agents defined in code (`one-code-guide`), taken as they are: never copied, so a lazy prompt stays lazy. */
|
|
183
|
+
export interface CodeAgents {
|
|
184
|
+
agents: readonly AgentDefinition[];
|
|
185
|
+
}
|
|
186
|
+
|
|
125
187
|
/** Later sources override earlier ones on name collisions. */
|
|
126
|
-
export function discoverAgents(sources: Array<string | AgentSource>): AgentDefinition[] {
|
|
188
|
+
export function discoverAgents(sources: Array<string | AgentSource | CodeAgents>): AgentDefinition[] {
|
|
127
189
|
const byName = new Map<string, AgentDefinition>();
|
|
128
190
|
for (const source of sources) {
|
|
191
|
+
if (typeof source !== "string" && "agents" in source) {
|
|
192
|
+
for (const agent of source.agents) byName.set(agent.name, agent);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
129
195
|
const { dir, namespace } = typeof source === "string" ? { dir: source, namespace: undefined } : source;
|
|
130
196
|
for (const file of collectMarkdownFiles(dir)) {
|
|
131
197
|
try {
|
|
@@ -145,11 +211,12 @@ export function discoverAgents(sources: Array<string | AgentSource>): AgentDefin
|
|
|
145
211
|
* Tools a child cannot function without, re-added when an agent file's `tools`
|
|
146
212
|
* allowlist omits them: pi's allowlist filters built-ins, extension tools AND
|
|
147
213
|
* injected custom tools by exact name, so a CC-style `tools: Read, Grep` would
|
|
148
|
-
* otherwise also strip the child's report
|
|
149
|
-
* deferred-tool loader and the runtime's
|
|
150
|
-
* allowlist never strips its own plumbing
|
|
214
|
+
* otherwise also strip the child's report channels (`SendMessage` → main and
|
|
215
|
+
* the final `SubagentHandback`), its deferred-tool loader and the runtime's
|
|
216
|
+
* `structured_output`. Claude Code's allowlist never strips its own plumbing
|
|
217
|
+
* either.
|
|
151
218
|
*/
|
|
152
|
-
const ALLOWLIST_ESSENTIALS = ["SendMessage", "tool_search", "structured_output"];
|
|
219
|
+
const ALLOWLIST_ESSENTIALS = ["SendMessage", SUBAGENT_HANDBACK, "tool_search", "structured_output"];
|
|
153
220
|
|
|
154
221
|
/** Apply an agent's allowlist to pi's `tools` option, keeping the essentials. */
|
|
155
222
|
export function childToolAllowlist(tools: string[] | undefined): string[] | undefined {
|
|
@@ -168,3 +235,29 @@ export function usableAllowlistedTools(registered: string[], allowlist: string[]
|
|
|
168
235
|
const available = new Set(registered);
|
|
169
236
|
return allowlist.filter((name) => available.has(name));
|
|
170
237
|
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* pi's session tool options for a named agent: its allowlist with the child's
|
|
241
|
+
* plumbing re-added, and its denylist. The one builder both runners use (the
|
|
242
|
+
* subagent runner and the workflow runner), so an agent file means the same
|
|
243
|
+
* thing wherever it runs; the workflow runner once passed the raw allowlist and
|
|
244
|
+
* no denylist, which stripped `structured_output` and ignored `disallowedTools`
|
|
245
|
+
* (SUBAGENTS-WORKFLOWS-REVIEW-2026-09-26 H2, H3).
|
|
246
|
+
*/
|
|
247
|
+
export function agentToolOptions(agent: AgentDefinition | undefined): { tools?: string[]; excludeTools?: string[] } {
|
|
248
|
+
return { tools: childToolAllowlist(agent?.tools), excludeTools: agent?.excludeTools };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The fail-loud error for an agent whose `tools` allowlist matched no tool the
|
|
253
|
+
* session registered, or undefined when the agent is usable. pi drops unknown
|
|
254
|
+
* names silently, so without this the agent would run with no tools and
|
|
255
|
+
* "complete" with an excuse.
|
|
256
|
+
*/
|
|
257
|
+
export function unusableAllowlistError(agent: AgentDefinition | undefined, registered: string[]): string | undefined {
|
|
258
|
+
if (!agent?.tools || usableAllowlistedTools(registered, agent.tools).length > 0) return undefined;
|
|
259
|
+
return (
|
|
260
|
+
`Agent "${agent.name}" lists tools (${agent.tools.join(", ")}) that match no available tool. ` +
|
|
261
|
+
"Use Claude Code names (Read, Edit, Write, Bash, Grep, Glob, WebFetch, WebSearch, NotebookEdit, Skill, Agent, SendMessage) or pi names (read, edit, write, bash, grep, find, ls, …) in the agent file's `tools` list."
|
|
262
|
+
);
|
|
263
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** Pure state for resumed-session subagent capability announcements. */
|
|
2
|
+
|
|
3
|
+
export type SubagentCapability = "models" | "agents" | "delegation";
|
|
4
|
+
export interface SubagentBaseline {
|
|
5
|
+
models: string | null;
|
|
6
|
+
agents: string | null;
|
|
7
|
+
delegation: string | null;
|
|
8
|
+
/** Account refusals the narrow tail correction has already communicated. */
|
|
9
|
+
refusedModels: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function emptySubagentBaseline(): SubagentBaseline {
|
|
13
|
+
return { models: null, agents: null, delegation: null, refusedModels: [] };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Stored baseline validation deliberately permits arbitrary reminder text. */
|
|
17
|
+
export function subagentBaseline(value: unknown): SubagentBaseline | undefined {
|
|
18
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
19
|
+
const input = value as Record<string, unknown>;
|
|
20
|
+
const output = emptySubagentBaseline();
|
|
21
|
+
for (const key of ["models", "agents", "delegation"] as const) {
|
|
22
|
+
if (input[key] !== null && typeof input[key] !== "string") return undefined;
|
|
23
|
+
output[key] = (input[key] as string | null | undefined) ?? null;
|
|
24
|
+
}
|
|
25
|
+
// v1 phase-2 snapshots did not carry this field; absence means no known
|
|
26
|
+
// refusal, not malformed state. Keep the list JSON-safe and deterministic.
|
|
27
|
+
if (input.refusedModels !== undefined && (!Array.isArray(input.refusedModels) || !input.refusedModels.every((model) => typeof model === "string"))) return undefined;
|
|
28
|
+
output.refusedModels = [...new Set((input.refusedModels as string[] | undefined) ?? [])];
|
|
29
|
+
return output;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A restored first-prepend block is a usable fallback for snapshots predating structured baselines. */
|
|
33
|
+
export function subagentBaselineFromStack(stack: readonly { key?: string; text: string }[]): SubagentBaseline {
|
|
34
|
+
const baseline = emptySubagentBaseline();
|
|
35
|
+
const keys: Record<SubagentCapability, string> = {
|
|
36
|
+
models: "subagent-models", agents: "subagent-agents", delegation: "subagent-delegation",
|
|
37
|
+
};
|
|
38
|
+
for (const capability of Object.keys(keys) as SubagentCapability[]) {
|
|
39
|
+
const entry = stack.find((item) => item.key === keys[capability]);
|
|
40
|
+
baseline[capability] = entry?.text ?? null;
|
|
41
|
+
}
|
|
42
|
+
return baseline;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type SubagentAnnouncement =
|
|
46
|
+
| { kind: "none"; baseline: SubagentBaseline }
|
|
47
|
+
| { kind: "standing"; baseline: SubagentBaseline }
|
|
48
|
+
| { kind: "addendum"; text: string; baseline: SubagentBaseline };
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Fresh sessions may rewrite message 1; resumed sessions must leave its locked
|
|
52
|
+
* block alone and describe a real capability change at the tail instead.
|
|
53
|
+
*/
|
|
54
|
+
export function planSubagentAnnouncement(input: {
|
|
55
|
+
restored: boolean;
|
|
56
|
+
baseline: SubagentBaseline;
|
|
57
|
+
capability: SubagentCapability;
|
|
58
|
+
text: string | null;
|
|
59
|
+
}): SubagentAnnouncement {
|
|
60
|
+
if (input.baseline[input.capability] === input.text) return { kind: "none", baseline: input.baseline };
|
|
61
|
+
const baseline = { ...input.baseline, [input.capability]: input.text };
|
|
62
|
+
if (!input.restored) return { kind: "standing", baseline };
|
|
63
|
+
if (input.text !== null) return { kind: "addendum", text: input.text, baseline };
|
|
64
|
+
return {
|
|
65
|
+
kind: "addendum",
|
|
66
|
+
text: "The subagent delegation guidance previously shown for this conversation is no longer active.",
|
|
67
|
+
baseline,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -10,15 +10,14 @@
|
|
|
10
10
|
* not read: both project files live in the repository, and a checked-in `env`
|
|
11
11
|
* block must not get to pick which provider receives subagent tasks — the
|
|
12
12
|
* same containment rule as `autoMode` config.
|
|
13
|
-
* - `subagentModel` — One Code's own top-level setting
|
|
14
|
-
* `autoMode.classifierModel`. Wins over the env var when both are set,
|
|
13
|
+
* - `subagentModel` — One Code's own top-level setting. Wins over the env var when both are set,
|
|
15
14
|
* because it is the more specific statement of intent. It is One Code's key,
|
|
16
15
|
* not Claude Code's: it is written to and read from `~/.onecode/settings.json`
|
|
17
16
|
* (+ managed settings), never from `~/.claude`, where an old One Code build may
|
|
18
17
|
* have left a stale value the current session cannot reach. Claude Code's own
|
|
19
18
|
* `CLAUDE_CODE_SUBAGENT_MODEL` env block is still honoured from `~/.claude`
|
|
20
19
|
* (that key *is* Claude Code's). See "Own state, borrowed config" in
|
|
21
|
-
* docs/decisions/memory-state.md.
|
|
20
|
+
* working-docs/decisions/memory-state.md.
|
|
22
21
|
*
|
|
23
22
|
* The env var additionally applies only to Claude-family sessions (see
|
|
24
23
|
* `applicableSubagentDefault`): it is Claude Code's knob, and its typical
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
export const DELEGATION_STEER = [
|
|
19
|
-
|
|
20
|
-
'Make
|
|
21
|
-
"Search directly
|
|
22
|
-
"When the agent has answered, report its answer and move on — do not re-read the files it already covered.",
|
|
19
|
+
"Delegation policy: when a request requires reading MANY files (a codebase overview, how a feature works across modules, exploring unfamiliar code), do NOT sweep the files yourself.",
|
|
20
|
+
'Make one Agent tool call per independent question, with subagent_type: "explore" and the complete question as the task; for several independent questions, issue those calls together in one message. The agent searches in its own separate context and returns just the answer; reading file after file yourself fills your context and degrades your answer.',
|
|
21
|
+
"Search directly for a single targeted lookup: one known file or symbol, or a word one grep can find everywhere.",
|
|
22
|
+
"When the agent has answered, report its answer and move on — do not re-read the files it already covered, except one you are about to edit (an edit needs a read first).",
|
|
23
23
|
].join("\n");
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fork's side of reading the parent's prompt cache (decisions/caching.md,
|
|
3
|
+
* "A call that inherits the session's context reads the session's cache").
|
|
4
|
+
*
|
|
5
|
+
* A fork's own session builds its requests from its own system prompt, tools
|
|
6
|
+
* and reminder stack, so every fork re-read the parent's whole transcript
|
|
7
|
+
* uncached: on a parent near a 1M-token window, the whole window per fork.
|
|
8
|
+
* This inline extension, loaded last in a fork's session, sends the parent's
|
|
9
|
+
* last captured request instead, with the fork's own messages after it:
|
|
10
|
+
*
|
|
11
|
+
* - `context_with_system` (it runs after every `context` handler, so the
|
|
12
|
+
* fork's reminder stack is already on the messages it drops) removes the
|
|
13
|
+
* messages the capture carries, leaving pi to convert only the fork's tail;
|
|
14
|
+
* - `before_provider_request` (inline extensions load after the path ones, so
|
|
15
|
+
* tool-search has already edited the body) replaces everything but the tail
|
|
16
|
+
* with the captured body (`forkRequestPayload`);
|
|
17
|
+
* - `before_provider_headers` sends the parent's session-affinity headers, so a
|
|
18
|
+
* gateway such as OpenRouter routes the fork to the host holding the cache.
|
|
19
|
+
*
|
|
20
|
+
* Both steps check the same thing first (the capture's model and its boundary
|
|
21
|
+
* message in this context); when either fails, or the tail leaves less than
|
|
22
|
+
* `MIN_REPLAY_OUTPUT_TOKENS` of the captured output room, the request goes out
|
|
23
|
+
* the ordinary way, whole, and uncached. That happens only for a fork that
|
|
24
|
+
* compacted its own history away, runs on another model, or has nearly
|
|
25
|
+
* filled the window.
|
|
26
|
+
*/
|
|
27
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
28
|
+
import { DEFER_CHANNEL } from "../lib/deferred.ts";
|
|
29
|
+
import { type AffinityModel, affinityHeaders, captureMatches, forkOutputRoom, forkRequestPayload, MIN_REPLAY_OUTPUT_TOKENS, type RequestCapture, toolNames, uncoveredMessages } from "../lib/request-replay.ts";
|
|
30
|
+
|
|
31
|
+
type ModelRef = { api?: string; provider?: string; id?: string };
|
|
32
|
+
|
|
33
|
+
export function forkCacheExtension(capture: RequestCapture) {
|
|
34
|
+
return (pi: ExtensionAPI) => {
|
|
35
|
+
// The fork advertises the parent's tools, where SendMessage is deferred
|
|
36
|
+
// (subagents/index.ts). Defer the fork's own SendMessage the same way, so a
|
|
37
|
+
// tool_search load reaches it: on a model without tool references the load
|
|
38
|
+
// appends it, and elsewhere the parent's compatible schema (to: "main")
|
|
39
|
+
// already declares it. Inline extensions load after tool-search.
|
|
40
|
+
pi.events.emit(DEFER_CHANNEL, { name: "SendMessage", keywords: ["message", "main", "report", "progress"] });
|
|
41
|
+
/** Set by the context step for the request it shaped; the payload step splices only then. */
|
|
42
|
+
let shaped = false;
|
|
43
|
+
/** The tool names of the fork's first request: anything added later was loaded during the run. */
|
|
44
|
+
let baseline: Set<string> | undefined;
|
|
45
|
+
|
|
46
|
+
pi.on("context_with_system", (event, ctx) => {
|
|
47
|
+
shaped = false;
|
|
48
|
+
const covers = capture.covers;
|
|
49
|
+
if (!covers || !captureMatches(capture, ctx.model as ModelRef | undefined)) return undefined;
|
|
50
|
+
const tail = uncoveredMessages(event.messages as { role: string; timestamp?: number }[], covers);
|
|
51
|
+
if (!tail) return undefined;
|
|
52
|
+
// Too little output room left after the prefix: send the fork whole
|
|
53
|
+
// (pi's overflow handling then compacts it) rather than a splice the
|
|
54
|
+
// window cannot hold. The messages' JSON overstates the wire tail.
|
|
55
|
+
const room = forkOutputRoom(capture, tail.filter((message) => message.role !== "system"));
|
|
56
|
+
if (room !== undefined && room < MIN_REPLAY_OUTPUT_TOKENS) return undefined;
|
|
57
|
+
shaped = true;
|
|
58
|
+
return { messages: tail as typeof event.messages };
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Route to the parent's host: a gateway picks it by the session-affinity
|
|
62
|
+
// headers, and the parent's cache lives there. Harmless on a whole request.
|
|
63
|
+
pi.on("before_provider_headers", (event, ctx) => {
|
|
64
|
+
if (!capture.sessionId || !captureMatches(capture, ctx.model as ModelRef | undefined)) return;
|
|
65
|
+
Object.assign(event.headers, affinityHeaders(ctx.model as AffinityModel, capture.sessionId));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
pi.on("before_provider_request", (event, ctx) => {
|
|
69
|
+
if (!shaped || !captureMatches(capture, ctx.model as ModelRef | undefined)) return undefined;
|
|
70
|
+
shaped = false;
|
|
71
|
+
const payload = event.payload as Record<string, unknown>;
|
|
72
|
+
baseline ??= new Set(toolNames(payload.tools));
|
|
73
|
+
return forkRequestPayload(capture, payload, baseline);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `one-code-guide` agent — One Code's counterpart of Claude Code's built-in
|
|
3
|
+
* `claude-code-guide` (2.1.283): a read-only agent the model spawns when the
|
|
4
|
+
* user asks how One Code works, which answers from the documentation on disk
|
|
5
|
+
* rather than from memory, and knows the user's live setup.
|
|
6
|
+
*
|
|
7
|
+
* Claude Code's guide fetches its docs over the web and runs on `haiku`. Ours
|
|
8
|
+
* reads the user guide shipped in this package and the running pi's docs and
|
|
9
|
+
* examples (lib/guide-docs.ts), so the answer matches the installed version,
|
|
10
|
+
* and runs on the session's subagent model (no `model` here, so the usual
|
|
11
|
+
* resolution applies). It answers about One Code only, and when One Code lacks
|
|
12
|
+
* something it suggests how to add it, down to a custom pi extension.
|
|
13
|
+
*
|
|
14
|
+
* Built in code, not as an `agents/*.md` file, because the prompt names paths
|
|
15
|
+
* and configuration known only at runtime. It is the lowest-precedence
|
|
16
|
+
* definition, so a user or project agent file of the same name replaces it.
|
|
17
|
+
* Pure (no pi imports): the subagents extension gathers the inputs. The brand
|
|
18
|
+
* text lives here only, so a downstream fork swaps this one module.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { AgentDefinition } from "./agents.ts";
|
|
22
|
+
import { claudeSourcesOn, projectConfigDirName } from "../lib/config-mode.ts";
|
|
23
|
+
|
|
24
|
+
export const GUIDE_AGENT = "one-code-guide";
|
|
25
|
+
|
|
26
|
+
const REPO_URL = "https://github.com/IsuruMaduranga/one-code";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Claude Code's guide tools where search runs through the shell (its
|
|
30
|
+
* embedded-search shape): bash for `grep`/`find`, read, and the web tools.
|
|
31
|
+
* pi's own grep/find/ls are active only on the tiny tier (search-tools), so an
|
|
32
|
+
* allowlist naming them left the child without search.
|
|
33
|
+
*/
|
|
34
|
+
export const GUIDE_TOOLS = ["bash", "read", "web_fetch", "web_search"];
|
|
35
|
+
|
|
36
|
+
export const GUIDE_DESCRIPTION =
|
|
37
|
+
'Use this agent when the user asks questions ("Can One Code...", "Does One Code...", "How do I...") about One Code itself: features, slash commands, settings, permission modes and auto mode, hooks, skills, plugins, MCP servers, subagents and workflows, models and providers, keyboard shortcuts, installation and troubleshooting. Also use it when the user wants One Code to do something it does not do yet: it suggests an existing feature or setting, or how to add it with a skill, agent, hook, MCP server or a custom pi extension. **IMPORTANT:** Before spawning a new agent, check if there is already a running or recently completed one-code-guide agent that you can continue via SendMessage.';
|
|
38
|
+
|
|
39
|
+
export interface GuideInstall {
|
|
40
|
+
/** `app` for the bundled `onecode`, `extension` for One Code on the user's own pi. */
|
|
41
|
+
shape: "app" | "extension";
|
|
42
|
+
/** How the app was installed (`npm`, `brew`), when known. */
|
|
43
|
+
method?: string;
|
|
44
|
+
/** pi's agent directory for this session (`getAgentDir()`). */
|
|
45
|
+
agentDir: string;
|
|
46
|
+
/** The One Code version, when known. */
|
|
47
|
+
version?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The user's live setup, listed so the answer can build on it. Names only. */
|
|
51
|
+
export interface GuideSetup {
|
|
52
|
+
skills: string[];
|
|
53
|
+
agents: string[];
|
|
54
|
+
plugins: string[];
|
|
55
|
+
/** Every configured MCP server, with its status when it is not connected. */
|
|
56
|
+
mcpServers: { name: string; status?: string }[];
|
|
57
|
+
/** Extensions dropped into the user's pi extensions directory. */
|
|
58
|
+
extensions: string[];
|
|
59
|
+
/** pi packages the user installed, One Code's own entry excluded. */
|
|
60
|
+
packages: string[];
|
|
61
|
+
/** Keys set in pi's settings file for this session (values omitted). */
|
|
62
|
+
settingsKeys: string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface GuideInput {
|
|
66
|
+
docs: { guide: string; piDocs?: string; piExamples?: string };
|
|
67
|
+
install: GuideInstall;
|
|
68
|
+
setup: GuideSetup;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The packages and settings keys in pi's settings file (its raw text), minus
|
|
73
|
+
* One Code's own package entry (`ownRoot`, this package's directory, which the
|
|
74
|
+
* app registers on every launch). Unreadable text gives nothing.
|
|
75
|
+
*/
|
|
76
|
+
export function settingsSetup(raw: string | undefined, ownRoot: string): Pick<GuideSetup, "packages" | "settingsKeys"> {
|
|
77
|
+
let settings: unknown;
|
|
78
|
+
try {
|
|
79
|
+
settings = raw === undefined ? undefined : JSON.parse(raw);
|
|
80
|
+
} catch {
|
|
81
|
+
return { packages: [], settingsKeys: [] };
|
|
82
|
+
}
|
|
83
|
+
if (typeof settings !== "object" || settings === null || Array.isArray(settings)) return { packages: [], settingsKeys: [] };
|
|
84
|
+
const record = settings as Record<string, unknown>;
|
|
85
|
+
const sources = (Array.isArray(record.packages) ? record.packages : [])
|
|
86
|
+
.map((entry) => (typeof entry === "string" ? entry : (entry as { source?: unknown } | null)?.source))
|
|
87
|
+
.filter((source): source is string => typeof source === "string");
|
|
88
|
+
const own = (source: string) => source === ownRoot || /[/\\]one-code-extension$/.test(source) || /^npm:one-code-extension(@.*)?$/.test(source);
|
|
89
|
+
return { packages: sources.filter((source) => !own(source)), settingsKeys: Object.keys(record).sort() };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function installSection({ shape, method, agentDir, version }: GuideInstall): string {
|
|
93
|
+
const v = version ? ` ${version}` : "";
|
|
94
|
+
if (shape === "app") {
|
|
95
|
+
return `This session runs the bundled One Code app (\`onecode\`${v}${method ? `, installed with ${method}` : ""}). It carries its own pinned pi and keeps pi's state in its agent directory, \`${agentDir}\`. pi's commands work through it: \`onecode install <source>\` installs a pi package (npm, git or a local path) into that directory's \`settings.json\`, \`onecode remove <source>\` removes one, and \`onecode list\` lists them. A plain \`pi\` command on the same machine is a separate installation and does not see these.`;
|
|
96
|
+
}
|
|
97
|
+
return `This session runs One Code as an extension on the user's own pi installation${v ? ` (One Code${v})` : ""}. pi's agent directory is \`${agentDir}\`; \`pi install <source>\`, \`pi remove <source>\` and \`pi list\` manage packages there.`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Whether a repository-supplied name may be listed in the guide's system
|
|
102
|
+
* prompt: an identifier (letters, digits, `_ . : @ -`, at most 64), never free
|
|
103
|
+
* text a cloned repo could use to smuggle instructions in.
|
|
104
|
+
*/
|
|
105
|
+
export function listableName(name: string): boolean {
|
|
106
|
+
return /^[A-Za-z0-9_.:@-]{1,64}$/.test(name);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function setupSection(setup: GuideSetup): string | undefined {
|
|
110
|
+
const rows: string[] = [];
|
|
111
|
+
const list = (label: string, items: string[]) => {
|
|
112
|
+
if (items.length > 0) rows.push(`**${label}:** ${items.join(", ")}`);
|
|
113
|
+
};
|
|
114
|
+
// Skill folders, agent files and .mcp.json servers can come from a cloned
|
|
115
|
+
// repository, so only identifier-shaped names reach the system prompt.
|
|
116
|
+
list("Custom skills", setup.skills.filter(listableName));
|
|
117
|
+
list("Custom agents", setup.agents.filter(listableName));
|
|
118
|
+
list("Enabled plugins", setup.plugins);
|
|
119
|
+
list(
|
|
120
|
+
"Configured MCP servers",
|
|
121
|
+
setup.mcpServers.filter((server) => listableName(server.name)).map((server) => (server.status ? `${server.name} (${server.status})` : server.name)),
|
|
122
|
+
);
|
|
123
|
+
list("Installed pi packages", setup.packages);
|
|
124
|
+
list("Extensions in the user extensions directory", setup.extensions);
|
|
125
|
+
list("Settings keys configured (values omitted)", setup.settingsKeys);
|
|
126
|
+
if (rows.length === 0) return undefined;
|
|
127
|
+
return `# User's Current Configuration\nThe user has the following custom setup in their environment:\n${rows.join("\n")}\nWhen answering questions, consider these configured features and proactively suggest them when relevant.`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function guideSystemPrompt({ docs, install, setup }: GuideInput): string {
|
|
131
|
+
const piDocs = docs.piDocs ? `\`${docs.piDocs}\`` : "pi's `docs/` folder (not found on this machine; use https://pi.dev/docs/latest)";
|
|
132
|
+
const piExamples = docs.piExamples ? `\`${docs.piExamples}\`` : "pi's `examples/` folder (not found on this machine)";
|
|
133
|
+
const extensionsDir = `${install.agentDir}/extensions/`;
|
|
134
|
+
const prompt = `You are the One Code guide agent. Your primary responsibility is helping users understand and use One Code effectively, and showing them how to make it do more.
|
|
135
|
+
|
|
136
|
+
**What One Code is:** a coding agent that recreates the Claude Code experience on any model provider. It is built as a package of extensions for the pi agent harness, so everything it does is an ordinary pi extension, and users can extend it the same way.
|
|
137
|
+
|
|
138
|
+
**How this session is installed:** ${installSection(install)}
|
|
139
|
+
|
|
140
|
+
**Documentation sources (all on this machine, matching the installed version):**
|
|
141
|
+
- **One Code user guide** (\`${docs.guide}\`): start with its \`README.md\`, the index of every page. Read it for any question about One Code's features, commands, settings, permissions and auto mode, hooks, skills, plugins, MCP, subagents, workflows, models, the terminal interface, Windows, troubleshooting, and how One Code differs from Claude Code.
|
|
142
|
+
- **pi documentation** (${piDocs}): read it for what One Code inherits from pi and for extending it: \`extensions.md\` (writing an extension), \`packages.md\` (installing and publishing packages), \`settings.md\`, \`keybindings.md\`, \`providers.md\` and \`models.md\`.
|
|
143
|
+
- **pi examples** (${piExamples}): working extensions to base a suggestion on (\`examples/extensions/\`).
|
|
144
|
+
|
|
145
|
+
**Approach:**
|
|
146
|
+
1. Read the One Code guide's index and pick the pages that cover the question
|
|
147
|
+
2. Read those pages; search the docs with \`grep\` and \`find\` through the shell when the index does not name the topic
|
|
148
|
+
3. For customization and extension questions, read pi's docs and examples too
|
|
149
|
+
4. Read the user's own configuration (settings files, ${claudeSourcesOn() ? "CLAUDE.md" : "AGENTS.md, ONECODE.md"}, \`${projectConfigDirName()}/\`) when it bears on the answer
|
|
150
|
+
5. Provide clear, actionable guidance based on the documentation, citing the file each fact came from
|
|
151
|
+
|
|
152
|
+
**When One Code does not do what the user wants,** say so plainly, then suggest the smallest change that gets them there, in this order:
|
|
153
|
+
1. An existing feature, command or setting that covers it
|
|
154
|
+
2. Configuration they can add themselves: a skill, a custom agent, a hook, an MCP server, or a plugin (the guide explains each)
|
|
155
|
+
3. A custom pi extension. Describe what it would do and which pi events or APIs it would use, from pi's \`extensions.md\` and the examples. To try it, the user drops a \`.ts\` file (or a folder with an \`index.ts\`) into \`${extensionsDir}\` for every project, or \`.pi/extensions/\` in one project (loaded once the project is trusted), then restarts or runs \`/reload\`. To share it, they make it a pi package and install it with the commands above. Community packages may already exist: search https://pi.dev/packages first, and tell the user to review a package's source before installing it, because extensions run with full access.
|
|
156
|
+
4. If it is a gap or a bug in One Code itself, suggest opening an issue at ${REPO_URL}/issues
|
|
157
|
+
|
|
158
|
+
**Guidelines:**
|
|
159
|
+
- Your training data does not know One Code. Never answer from memory: every fact about One Code comes from the documentation above. If the docs do not cover the topic, say so rather than guess.
|
|
160
|
+
- Answer questions about One Code (and pi, as far as it shapes One Code) only. For anything else, say it is outside what you cover and suggest asking in the main conversation.
|
|
161
|
+
- Read files with the read tool, not the shell. Use the shell only for read-only searches (\`grep\`, \`find\`). Never edit, write, install or delete anything: when a suggestion needs a file written or a package installed, describe it; the main agent or the user does it.
|
|
162
|
+
- Keep responses concise and actionable
|
|
163
|
+
- Include specific examples, commands or code snippets when helpful
|
|
164
|
+
- Reference the documentation file paths in your responses
|
|
165
|
+
- Help users discover features by proactively suggesting related commands, shortcuts, or capabilities
|
|
166
|
+
|
|
167
|
+
Complete the user's request by providing accurate, documentation-based guidance.`;
|
|
168
|
+
const setupText = setupSection(setup);
|
|
169
|
+
return setupText ? `${prompt}\n\n${setupText}` : prompt;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The definition, with the prompt built on first read: the catalog lists the
|
|
174
|
+
* guide on every agent listing and spawn, and only a spawn of the guide needs
|
|
175
|
+
* its prompt (the setup it lists costs a settings read and directory scans).
|
|
176
|
+
*/
|
|
177
|
+
export function guideAgentDefinition(input: () => GuideInput): AgentDefinition {
|
|
178
|
+
let prompt: string | undefined;
|
|
179
|
+
return {
|
|
180
|
+
name: GUIDE_AGENT,
|
|
181
|
+
description: GUIDE_DESCRIPTION,
|
|
182
|
+
tools: GUIDE_TOOLS,
|
|
183
|
+
get systemPrompt() {
|
|
184
|
+
prompt ??= guideSystemPrompt(input());
|
|
185
|
+
return prompt;
|
|
186
|
+
},
|
|
187
|
+
source: "built-in",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -42,6 +42,8 @@ export function awaitHandBackReview(
|
|
|
42
42
|
run: { taskId: string; name: string },
|
|
43
43
|
actions: ChildAction[] | undefined,
|
|
44
44
|
timeoutMs = HAND_BACK_REVIEW_TIMEOUT_MS,
|
|
45
|
+
/** The main-session call that started the turn (SubagentActionsPayload.startedBy). */
|
|
46
|
+
startedBy?: string,
|
|
45
47
|
): Promise<HandBackVerdict | undefined> {
|
|
46
48
|
if (!actions?.length) return Promise.resolve(undefined);
|
|
47
49
|
return new Promise((resolve) => {
|
|
@@ -59,6 +61,7 @@ export function awaitHandBackReview(
|
|
|
59
61
|
actions,
|
|
60
62
|
background: true,
|
|
61
63
|
agentName: run.name,
|
|
64
|
+
...(startedBy ? { startedBy } : {}),
|
|
62
65
|
onReview: finish,
|
|
63
66
|
} satisfies SubagentActionsPayload);
|
|
64
67
|
});
|