Compare commits
3
Commits
75dcd21776
...
fccb75298e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fccb75298e | ||
|
|
3208abe464 | ||
|
|
08c7ca752a |
+50
-13
@@ -33,7 +33,32 @@
|
|||||||
],
|
],
|
||||||
"defaultMode": "auto"
|
"defaultMode": "auto"
|
||||||
},
|
},
|
||||||
|
"model": "opus[1m]",
|
||||||
|
"disableClaudeAiConnectors": true,
|
||||||
|
"remoteControlAtStartup": false,
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
"PostToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Bash",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "atuin hook claude-code"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"PostToolUseFailure": [
|
||||||
|
{
|
||||||
|
"matcher": "Bash",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "atuin hook claude-code"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"PreToolUse": [
|
"PreToolUse": [
|
||||||
{
|
{
|
||||||
"matcher": "Bash",
|
"matcher": "Bash",
|
||||||
@@ -47,22 +72,33 @@
|
|||||||
"command": "~/.claude/hooks/block-file-deletion.sh"
|
"command": "~/.claude/hooks/block-file-deletion.sh"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": "Bash",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "atuin hook claude-code"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"disableWorkflows": true,
|
||||||
|
"disableArtifact": true,
|
||||||
"statusLine": {
|
"statusLine": {
|
||||||
"type": "command",
|
"type": "command",
|
||||||
"command": "bash /home/tgrosinger/.claude/statusline-command.sh"
|
"command": "bash /home/tgrosinger/.claude/statusline-command.sh"
|
||||||
},
|
},
|
||||||
"enabledPlugins": {
|
"enabledPlugins": {
|
||||||
"pr-review-toolkit@claude-plugins-official": true,
|
|
||||||
"gopls-lsp@claude-plugins-official": true,
|
|
||||||
"frontend-design@claude-plugins-official": true,
|
|
||||||
"code-simplifier@claude-plugins-official": true,
|
|
||||||
"skill-creator@claude-plugins-official": true,
|
|
||||||
"typescript-lsp@claude-plugins-official": true,
|
|
||||||
"claude-md-management@claude-plugins-official": true,
|
"claude-md-management@claude-plugins-official": true,
|
||||||
"security-guidance@claude-plugins-official": true
|
"code-simplifier@claude-plugins-official": true,
|
||||||
|
"frontend-design@claude-plugins-official": true,
|
||||||
|
"gopls-lsp@claude-plugins-official": true,
|
||||||
|
"pr-review-toolkit@claude-plugins-official": true,
|
||||||
|
"security-guidance@claude-plugins-official": true,
|
||||||
|
"skill-creator@claude-plugins-official": true,
|
||||||
|
"typescript-lsp@claude-plugins-official": true
|
||||||
},
|
},
|
||||||
"sandbox": {
|
"sandbox": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@@ -86,19 +122,23 @@
|
|||||||
"filesystem": {
|
"filesystem": {
|
||||||
"allowWrite": [
|
"allowWrite": [
|
||||||
"~/.local/share/pnpm",
|
"~/.local/share/pnpm",
|
||||||
"~/.cache/pnpm"
|
"~/.cache/pnpm",
|
||||||
|
"~/Documents/Atrium"
|
||||||
],
|
],
|
||||||
"denyRead": [
|
"denyRead": [
|
||||||
"~/.ssh",
|
"~/.ssh",
|
||||||
"~/.config/Signal",
|
"~/.config/Signal",
|
||||||
"~/Documents"
|
"~/Documents"
|
||||||
|
],
|
||||||
|
"allowRead": [
|
||||||
|
"~/Documents/Atrium"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"excludedCommands": [
|
"excludedCommands": [
|
||||||
"git push *",
|
"git push *",
|
||||||
"brew *",
|
"brew *",
|
||||||
"devbox add *",
|
"tn *",
|
||||||
"nix *"
|
"obsidian *"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"spinnerVerbs": {
|
"spinnerVerbs": {
|
||||||
@@ -119,8 +159,5 @@
|
|||||||
"theme": "light",
|
"theme": "light",
|
||||||
"editorMode": "vim",
|
"editorMode": "vim",
|
||||||
"agentPushNotifEnabled": true,
|
"agentPushNotifEnabled": true,
|
||||||
"disableClaudeAiConnectors": true,
|
|
||||||
"disableWorkflows": true,
|
|
||||||
"disableArtifact": true,
|
|
||||||
"skipAutoPermissionPrompt": true
|
"skipAutoPermissionPrompt": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* Atuin extension for pi.
|
||||||
|
*
|
||||||
|
* Tracks bash commands executed by pi in Atuin history with author `pi`.
|
||||||
|
*
|
||||||
|
* Install with:
|
||||||
|
* atuin hook install pi
|
||||||
|
*
|
||||||
|
* Then restart pi or run /reload.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
|
const ATUIN_AUTHOR = "pi";
|
||||||
|
const ATUIN_TIMEOUT_MS = 10_000;
|
||||||
|
|
||||||
|
async function startHistory(
|
||||||
|
pi: ExtensionAPI,
|
||||||
|
cwd: string,
|
||||||
|
command: string,
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
try {
|
||||||
|
const result = await pi.exec(
|
||||||
|
"atuin",
|
||||||
|
["history", "start", "--author", ATUIN_AUTHOR, "--", command],
|
||||||
|
{ cwd, timeout: ATUIN_TIMEOUT_MS },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.code !== 0) return undefined;
|
||||||
|
|
||||||
|
const id = result.stdout.trim();
|
||||||
|
return id.length > 0 ? id : undefined;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function endHistory(
|
||||||
|
pi: ExtensionAPI,
|
||||||
|
cwd: string,
|
||||||
|
historyId: string,
|
||||||
|
exitCode: number,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await pi.exec(
|
||||||
|
"atuin",
|
||||||
|
["history", "end", historyId, "--exit", String(exitCode)],
|
||||||
|
{ cwd, timeout: ATUIN_TIMEOUT_MS },
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// Ignore Atuin failures so command execution is never blocked.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The bash tool reports failures by appending a status line to the result
|
||||||
|
// text rather than exposing a numeric exit code, so recover it from there.
|
||||||
|
function exitCodeFromResult(result: unknown, isError: boolean): number {
|
||||||
|
if (!isError) return 0;
|
||||||
|
|
||||||
|
const content = (result as { content?: unknown } | undefined)?.content;
|
||||||
|
const text = Array.isArray(content)
|
||||||
|
? content
|
||||||
|
.map((part) => {
|
||||||
|
const t = (part as { text?: unknown } | undefined)?.text;
|
||||||
|
return typeof t === "string" ? t : "";
|
||||||
|
})
|
||||||
|
.join("\n")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
const exited = text.match(/Command exited with code (\d+)\s*$/);
|
||||||
|
if (exited) return Number(exited[1]);
|
||||||
|
if (/Command aborted\s*$/.test(text)) return 130;
|
||||||
|
if (/Command timed out after \S+ seconds\s*$/.test(text)) return 124;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function atuinPiExtension(pi: ExtensionAPI) {
|
||||||
|
// Atuin history IDs for in-flight bash tool calls, keyed by tool call ID.
|
||||||
|
const pending = new Map<string, string>();
|
||||||
|
|
||||||
|
// Observe bash executions through events instead of registering a bash
|
||||||
|
// tool: registering one conflicts with other extensions that provide
|
||||||
|
// their own bash tool (sandboxes, RTK, remote runners), while events
|
||||||
|
// fire no matter which extension's bash tool ends up executing the
|
||||||
|
// command.
|
||||||
|
pi.on("tool_call", async (event, ctx: ExtensionContext) => {
|
||||||
|
if (event.toolName !== "bash") return;
|
||||||
|
|
||||||
|
const command = (event.input as { command?: unknown }).command;
|
||||||
|
if (typeof command !== "string" || command.length === 0) return;
|
||||||
|
|
||||||
|
const historyId = await startHistory(pi, ctx.cwd, command);
|
||||||
|
if (historyId) pending.set(event.toolCallId, historyId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// tool_execution_end also fires when another extension blocks the call,
|
||||||
|
// unlike tool_result, so entries started above are always closed.
|
||||||
|
pi.on("tool_execution_end", async (event, ctx: ExtensionContext) => {
|
||||||
|
const historyId = pending.get(event.toolCallId);
|
||||||
|
if (!historyId) return;
|
||||||
|
pending.delete(event.toolCallId);
|
||||||
|
|
||||||
|
await endHistory(pi, ctx.cwd, historyId, exitCodeFromResult(event.result, event.isError));
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user