Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
cad099d
docs(spec): add workflow pane offload & resume RFC
flora131 May 8, 2026
a25e029
feat(offload): add OffloadResumeMetadata and MetadataJsonWithResume t…
lavaman131 May 8, 2026
af03f35
feat(offload): add OffloadManager skeleton with state machine and ide…
lavaman131 May 8, 2026
0cd45f3
feat(offload): add offloaded/resuming status rendering in panel compo…
lavaman131 May 8, 2026
4c94254
test(providers): add RFC §5.4 empty agentSessionId guard tests for al…
lavaman131 May 8, 2026
a78c8db
feat(offload): add mtime belt-and-suspenders to waitForClaudeReady (R…
lavaman131 May 8, 2026
2a166b9
feat(offload): wire claudeOffloadCleanup into OffloadManager.killOnePane
lavaman131 May 8, 2026
d8469d8
feat(ui): top-right toast stack with severity + auto-dismiss
lavaman131 May 9, 2026
a19d7fb
fix(providers): include server-mode flags in opencode/copilot resume …
lavaman131 May 9, 2026
c673f62
fix(providers/claude): match resume in SessionStart hook so claude-re…
lavaman131 May 9, 2026
2900359
feat(offload): per-stage offloadSession with chrome-tab focus-leave t…
lavaman131 May 9, 2026
72c2367
refactor(ui): extract STATUS_TABLE single-source for status helpers
lavaman131 May 9, 2026
705e546
fix(workflows/ralph): use systemPrompt.append for reviewer so outputF…
lavaman131 May 9, 2026
6c30f9c
chore(agents): neutralize host git config for ast-grep MCP server
lavaman131 May 9, 2026
8146e01
chore(examples): add .opencode config to hello-world example
lavaman131 May 9, 2026
e56d430
test(offload): add unit tests for shellQuote helper
lavaman131 May 9, 2026
a2cef00
fix(ui): defer SessionGraphPanel render until OffloadManager is attached
lavaman131 May 9, 2026
ca6a254
test(offload): add unit tests for telemetry sink
lavaman131 May 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .claude/agents/codebase-analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ mcpServers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
# Neutralize host git config so uvx's `git clone` is hermetic — no
# insteadOf rewrites, GPG signing, or hooks bleed in. /dev/null works
# on POSIX directly and on Windows via Git-for-Windows MSYS path
# translation; on native Windows git the path is missing → empty config.
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at understanding HOW code works. Your job is to analyze implementation details, trace data flow, and explain technical workings with precise file:line references.
Expand Down
3 changes: 3 additions & 0 deletions .claude/agents/codebase-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mcpServers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at finding WHERE code lives in a codebase. Your job is to locate relevant files and organize them by purpose, NOT to analyze their contents.
Expand Down
3 changes: 3 additions & 0 deletions .claude/agents/codebase-pattern-finder.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mcpServers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at finding code patterns and examples in the codebase. Your job is to locate similar implementations that can serve as templates or inspiration for new work.
Expand Down
3 changes: 3 additions & 0 deletions .claude/agents/codebase-research-analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mcpServers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at extracting HIGH-VALUE insights from thoughts documents. Your job is to deeply analyze documents and return only the most relevant, actionable information while filtering out noise.
Expand Down
3 changes: 3 additions & 0 deletions .claude/agents/codebase-research-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mcpServers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at finding documents in the research/ directory. Your job is to locate relevant research documents and categorize them, NOT to analyze their contents in depth.
Expand Down
7 changes: 7 additions & 0 deletions .github/agents/codebase-analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ mcp-servers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
# Neutralize host git config so uvx's `git clone` is hermetic — no
# insteadOf rewrites, GPG signing, or hooks bleed in. /dev/null works
# on POSIX directly and on Windows via Git-for-Windows MSYS path
# translation; on native Windows git the path is missing → empty config.
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at understanding HOW code works. Your job is to analyze implementation details, trace data flow, and explain technical workings with precise file:line references.
Expand Down
3 changes: 3 additions & 0 deletions .github/agents/codebase-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mcp-servers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at finding WHERE code lives in a codebase. Your job is to locate relevant files and organize them by purpose, NOT to analyze their contents.
Expand Down
3 changes: 3 additions & 0 deletions .github/agents/codebase-pattern-finder.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ mcp-servers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at finding code patterns and examples in the codebase. Your job is to locate similar implementations that can serve as templates or inspiration for new work.
Expand Down
3 changes: 3 additions & 0 deletions .github/agents/codebase-research-analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mcp-servers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at extracting HIGH-VALUE insights from thoughts documents. Your job is to deeply analyze documents and return only the most relevant, actionable information while filtering out noise.
Expand Down
3 changes: 3 additions & 0 deletions .github/agents/codebase-research-locator.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mcp-servers:
type: stdio
command: uvx
args: ["--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"]
env:
GIT_CONFIG_GLOBAL: /dev/null
GIT_CONFIG_SYSTEM: /dev/null
---

You are a specialist at finding documents in the research/ directory. Your job is to locate relevant research documents and categorize them, NOT to analyze their contents in depth.
Expand Down
47 changes: 39 additions & 8 deletions .opencode/opencode.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,25 @@
},
"codegraph": {
"type": "local",
"command": ["codegraph", "serve", "--mcp"],
"command": [
"codegraph",
"serve",
"--mcp"
],
"enabled": true
},
"ast-grep": {
"type": "local",
"command": ["uvx", "--from", "git+https://github.com/ast-grep/ast-grep-mcp", "ast-grep-server"],
"command": [
"uvx",
"--from",
"git+https://github.com/ast-grep/ast-grep-mcp",
"ast-grep-server"
],
"environment": {
"GIT_CONFIG_GLOBAL": "/dev/null",
"GIT_CONFIG_SYSTEM": "/dev/null"
},
"enabled": true
}
},
Expand All @@ -36,22 +49,40 @@
},
"agent": {
"codebase-locator": {
"tools": { "codegraph*": true, "ast-grep*": true }
"tools": {
"codegraph*": true,
"ast-grep*": true
}
},
"codebase-pattern-finder": {
"tools": { "codegraph*": true, "ast-grep*": true }
"tools": {
"codegraph*": true,
"ast-grep*": true
}
},
"codebase-analyzer": {
"tools": { "codegraph*": true, "ast-grep*": true }
"tools": {
"codegraph*": true,
"ast-grep*": true
}
},
"codebase-online-researcher": {
"tools": { "codegraph*": true, "ast-grep*": false }
"tools": {
"codegraph*": true,
"ast-grep*": false
}
},
"codebase-research-locator": {
"tools": { "codegraph*": true, "ast-grep*": true }
"tools": {
"codegraph*": true,
"ast-grep*": true
}
},
"codebase-research-analyzer": {
"tools": { "codegraph*": true, "ast-grep*": true }
"tools": {
"codegraph*": true,
"ast-grep*": true
}
}
},
"permission": "allow",
Expand Down
27 changes: 27 additions & 0 deletions examples/hello-world/.opencode/opencode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"azure-devops": {
"type": "local",
"command": [
"bunx",
"-y",
"@azure-devops/mcp",
"<your-org>"
],
"enabled": false
},
"github-mcp-server": {
"type": "remote",
"url": "https://api.githubcopilot.com/mcp",
"enabled": true,
"headers": {
"Authorization": "Bearer {env:GH_TOKEN}"
}
}
},
"permission": "allow",
"instructions": [
"~/.atomic/AGENTS.md"
]
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"test": "bun test --parallel packages tests",
"test:coverage": "bun test --coverage packages tests",
"typecheck": "bun --filter '*' typecheck",
"lint": "oxlint --config=oxlint.json packages && bun run scripts/lint-custom-workflows.ts && bun run --filter @bastani/atomic-sdk lint:mcp && bun run --filter @bastani/atomic-sdk lint:file-discovery",
"lint": "oxlint --config=oxlint.json packages && bun run scripts/lint-custom-workflows.ts && bun run --filter @bastani/atomic-sdk lint:mcp && bun run --filter @bastani/atomic-sdk lint:file-discovery && bun run lint:offload-await",
"lint:offload-await": "bun run scripts/lint-offload-await.ts",
"lint:fix": "oxlint --config=oxlint.json --fix packages",
"prepare": "[ -n \"$CI\" ] || prek install || true"
},
Expand Down
1 change: 1 addition & 0 deletions packages/atomic-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"./providers/copilot": "./src/providers/copilot.ts",
"./providers/claude-stop-hook": "./src/providers/claude-stop-hook.ts",
"./providers/claude-inflight-hook": "./src/providers/claude-inflight-hook.ts",
"./lib/telemetry": "./src/lib/telemetry/index.ts",
"./lib/atomic-temp": "./src/lib/atomic-temp.ts",
"./lib/spawn": "./src/lib/spawn.ts",
"./lib/terminal-env": "./src/lib/terminal-env.ts",
Expand Down
4 changes: 3 additions & 1 deletion packages/atomic-sdk/src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function Header() {
const storeVersion = useStoreVersion(store);

const counts = useMemo(() => {
const c: Record<SessionStatus, number> = { complete: 0, running: 0, pending: 0, error: 0, awaiting_input: 0 };
const c: Record<SessionStatus, number> = { complete: 0, running: 0, pending: 0, error: 0, awaiting_input: 0, offloaded: 0, resuming: 0 };
for (const s of store.sessions) c[s.status]++;
return c;
}, [storeVersion]);
Expand Down Expand Up @@ -79,6 +79,8 @@ export function Header() {
<CountBadge color={theme.info} backgroundColor={theme.backgroundElement} icon={"?"} count={counts.awaiting_input} />
<CountBadge color={theme.textDim} backgroundColor={theme.backgroundElement} icon={"\u25CB"} count={counts.pending} />
<CountBadge color={theme.error} backgroundColor={theme.backgroundElement} icon={"\u2717"} count={counts.error} />
<CountBadge color={theme.textDim} backgroundColor={theme.backgroundElement} icon={"\u25cc"} count={counts.offloaded} />
<CountBadge color={theme.warning} backgroundColor={theme.backgroundElement} icon={"\u25d0"} count={counts.resuming} />
</box>
</box>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/** @jsxImportSource @opentui/react */
/**
* Tests for OrchestratorPanel.attachOffloadManager — setter-based wiring.
*/

import { test, expect, mock } from "bun:test";
import { OrchestratorPanel } from "./orchestrator-panel.tsx";
import type { OffloadManager } from "../runtime/offload-manager.ts";
import type { CliRenderer } from "@opentui/core";

// ─── Helpers ─────────────────────────────────────────────────────────────────

function makeStubRenderer(): CliRenderer {
// Minimal stub satisfying the surface used by createWithRenderer.
// Note: React's scheduler dispatches async reconciler work after render;
// this stub intentionally omits low-level renderer internals (getChildren,
// etc.) so those async tasks may throw unhandled errors. The synchronous
// test assertions below still pass correctly.
return {
themeMode: null,
width: 80,
height: 24,
widthMethod: "terminal",
root: {
children: [],
getChildren: () => [],
requestRender: mock(() => {}),
add: mock(() => {}),
remove: mock(() => {}),
} as unknown as CliRenderer["root"],
setBackgroundColor: mock(() => {}),
requestRender: mock(() => {}),
addInputHandler: mock(() => {}),
removeInputHandler: mock(() => {}),
on: mock(() => ({}) as unknown as CliRenderer),
once: mock(() => ({}) as unknown as CliRenderer),
off: mock(() => ({}) as unknown as CliRenderer),
emit: mock(() => false),
destroy: mock(() => {}),
resetTerminalBgColor: mock(() => {}),
setFrameCallback: mock(() => {}),
removeFrameCallback: mock(() => {}),
clearFrameCallbacks: mock(() => {}),
requestLive: mock(() => {}),
dropLive: mock(() => {}),
} as unknown as CliRenderer;
}

function makeStubOffloadManager(): OffloadManager {
return {
registerSession: mock(async () => {}),
offloadSession: mock(async () => {}),
onWorkflowCompletion: mock(async () => {}),
requestResume: mock(async () => {}),
getStatus: mock(() => "alive" as const),
};
}

// ─── Tests ────────────────────────────────────────────────────────────────────

test("OrchestratorPanel exposes attachOffloadManager method", () => {
const renderer = makeStubRenderer();
const panel = OrchestratorPanel.createWithRenderer(renderer, { tmuxSession: "test-session" });
expect(typeof panel.attachOffloadManager).toBe("function");
panel.destroy();
});

test("attachOffloadManager does not throw when called with valid manager", () => {
const renderer = makeStubRenderer();
const panel = OrchestratorPanel.createWithRenderer(renderer, { tmuxSession: "test-session" });
const mgr = makeStubOffloadManager();
expect(() => panel.attachOffloadManager(mgr)).not.toThrow();
panel.destroy();
});

test("attachOffloadManager is idempotent — calling twice does not throw", () => {
const renderer = makeStubRenderer();
const panel = OrchestratorPanel.createWithRenderer(renderer, { tmuxSession: "test-session" });
const mgr = makeStubOffloadManager();
expect(() => {
panel.attachOffloadManager(mgr);
panel.attachOffloadManager(mgr);
}).not.toThrow();
panel.destroy();
});

test("attachOffloadManager returns void", () => {
const renderer = makeStubRenderer();
const panel = OrchestratorPanel.createWithRenderer(renderer, { tmuxSession: "test-session" });
const mgr = makeStubOffloadManager();
const result = panel.attachOffloadManager(mgr);
expect(result).toBeUndefined();
panel.destroy();
});
Loading
Loading