Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .agents/skills/ab-graphify/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
name: ab-graphify
description: "Graphify maps the codebase into a queryable structural knowledge graph for research and architecture navigation."
---

# ab-graphify

Graphify maps your entire codebase into a queryable structural knowledge graph stored at
Expand Down
5 changes: 5 additions & 0 deletions .claude/skills/ab-graphify/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
name: ab-graphify
description: "Graphify maps the codebase into a queryable structural knowledge graph for research and architecture navigation."
---

# ab-graphify

Graphify maps your entire codebase into a queryable structural knowledge graph stored at
Expand Down
51 changes: 51 additions & 0 deletions .platform/domains/vscode-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
domain_id: dom-vscode-extension
slug: vscode-extension
status: active
repo_ids: [repo-primary]
related_domain_slugs: [orchestration, templates]
created_at: 2026-06-26
updated_at: 2026-06-26
---

# vscode-extension

## What this domain does

This domain owns the Agentboard VS Code extension and the runtime files that feed its live dashboard. It lets a developer see active agent sessions, workstreams, catalog metadata, file activity, cost, model, and staleness inside the editor.

## Backend / source of truth

- `~/.agentboard/sessions/*.json` is the cross-provider live session snapshot source for the dashboard.
- `.platform/events.jsonl` is the per-project activity/event source used by the activity feed and code activity stats.
- Provider hooks and wrappers must normalize to the existing Claude-compatible session/event schemas instead of forcing dashboard-specific provider branches.

## Frontend / clients

- `extensions/vscode/src/dashboardPanel.ts` renders the Live/Catalog dashboard webview.
- `extensions/vscode/src/extension.ts` registers commands, sidebar providers, and dashboard open/refresh behavior.
- `extensions/vscode/src/workspaceRoot.ts` resolves the active Agentboard workspace root.

## API contract locked

- Session JSON must remain provider-neutral enough for Claude and Codex to appear together.
- Event records must stay newline-delimited JSON in `.platform/events.jsonl`.
- Runtime artifacts remain ignored by git; templates and hook scripts are tracked.
- The dashboard should prefer the current VS Code workspace over stale global live metadata.

## Key files

- `extensions/vscode/src/dashboardPanel.ts`
- `extensions/vscode/src/extension.ts`
- `extensions/vscode/src/workspaceRoot.ts`
- `templates/platform/scripts/hooks/status-bridge.js`
- `templates/platform/scripts/hooks/event-logger.sh`
- `templates/platform/scripts/session-track.sh`
- `templates/codex/`
- `templates/root/AGENTS.md.template`

## Decisions locked

- Keep the dashboard data model provider-compatible instead of adding separate Claude/Codex UI paths.
- Keep runtime telemetry local and gitignored.
- Prefer small hook/wrapper adapters that emit the existing session/event formats.
1 change: 1 addition & 0 deletions .platform/memory/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,4 @@ Format: `YYYY-MM-DD — <task> — <outcome> — <takeaway>`
2026-06-14 — Silicon Valley product mindset — added provider-neutral workflow, role, root-entry, and contract-test coverage so PM/engineering agents pursue best-in-class product ambition with explicit scope and approval guardrails
2026-06-15 — manual QA artifact gate — upgraded manual QA from chat-only final plan to stream-scoped markdown artifact gate with archived QA history — future agents must ship with executable QA evidence or a documented not-required reason
2026-06-15 — QA execution journal — added a chronological journal requirement for LLM-driven interactive QA so Maestro/browser agents document what they did, saw, fixed, retested, passed, skipped, and escalated
2026-06-26 — Codex dashboard telemetry — Codex now emits Claude-compatible session snapshots and dashboard-readable file activity through native hooks plus wrapper fallback
2 changes: 1 addition & 1 deletion .platform/roles/docs-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ reveal to be confusing.
- **Prioritised fix list** — ordered by user-impact; each item names the doc
location, the problem, and the correct current behaviour

## Hard Rules
## Constraints

- **Every inaccuracy finding includes the current correct behaviour** — do not
just flag it, state the truth.
Expand Down
191 changes: 191 additions & 0 deletions .platform/scripts/hooks/codex-hook-bridge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#!/usr/bin/env node
/**
* codex-hook-bridge.js — normalize Codex native hook payloads for Agentboard.
*
* Codex project hooks run in the session cwd and send event payloads on stdin.
* This adapter writes the same session JSON and events.jsonl shapes that the
* VS Code dashboard already consumes for Claude Code.
*/

'use strict';

const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const { writeSnapshot, deriveSessionId, findProjectRoot } = require('./session-snapshot.js');

const MAX_STDIN = 512 * 1024;

function readPayload() {
try {
if (process.stdin.isTTY) return {};
const raw = fs.readFileSync(0, { encoding: 'utf8' }).slice(0, MAX_STDIN).trim();
return raw ? JSON.parse(raw) : {};
} catch {
return {};
}
}

function get(obj, pathExpr) {
let cur = obj;
for (const part of pathExpr.split('.')) {
if (cur == null || typeof cur !== 'object') return undefined;
cur = cur[part];
}
return cur;
}

function firstString(...values) {
for (const value of values) {
if (typeof value === 'string' && value.trim()) return value.trim();
if (typeof value === 'number' && Number.isFinite(value)) return String(value);
}
return '';
}

function toolName(payload) {
return firstString(
payload.tool_name,
payload.toolName,
payload.tool,
payload.name,
payload.matcher,
get(payload, 'tool.name'),
get(payload, 'tool_call.name'),
get(payload, 'toolCall.name'),
);
}

function eventName(payload) {
return firstString(
process.env.AGENTBOARD_CODEX_HOOK_EVENT,
payload.hook_event_name,
payload.hookEventName,
payload.event,
payload.event_name,
payload.type,
);
}

function filePath(payload) {
return firstString(
payload.file_path,
payload.filePath,
payload.path,
get(payload, 'tool_input.file_path'),
get(payload, 'tool_input.path'),
get(payload, 'toolInput.file_path'),
get(payload, 'toolInput.path'),
get(payload, 'input.file_path'),
get(payload, 'input.path'),
get(payload, 'params.file_path'),
get(payload, 'params.path'),
get(payload, 'arguments.file_path'),
get(payload, 'arguments.path'),
);
}

function command(payload) {
return firstString(
payload.command,
get(payload, 'tool_input.command'),
get(payload, 'toolInput.command'),
get(payload, 'input.command'),
get(payload, 'params.command'),
get(payload, 'arguments.command'),
);
}

function label(payload) {
return firstString(
payload.label,
payload.subagent_type,
payload.subagentType,
payload.agent_type,
payload.agentType,
payload.name,
get(payload, 'subagent.type'),
get(payload, 'agent.type'),
);
}

function runEventLogger(root, payload, env = {}) {
const hook = path.join(root, '.platform', 'scripts', 'hooks', 'event-logger.sh');
if (!fs.existsSync(hook)) return;
spawnSync('bash', [hook], {
cwd: root,
input: JSON.stringify(payload),
stdio: ['pipe', 'ignore', 'ignore'],
env: {
...process.env,
AGENTBOARD_PROVIDER: 'codex',
...env,
},
timeout: 3000,
});
}

function normalizePayload(payload, hookEvent, sessionId) {
const t = toolName(payload);
const fp = filePath(payload);
const cmd = command(payload);
const out = {
...payload,
hook_event_name: hookEvent,
session_id: sessionId,
};

if (!out.tool_name && t) out.tool_name = t === 'apply_patch' ? 'Edit' : t;
if (fp && !out.file_path) out.file_path = fp;
if (cmd && !out.command) out.command = cmd;
return out;
}

function main() {
const payload = readPayload();
const root = findProjectRoot(firstString(payload.cwd, get(payload, 'workspace.current_dir')) || process.cwd());
const provider = 'codex';
const sessionId = deriveSessionId(payload, provider);
const hookEvent = eventName(payload) || 'PostToolUse';

writeSnapshot(payload, { provider, sessionId, cwd: root });

if (hookEvent === 'SubagentStart') {
runEventLogger(root, {
hook_event_name: hookEvent,
session_id: sessionId,
tool_name: 'Agent',
label: label(payload) || 'sub-agent',
subagent_type: label(payload) || 'sub-agent',
}, { AGENTBOARD_HOOK_TYPE: 'agent_start' });
return;
}

if (hookEvent === 'SubagentStop') {
runEventLogger(root, {
hook_event_name: hookEvent,
session_id: sessionId,
tool_name: 'Agent',
label: label(payload) || 'sub-agent',
subagent_type: label(payload) || 'sub-agent',
}, { AGENTBOARD_HOOK_TYPE: 'agent_done' });
return;
}

if (hookEvent === 'SessionStart' || hookEvent === 'Stop') {
runEventLogger(root, {
hook_event_name: hookEvent === 'Stop' ? 'SessionEnd' : 'SessionStart',
session_id: sessionId,
provider,
});
return;
}

runEventLogger(root, normalizePayload(payload, hookEvent, sessionId));
}

try {
main();
} catch {
process.exit(0);
}
Loading
Loading