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
|
@@ -15,22 +15,37 @@
|
|
|
15
15
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
16
16
|
import { basename, dirname, join, relative } from "node:path";
|
|
17
17
|
import { findGitRoot } from "../lib/git.ts";
|
|
18
|
-
import { discoverPlugins } from "../lib/plugins.ts";
|
|
18
|
+
import { defaultDiscoverRoots, discoverPlugins } from "../lib/plugins.ts";
|
|
19
|
+
import { claudeUserDir } from "../lib/paths.ts";
|
|
19
20
|
|
|
20
21
|
export interface StartupSection {
|
|
21
22
|
label: string;
|
|
22
23
|
items: string[];
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The project context files One Code actually gathers, from cwd up to the git
|
|
28
|
+
* root (or just cwd outside a repo), for the startup banner. Mirrors what the
|
|
29
|
+
* blocks send: a directory's CLAUDE.md, or its AGENTS.md when it has no CLAUDE.md
|
|
30
|
+
* (CLAUDE.md > AGENTS.md), plus CLAUDE.local.md and ONECODE.md when present — so
|
|
31
|
+
* AGENTS.md is listed only when it's really in play, not whenever it exists.
|
|
32
|
+
*/
|
|
26
33
|
export function contextFileNames(cwd: string): string[] {
|
|
27
34
|
const stop = findGitRoot(cwd) ?? cwd;
|
|
28
35
|
const found: string[] = [];
|
|
29
36
|
let dir = cwd;
|
|
30
37
|
while (true) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
const rel = (name: string) => relative(cwd, join(dir, name)) || name;
|
|
39
|
+
const present = (name: string) => existsSync(join(dir, name));
|
|
40
|
+
// CLAUDE.md is primary; AGENTS.md stands in only when there is no CLAUDE.md.
|
|
41
|
+
if (present("CLAUDE.md")) found.push(rel("CLAUDE.md"));
|
|
42
|
+
else if (present("AGENTS.md")) found.push(rel("AGENTS.md"));
|
|
43
|
+
if (present("CLAUDE.local.md")) found.push(rel("CLAUDE.local.md"));
|
|
44
|
+
for (const name of ["ONECODE.md", "onecode.md", "One Code.md"]) {
|
|
45
|
+
if (present(name)) {
|
|
46
|
+
found.push(rel(name));
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
34
49
|
}
|
|
35
50
|
if (dir === stop) break;
|
|
36
51
|
const parent = dirname(dir);
|
|
@@ -49,7 +64,7 @@ export function contextFileNames(cwd: string): string[] {
|
|
|
49
64
|
export function skillNames(cwd: string, home: string, agentDir: string): string[] {
|
|
50
65
|
const dirs = [
|
|
51
66
|
join(cwd, ".claude", "skills"),
|
|
52
|
-
join(home, "
|
|
67
|
+
join(claudeUserDir(home), "skills"),
|
|
53
68
|
join(agentDir, "skills"),
|
|
54
69
|
];
|
|
55
70
|
const names = new Set<string>();
|
|
@@ -63,7 +78,7 @@ export function skillNames(cwd: string, home: string, agentDir: string): string[
|
|
|
63
78
|
// Unreadable dir: skip, same as pi would.
|
|
64
79
|
}
|
|
65
80
|
}
|
|
66
|
-
for (const skill of discoverPlugins(
|
|
81
|
+
for (const skill of discoverPlugins(defaultDiscoverRoots(agentDir, cwd, home)).skills) {
|
|
67
82
|
names.add(skill.name);
|
|
68
83
|
}
|
|
69
84
|
return [...names].sort((a, b) => a.localeCompare(b));
|
|
@@ -72,7 +87,7 @@ export function skillNames(cwd: string, home: string, agentDir: string): string[
|
|
|
72
87
|
/** Saved workflow names from the Claude Code layout dirs (project shadows user). */
|
|
73
88
|
export function workflowNames(cwd: string, home: string): string[] {
|
|
74
89
|
const names = new Set<string>();
|
|
75
|
-
for (const dir of [join(cwd, ".claude", "workflows"), join(home, "
|
|
90
|
+
for (const dir of [join(cwd, ".claude", "workflows"), join(claudeUserDir(home), "workflows")]) {
|
|
76
91
|
if (!existsSync(dir)) continue;
|
|
77
92
|
try {
|
|
78
93
|
for (const entry of readdirSync(dir)) {
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* ~/.claude/commands, <project>/.claude/commands → slash commands (prompt templates;
|
|
9
9
|
* Claude Code's $ARGUMENTS works as-is)
|
|
10
10
|
*
|
|
11
|
+
* It also points discovery at the catalog of skills bundled with this package
|
|
12
|
+
* (`<package>/skills`) — Claude Code's self-contained built-in skills, which
|
|
13
|
+
* ship inside its binary rather than on disk, so nothing else would surface
|
|
14
|
+
* them. See docs/decisions/skills-plugins.md and docs/findings (the extraction
|
|
15
|
+
* process is recorded in .claude/skills/extract-cc-skills/).
|
|
16
|
+
*
|
|
11
17
|
* CLAUDE.md needs no handling — pi discovers it natively alongside AGENTS.md.
|
|
12
18
|
*/
|
|
13
19
|
|
|
@@ -16,6 +22,9 @@ import os from "node:os";
|
|
|
16
22
|
import { join } from "node:path";
|
|
17
23
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
18
24
|
import { claudeConfigDir } from "../lib/paths.ts";
|
|
25
|
+
import { claudeUserDir } from "../lib/paths.ts";
|
|
26
|
+
|
|
27
|
+
import { BUNDLED_SKILLS_DIR } from "../lib/skill-scan.ts";
|
|
19
28
|
|
|
20
29
|
/**
|
|
21
30
|
* `claudeDir` is Claude Code's config dir (honours CLAUDE_CONFIG_DIR), defaulting
|
|
@@ -26,7 +35,8 @@ import { claudeConfigDir } from "../lib/paths.ts";
|
|
|
26
35
|
export function claudeResourcePaths(
|
|
27
36
|
cwd: string,
|
|
28
37
|
home: string,
|
|
29
|
-
claudeDir: string =
|
|
38
|
+
claudeDir: string = claudeUserDir(home),
|
|
39
|
+
bundledSkillsDir?: string,
|
|
30
40
|
): { skillPaths: string[]; promptPaths: string[] } {
|
|
31
41
|
const candidates = {
|
|
32
42
|
skillPaths: [
|
|
@@ -34,6 +44,11 @@ export function claudeResourcePaths(
|
|
|
34
44
|
join(home, ".agents", "skills"),
|
|
35
45
|
join(cwd, ".claude", "skills"),
|
|
36
46
|
join(cwd, ".agents", "skills"),
|
|
47
|
+
// The bundled catalog is listed LAST so it loses a name collision:
|
|
48
|
+
// pi keeps the first-loaded skill for a given name, so a user or
|
|
49
|
+
// project skill of the same name wins and the bundled one is the
|
|
50
|
+
// fallback (see docs/decisions/skills-plugins.md).
|
|
51
|
+
...(bundledSkillsDir ? [bundledSkillsDir] : []),
|
|
37
52
|
],
|
|
38
53
|
promptPaths: [join(claudeDir, "commands"), join(cwd, ".claude", "commands")],
|
|
39
54
|
};
|
|
@@ -45,6 +60,6 @@ export function claudeResourcePaths(
|
|
|
45
60
|
|
|
46
61
|
export default function claudeCompatExtension(pi: ExtensionAPI) {
|
|
47
62
|
pi.on("resources_discover", (event) => {
|
|
48
|
-
return claudeResourcePaths(event.cwd, os.homedir(), claudeConfigDir());
|
|
63
|
+
return claudeResourcePaths(event.cwd, os.homedir(), claudeConfigDir(), BUNDLED_SKILLS_DIR);
|
|
49
64
|
});
|
|
50
65
|
}
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* pi's `context` event), so it never persists to the session and never doubles up
|
|
11
11
|
* on resume.
|
|
12
12
|
*
|
|
13
|
+
* When ONECODE.md files exist, this extension also emits a separate `# oneCodeMd`
|
|
14
|
+
* block at CONTEXT_ORDER.oneCodeMd — after # claudeMd, so One Code-specific
|
|
15
|
+
* instructions take precedence over CLAUDE.md. Keeping them out of # claudeMd
|
|
16
|
+
* leaves that block byte-exact with Claude Code.
|
|
17
|
+
*
|
|
13
18
|
* Paths are re-derived from home/cwd here rather than shared with the memory
|
|
14
19
|
* extension (jiti gives each extension its own module instance). CLAUDE.md is no
|
|
15
20
|
* longer put in the system prompt, and MEMORY.md is no longer a separate reminder
|
|
@@ -21,12 +26,13 @@ import { readFileSync } from "node:fs";
|
|
|
21
26
|
import os from "node:os";
|
|
22
27
|
import { join } from "node:path";
|
|
23
28
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
24
|
-
import { buildClaudeMdBlock, discoverContextFiles } from "../lib/claude-context.ts";
|
|
29
|
+
import { buildClaudeMdBlock, buildOneCodeBlock, discoverContextFiles, discoverOneCodeFiles } from "../lib/claude-context.ts";
|
|
25
30
|
import { projectMemoryDir, truncateIndex } from "../lib/memory.ts";
|
|
26
|
-
import { claudeConfigDir } from "../lib/paths.ts";
|
|
31
|
+
import { claudeConfigDir, oneCodeStateDir } from "../lib/paths.ts";
|
|
27
32
|
import { CONTEXT_ORDER, REMINDER_CHANNEL } from "../lib/reminders.ts";
|
|
28
33
|
|
|
29
34
|
const REMINDER_KEY = "claude-context";
|
|
35
|
+
const ONECODE_REMINDER_KEY = "one-code-context";
|
|
30
36
|
|
|
31
37
|
/** The account email Claude Code stamps as `# userEmail`. git config is our proxy. */
|
|
32
38
|
function resolveEmail(cwd: string): string | null {
|
|
@@ -53,10 +59,17 @@ function readMemoryIndex(cwd: string): { path: string; content: string } | null
|
|
|
53
59
|
|
|
54
60
|
export default function claudeContextExtension(pi: ExtensionAPI) {
|
|
55
61
|
pi.on("session_start", (_event, ctx) => {
|
|
62
|
+
// The # claudeMd block carries the CLAUDE.md family, falling back to a
|
|
63
|
+
// directory's AGENTS.md when it has no CLAUDE.md (CLAUDE.md > AGENTS.md). It
|
|
64
|
+
// stays byte-exact with Claude Code wherever CLAUDE.md is present, since
|
|
65
|
+
// AGENTS.md only fills in for a missing one. ONECODE.md rides its own
|
|
66
|
+
// higher-precedence block below.
|
|
56
67
|
const inner = buildClaudeMdBlock({
|
|
57
68
|
contextFiles: discoverContextFiles({
|
|
58
69
|
cwd: ctx.cwd,
|
|
59
70
|
homeClaudeDir: claudeConfigDir(),
|
|
71
|
+
agentsFallback: true,
|
|
72
|
+
home: os.homedir(),
|
|
60
73
|
}),
|
|
61
74
|
memoryIndex: readMemoryIndex(ctx.cwd),
|
|
62
75
|
email: resolveEmail(ctx.cwd),
|
|
@@ -64,16 +77,32 @@ export default function claudeContextExtension(pi: ExtensionAPI) {
|
|
|
64
77
|
// block changes only across days too.
|
|
65
78
|
date: new Date().toISOString().slice(0, 10),
|
|
66
79
|
});
|
|
67
|
-
if (
|
|
80
|
+
if (inner) {
|
|
81
|
+
pi.events.emit(REMINDER_CHANNEL, {
|
|
82
|
+
text: inner,
|
|
83
|
+
scope: "every-turn",
|
|
84
|
+
key: REMINDER_KEY,
|
|
85
|
+
placement: "first-prepend",
|
|
86
|
+
order: CONTEXT_ORDER.claudeMd,
|
|
87
|
+
// Claude Code's claudeMd block ends `</system-reminder>\n\n` on the wire.
|
|
88
|
+
suffix: "\n\n",
|
|
89
|
+
});
|
|
90
|
+
}
|
|
68
91
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
// One Code's own instructions ride in a separate block AFTER # claudeMd, so
|
|
93
|
+
// they take precedence over CLAUDE.md (higher order = closer to the user text).
|
|
94
|
+
const oneCode = buildOneCodeBlock(
|
|
95
|
+
discoverOneCodeFiles({ cwd: ctx.cwd, homeOneCodeDir: oneCodeStateDir(), home: os.homedir() }),
|
|
96
|
+
);
|
|
97
|
+
if (oneCode) {
|
|
98
|
+
pi.events.emit(REMINDER_CHANNEL, {
|
|
99
|
+
text: oneCode,
|
|
100
|
+
scope: "every-turn",
|
|
101
|
+
key: ONECODE_REMINDER_KEY,
|
|
102
|
+
placement: "first-prepend",
|
|
103
|
+
order: CONTEXT_ORDER.oneCodeMd,
|
|
104
|
+
suffix: "\n\n",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
78
107
|
});
|
|
79
108
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* clear extension — Claude Code's `/clear`.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code's `/clear` wipes the conversation and starts fresh in the same
|
|
5
|
+
* working directory. pi's nearest primitive is `newSession()` (its built-in
|
|
6
|
+
* command is `/new`). A user coming from Claude Code types `/clear` out of
|
|
7
|
+
* habit; without this it isn't a command, so the word goes to the model as a
|
|
8
|
+
* message instead of resetting context. This registers `/clear` alongside pi's
|
|
9
|
+
* `/new`, routed through the same `newSession()` call.
|
|
10
|
+
*
|
|
11
|
+
* Nuance vs. Claude Code: CC clears history in place; pi starts a new session
|
|
12
|
+
* file (same cwd, extensions, config). The user-facing effect is identical —
|
|
13
|
+
* a clean context — which is what a `/clear` habit wants. Like `/exit`→`/quit`,
|
|
14
|
+
* we cannot fold this into pi's `/new` palette entry (pi has no command
|
|
15
|
+
* aliases), so `/clear` and `/new` are two entries that both reset.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
19
|
+
|
|
20
|
+
export default function clearExtension(pi: ExtensionAPI) {
|
|
21
|
+
pi.registerCommand("clear", {
|
|
22
|
+
description: "Clear the conversation and start fresh (same as /new)",
|
|
23
|
+
handler: async (_args, ctx) => {
|
|
24
|
+
await ctx.newSession();
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which request shape a compaction takes, and how a standalone one is made to
|
|
3
|
+
* fit the window. Pure: no pi extension API, only pi-ai's own estimator.
|
|
4
|
+
*
|
|
5
|
+
* Two shapes exist:
|
|
6
|
+
*
|
|
7
|
+
* - **Replay**: the session's last real request (captured from the `context`
|
|
8
|
+
* event) plus the instruction. Cache-aligned, so the call is mostly cache
|
|
9
|
+
* reads — the reason the extension compacts on the session model at all.
|
|
10
|
+
* It only works while the request leaves room for the summary: pi clamps
|
|
11
|
+
* `max_tokens` to what the window has left (`clampMaxTokensToContext`), so a
|
|
12
|
+
* request at or over the window is sent with `max_tokens` 1, or the provider
|
|
13
|
+
* rejects it outright. Both return nothing usable. An overflow-triggered
|
|
14
|
+
* compaction is *by definition* in that state (review H2: every overflow
|
|
15
|
+
* fell through to pi's summary).
|
|
16
|
+
*
|
|
17
|
+
* - **Standalone**: the doomed span alone (what pi is about to discard,
|
|
18
|
+
* reconstructed from `preparation`), no tools, a one-line system prompt, and
|
|
19
|
+
* the instruction. It cannot hit the cache, which is moot on an overflowing
|
|
20
|
+
* request anyway; what it keeps is One Code's summary: the nine sections,
|
|
21
|
+
* the continuation preamble, and the transcript pointer. Even the doomed
|
|
22
|
+
* span may not fit — pi keeps only ~20k tokens after the cut, so on an
|
|
23
|
+
* overflow the span is nearly the whole window — so `fitToBudget` clears
|
|
24
|
+
* the largest tool results first until the request has room. Their full
|
|
25
|
+
* text is in the session transcript the summary points at (Claude Code's
|
|
26
|
+
* own microcompact clears old tool results the same way).
|
|
27
|
+
*
|
|
28
|
+
* Both decisions ask pi-ai's `clampMaxTokensToContext` — the very function
|
|
29
|
+
* `completeSimple` applies to the request — rather than re-deriving its
|
|
30
|
+
* arithmetic, so they can never disagree with the clamp they predict. One
|
|
31
|
+
* trap in that estimator: it anchors on the last assistant message's
|
|
32
|
+
* `usage` total when there is one (`withoutUsage`).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { type Api, type Context, contentText, type Message, type Model } from "@earendil-works/pi-ai";
|
|
36
|
+
import { PREVIEW_BYTES } from "../lib/persisted-output.ts";
|
|
37
|
+
// Vendored from pi-ai: importing its deep subpaths directly breaks under the
|
|
38
|
+
// bundled app's library loader (distribution review 2026-09-09, H1).
|
|
39
|
+
import { clampMaxTokensToContext, estimateMessageTokens } from "../lib/pi-ai-estimate.ts";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The least output room a replay must leave for the summary. Below it the
|
|
43
|
+
* clamp truncates the reply (or the request is rejected) and pi's summary
|
|
44
|
+
* would serve instead. With pi's default `reserveTokens` (16 384) a threshold
|
|
45
|
+
* compaction leaves ~12k, comfortably above; an overflow leaves nothing.
|
|
46
|
+
*/
|
|
47
|
+
export const MIN_REPLAY_SUMMARY_TOKENS = 4096;
|
|
48
|
+
|
|
49
|
+
/** The output room a standalone request is trimmed to leave for the summary. */
|
|
50
|
+
export const STANDALONE_SUMMARY_TOKENS = 8192;
|
|
51
|
+
|
|
52
|
+
/** How much of a cleared tool result's text is kept as a landmark (persisted-output's preview size). */
|
|
53
|
+
export const CLEARED_RESULT_HEAD_CHARS = PREVIEW_BYTES;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* True when replaying the captured request would leave room for the summary:
|
|
57
|
+
* the `max_tokens` pi will actually send, given this request, is at least the
|
|
58
|
+
* floor. An overflow never replays — the request that just failed *is* the
|
|
59
|
+
* capture, whatever the estimate says.
|
|
60
|
+
*/
|
|
61
|
+
export function replayFits(reason: "manual" | "threshold" | "overflow", model: Model<Api>, request: Context, maxTokens: number): boolean {
|
|
62
|
+
if (reason === "overflow") return false;
|
|
63
|
+
return clampMaxTokensToContext(model, request, maxTokens) >= MIN_REPLAY_SUMMARY_TOKENS;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Clears the largest tool results (oldest first on a tie) until pi's clamp
|
|
68
|
+
* would leave `STANDALONE_SUMMARY_TOKENS` of output room, or none are left to
|
|
69
|
+
* clear (then the provider's verdict decides, and pi's summary serves on
|
|
70
|
+
* failure). Nothing but tool results is touched. Returns a new context with
|
|
71
|
+
* new messages — the inputs are never mutated.
|
|
72
|
+
*/
|
|
73
|
+
export function fitToBudget(model: Model<Api>, request: Context, maxTokens: number): { request: Context; cleared: number } {
|
|
74
|
+
const messages = [...request.messages];
|
|
75
|
+
const fitted: Context = { ...request, messages };
|
|
76
|
+
const candidates = messages
|
|
77
|
+
.map((message, index) => ({ index, tokens: message.role === "toolResult" ? estimateMessageTokens(message) : -1 }))
|
|
78
|
+
.filter(({ tokens }) => tokens >= 0)
|
|
79
|
+
.sort((a, b) => b.tokens - a.tokens || a.index - b.index);
|
|
80
|
+
|
|
81
|
+
let cleared = 0;
|
|
82
|
+
for (const { index } of candidates) {
|
|
83
|
+
if (clampMaxTokensToContext(model, fitted, maxTokens) >= STANDALONE_SUMMARY_TOKENS) break;
|
|
84
|
+
messages[index] = clearToolResult(messages[index]);
|
|
85
|
+
cleared++;
|
|
86
|
+
}
|
|
87
|
+
return { request: fitted, cleared };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The messages with every assistant `usage` zeroed. pi-ai's estimator anchors
|
|
92
|
+
* on the *last assistant message's usage total* plus the messages after it
|
|
93
|
+
* (`estimateContextTokens`), and a usage total describes the request it came
|
|
94
|
+
* from — the session's, with its ~20k of system prompt and tools, the
|
|
95
|
+
* messages before the span, and on an overflow a total already past the
|
|
96
|
+
* window. Anchored on it, a standalone request that fit by content was still
|
|
97
|
+
* clamped to `max_tokens` 1 (measured). Zero usage is skipped by the
|
|
98
|
+
* estimator (it needs a positive total), so the clamp counts what is actually
|
|
99
|
+
* sent. The replay path keeps its usage: there the numbers describe the very
|
|
100
|
+
* prefix being replayed. Copies; the inputs are untouched.
|
|
101
|
+
*/
|
|
102
|
+
export function withoutUsage(messages: readonly Message[]): Message[] {
|
|
103
|
+
return messages.map((message) =>
|
|
104
|
+
message.role === "assistant" && message.usage
|
|
105
|
+
? { ...message, usage: { ...message.usage, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0 } }
|
|
106
|
+
: message,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A tool result reduced to its opening text and a note; images are dropped. */
|
|
111
|
+
export function clearToolResult(message: Message): Message {
|
|
112
|
+
if (message.role !== "toolResult") return message;
|
|
113
|
+
const text = contentText(message.content);
|
|
114
|
+
const head = text.slice(0, CLEARED_RESULT_HEAD_CHARS);
|
|
115
|
+
const omitted = text.length - head.length;
|
|
116
|
+
const images = message.content.filter((block) => block.type !== "text").length;
|
|
117
|
+
const note = `[Tool result cleared to fit the compaction request: ${omitted} more characters${
|
|
118
|
+
images > 0 ? ` and ${images} image${images === 1 ? "" : "s"}` : ""
|
|
119
|
+
} omitted. The full result is in the session transcript.]`;
|
|
120
|
+
return { ...message, content: [{ type: "text", text: head ? `${head}\n${note}` : note }] };
|
|
121
|
+
}
|