@hybridlabor-api/aos 4.16.0 → 4.17.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.
Files changed (73) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude/hooks/env-file-protection.mjs +19 -1
  3. package/.claude/hooks/go-gate.mjs +787 -68
  4. package/.claude/hooks/go-grant.mjs +88 -0
  5. package/.claude/settings.json +8 -0
  6. package/.codex-plugin/plugin.json +36 -6
  7. package/.opencode/commands/bdb-aos-brainstorm.md +5 -0
  8. package/.opencode/commands/bdb-aos-doctor.md +5 -0
  9. package/.opencode/commands/bdb-aos-graph.md +5 -0
  10. package/.opencode/commands/bdb-aos-init.md +5 -0
  11. package/.opencode/commands/bdb-aos-loop.md +5 -0
  12. package/.opencode/commands/bdb-aos-mastersession.md +5 -0
  13. package/.opencode/commands/bdb-aos-memb.md +5 -0
  14. package/.opencode/commands/bdb-aos-orchestrator.md +5 -0
  15. package/.opencode/commands/bdb-aos-plan.md +5 -0
  16. package/.opencode/commands/bdb-aos-playbooks.md +5 -0
  17. package/.opencode/commands/bdb-aos-setup.md +5 -0
  18. package/.opencode/commands/bdb-aos-shipping.md +5 -0
  19. package/.opencode/commands/bdb-aos-startproject.md +5 -0
  20. package/.opencode/commands/bdb-aos-store.md +5 -0
  21. package/.opencode/plugins/bdb-aos.js +54 -10
  22. package/README.md +4 -4
  23. package/agy-commands/loop.md +5 -0
  24. package/bin/aos-doctor.mjs +5 -3
  25. package/bin/aos-uninstall.mjs +25 -1
  26. package/commands/brainstorm.md +5 -0
  27. package/commands/doctor.md +5 -0
  28. package/commands/graph.md +5 -0
  29. package/commands/init.md +5 -0
  30. package/commands/loop.md +5 -0
  31. package/commands/mastersession.md +5 -0
  32. package/commands/memb.md +5 -0
  33. package/commands/orchestrator.md +5 -0
  34. package/commands/plan.md +5 -0
  35. package/commands/playbooks.md +5 -0
  36. package/commands/setup.md +5 -0
  37. package/commands/shipping.md +5 -0
  38. package/commands/startproject.md +5 -0
  39. package/commands/store.md +5 -0
  40. package/docs/codex-agy-setup.md +16 -0
  41. package/docs/opencode-setup.md +36 -0
  42. package/docs/plugin-migration.md +61 -0
  43. package/installer.js +223 -52
  44. package/lib/plugin-migration.js +462 -0
  45. package/lib/store-ui/index.html +9 -1
  46. package/lib/store-ui/server.mjs +2 -0
  47. package/package.json +7 -2
  48. package/plugin-commands.json +144 -0
  49. package/plugin.json +302 -0
  50. package/plugins/bdb-aos-codex/.codex-plugin/plugin.json +38 -0
  51. package/plugins/bdb-aos-codex/skills/brainstorm/SKILL.md +6 -0
  52. package/plugins/bdb-aos-codex/skills/doctor/SKILL.md +6 -0
  53. package/plugins/bdb-aos-codex/skills/graph/SKILL.md +6 -0
  54. package/plugins/bdb-aos-codex/skills/init/SKILL.md +6 -0
  55. package/plugins/bdb-aos-codex/skills/loop/SKILL.md +6 -0
  56. package/plugins/bdb-aos-codex/skills/mastersession/SKILL.md +6 -0
  57. package/plugins/bdb-aos-codex/skills/memb/SKILL.md +6 -0
  58. package/plugins/bdb-aos-codex/skills/orchestrator/SKILL.md +6 -0
  59. package/plugins/bdb-aos-codex/skills/plan/SKILL.md +6 -0
  60. package/plugins/bdb-aos-codex/skills/playbooks/SKILL.md +6 -0
  61. package/plugins/bdb-aos-codex/skills/setup/SKILL.md +6 -0
  62. package/plugins/bdb-aos-codex/skills/shipping/SKILL.md +6 -0
  63. package/plugins/bdb-aos-codex/skills/startproject/SKILL.md +6 -0
  64. package/plugins/bdb-aos-codex/skills/store/SKILL.md +6 -0
  65. package/scripts/build-plugin-manifest.mjs +202 -3
  66. package/skills/bdb-aos/scripts/list-playbooks.mjs +72 -0
  67. package/skills/global_config/gogate/SKILL.md +123 -0
  68. package/skills/global_config/loop-templates/SKILL.md +27 -0
  69. package/skills/global_config/loop-templates/references/ci-until-green.md +27 -0
  70. package/skills/global_config/loop-templates/references/daily-summary.md +27 -0
  71. package/skills/global_config/loop-templates/references/pr-to-merge.md +27 -0
  72. package/skills/global_config/loop-templates/references/review-rounds.md +27 -0
  73. package/.codex-plugin/marketplace.json +0 -11
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ // UserPromptSubmit hook for the go-gate modes and grants. When the human types,
3
+ // as the whole message (plain form, primary):
4
+ // gogate grant <scope[,scope...]> <15m|2h|1d|session>
5
+ // gogate hard|soft|off|status
6
+ // or the slash form /bdb-aos:gogate ... (rarely verifies, the hook says so),
7
+ // it records the request in ~/.aos/gate/<session-key>.json with a pointer to
8
+ // the prompt (transcript, issue time, text hash). Nothing here is trusted on
9
+ // its own: go-gate.mjs re-reads the transcript at use time and honours an
10
+ // entry only if a human-typed (origin-checked) prompt with that hash exists.
11
+ // A loop, peer, bus or agent prompt may reach this hook, but its transcript
12
+ // entry is not human, so what it records never verifies.
13
+ //
14
+ // CLI: node go-grant.mjs --status [--session <id>] (read-only, prints status)
15
+
16
+ import { readFileSync, realpathSync } from "node:fs";
17
+ import { fileURLToPath } from "node:url";
18
+ import {
19
+ parseGogate, textHash, sessionKey, readState, writeState, gateLog, effectiveGate,
20
+ gogateCommandsFor, statusText, listStateKeys, OPENCODE_MAX_GRANT_MS,
21
+ } from "./go-gate.mjs";
22
+
23
+ const emptyState = (source) => ({ transcript: source, grants: [] });
24
+
25
+ // Applies a gogate prompt to the store. `source` is the transcript path or
26
+ // "opencode:<sessionID>"; `uuid` the message id when the harness knows it.
27
+ // Returns the text for the model (additionalContext), or null for non-gogate prompts.
28
+ export function applyGogate(prompt, { key, source, uuid = null, now = Date.now(), opencode = false }) {
29
+ const p = parseGogate(prompt);
30
+ if (!p) return null;
31
+ const slashNote = p.form === "slash"
32
+ ? ` Note: this was a slash command. ${opencode ? "OpenCode accepts only the plain form" : "Claude Code stores slash commands without human-origin data, so the gate will most likely ignore it"}; to make it count, type the plain form as the whole message: \`gogate ${p.cmd === "grant" ? `grant ${p.scopes.join(",")} ${p.session ? "session" : "<duration>"}` : p.cmd || "status"}\`.`
33
+ : "";
34
+ if (p.error) return `gogate: ${p.error}. Nothing changed.`;
35
+ if (!key || !source) return "gogate: no session id in hook input, nothing recorded.";
36
+ if (process.env.AOS_ACP_CLIENT) return "gogate: an aos-acp worker session cannot set modes or grants.";
37
+ if (p.cmd === "status") return status(key);
38
+ if (opencode && p.cmd === "off") return "gogate: `off` is not available on OpenCode (no human-origin signal there). Nothing changed.";
39
+ let st = readState(key);
40
+ if (!st || typeof st !== "object" || st.transcript !== source) st = emptyState(source);
41
+ const src = { transcript: source, uuid, issued_at: new Date(now).toISOString(), text_hash: textHash(prompt) };
42
+ if (p.cmd === "grant") {
43
+ const ms = opencode ? Math.min(p.ms, OPENCODE_MAX_GRANT_MS) : p.ms;
44
+ st.grants = (Array.isArray(st.grants) ? st.grants : []).filter((g) => Date.parse(g?.until) > now);
45
+ for (const scope of p.scopes) st.grants.push({ scope, until: new Date(now + ms).toISOString(), ...(p.session ? { session_key: key } : {}), source: src });
46
+ } else {
47
+ // Append, never overwrite: a later prompt that does not verify must not erase the
48
+ // store entry of the mode that is still in force.
49
+ st.modes = [...(Array.isArray(st.modes) ? st.modes : []), { mode: p.cmd, source: src }].slice(-20);
50
+ }
51
+ try {
52
+ writeState(key, st);
53
+ } catch (e) {
54
+ return `gogate: could NOT record ${p.cmd}: writing ~/.aos/gate failed (${e.code || e.message}). Nothing changed; the gate stays as it was. Tell the human.`;
55
+ }
56
+ gateLog(key, `recorded ${p.form} ${p.cmd}${p.scopes ? ` ${p.scopes.join(",")} ${p.session ? "session" : `${p.ms / 60000}m`}` : ""}`);
57
+ return [
58
+ `gogate: recorded ${p.cmd}${p.scopes ? ` for ${p.scopes.join(", ")}` : ""}${p.session ? " (session = this session id, at most 24h, survives --resume)" : ""}${opencode && p.cmd === "grant" && p.ms > OPENCODE_MAX_GRANT_MS ? " (capped at 2h on OpenCode)" : ""}.`,
59
+ "The gate verifies it against this prompt in the transcript on every use.",
60
+ slashNote,
61
+ "Tell the human what was recorded; never try to change modes or grants yourself.",
62
+ ].filter(Boolean).join(" ");
63
+ }
64
+
65
+ export function status(key) {
66
+ const st = readState(key);
67
+ const eff = st ? effectiveGate(key, { source: st.transcript, cmds: gogateCommandsFor(st.transcript), opencode: String(st.transcript).startsWith("opencode:") }) : { mode: "soft", grants: [], rejected: [] };
68
+ return statusText(key, eff);
69
+ }
70
+
71
+ const isMain = () => { try { return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url)); } catch { return false; } };
72
+
73
+ if (isMain()) {
74
+ const argv = process.argv.slice(2);
75
+ if (argv.includes("--status")) {
76
+ const i = argv.indexOf("--session");
77
+ const id = i !== -1 ? argv[i + 1] : process.env.AOS_GATE_SESSION || process.env.CLAUDE_SESSION_ID;
78
+ const keys = id ? [sessionKey(id)] : listStateKeys();
79
+ console.log(keys.length ? keys.map(status).join("\n\n") : "AOS go-gate: no session state; every session runs in soft mode with no grants (behaves like hard).");
80
+ process.exit(0);
81
+ }
82
+ try {
83
+ const input = JSON.parse(readFileSync(0, "utf8"));
84
+ const text = applyGogate(input.prompt, { key: sessionKey(input.session_id), source: input.transcript_path });
85
+ if (text) console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: "UserPromptSubmit", additionalContext: text } }));
86
+ } catch { /* a failing hook must never block the prompt */ }
87
+ process.exit(0);
88
+ }
@@ -123,6 +123,14 @@
123
123
  }
124
124
  ]
125
125
  },
126
+ {
127
+ "hooks": [
128
+ {
129
+ "type": "command",
130
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/go-grant.mjs\""
131
+ }
132
+ ]
133
+ },
126
134
  {
127
135
  "hooks": [
128
136
  {
@@ -1,8 +1,38 @@
1
1
  {
2
- "name": "bdb-dev-optimized-agent-skills",
3
- "version": "2.3.0",
4
- "description": "BDB DEV Optimized Agent Skills and MCP pack for Codex CLI, Claude, Cursor and Antigravity",
5
- "author": "hybridlabor-api",
6
- "skills_dir": "./skills/global_config",
7
- "mcp_config": "./.codex-plugin/.mcp.json"
2
+ "name": "bdb-aos",
3
+ "version": "4.17.0",
4
+ "description": "Full BDB Agent OS suite for Codex: command entry points ($bdb-aos:setup, $bdb-aos:init, ...) generated from plugin-commands.json.",
5
+ "author": {
6
+ "name": "Tim Rennings / Hybridlabor Global LLC",
7
+ "email": "hybridlabor@gmail.com"
8
+ },
9
+ "homepage": "https://github.com/hybridlabor-api/aos",
10
+ "repository": "https://github.com/hybridlabor-api/aos",
11
+ "license": "Apache-2.0",
12
+ "keywords": [
13
+ "skills",
14
+ "agent-os",
15
+ "cicd",
16
+ "multiagent",
17
+ "startcycle",
18
+ "bdb"
19
+ ],
20
+ "skills": "./plugins/bdb-aos-codex/skills/",
21
+ "interface": {
22
+ "displayName": "BDB Agent OS",
23
+ "shortDescription": "AOS commands for setup, planning and pipelines",
24
+ "longDescription": "Entry-point skills for the BDB Agent OS suite: invoke $bdb-aos:<command> to run setup, project init, planning and the startcycle pipelines.",
25
+ "developerName": "Hybridlabor Global LLC",
26
+ "category": "Developer Tools",
27
+ "capabilities": [
28
+ "Interactive",
29
+ "Read",
30
+ "Write"
31
+ ],
32
+ "websiteURL": "https://github.com/hybridlabor-api/aos",
33
+ "defaultPrompt": [
34
+ "$bdb-aos:setup",
35
+ "$bdb-aos:doctor"
36
+ ]
37
+ }
8
38
  }
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Brainstorm a topic with the matching skill: bdbrainstorm, bdbmediastorm for event tech, grill-me for a stress test."
3
+ ---
4
+
5
+ Pick by topic: `bdbmediastorm` for live event technology and show control, `grill-me` when the human wants an existing idea stress-tested, `bdbrainstorm` otherwise. Say which one you chose. Topic: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Check the machine and the current project, and print suggested permissions.allow entries without writing any settings."
3
+ ---
4
+
5
+ Run `aos-doctor` (machine check) and report the result. If the current folder is an AOS project, also run the project check `node <aos-project-init skill dir>/scripts/aos-project-doctor.mjs` (it covers `.aos/project.json`, AGENTS.md and the wiki). Print suggested `permissions.allow` entries as text for the human to copy. Never write to settings.json or settings.local.json. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Run the startcycle-graph pipeline: durable state, repair loop, quality gate, escalation."
3
+ ---
4
+
5
+ Run the `startcycle-graph` skill; its contract defines the flow. Task: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Set up the current project folder for AOS: slug, wiki, memory, Synapse map, AGENTS.md."
3
+ ---
4
+
5
+ Run the `aos-project-init` skill for the current project folder. This is project setup, not machine setup (that is `/bdb-aos-setup`). Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Run a prompt repeatedly on an interval, using the harness built-in plus loop templates."
3
+ ---
4
+
5
+ OpenCode has no built-in loop. Use `opencode-loop`, the opt-in package (AOS_OPENCODE_OPTIONAL=loop, see docs/opencode-setup.md); without it, say so and stop. Do not schedule gated commands through its shell loop. Offer a `loop-templates` template when the human has no prompt yet. Safety: a loop prompt never issues a GO, never types or runs `gogate`, never sets a mode or grant, and never merges, pushes or publishes unless a valid grant covers the action. Act on a guarded step only if the human's immediately preceding message is a literal GO or a valid grant covers it; a loop iteration never satisfies the first condition, so it must STOP and report instead of continuing a blocked guarded step. Templates (CI until green, follow a PR to merge, review rounds until no blockers, daily session summary) live in the `loop-templates` skill with run and duration limits and a stop condition each. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Supervise other Claude Code sessions on this machine: roster, status requests, GO board, idle notices."
3
+ ---
4
+
5
+ Act as the master session with the `master-session` skill (ACP bridge: `bin/aos-acp.mjs`, see docs/master-session-acp.md). Keep the overview, relay the human's decisions, never decide for the human. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Query or store long-term memory with memB."
3
+ ---
4
+
5
+ Use `memb-skill` to search or remember and `memb-ingest` to import material. Never store secrets, tokens or credentials. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Coordinate parallel coding agents across repositories through the AO daemon."
3
+ ---
4
+
5
+ Use the `ao-orchestrator` skill. It needs the AO daemon; check that it is running first and stop with a clear message if not. Do not confuse it with `agent-orchestrator`. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Plan end to end: draft, render in plan-canvas or Plan Builder, annotate, await approval, then hand off."
3
+ ---
4
+
5
+ Run the full planning pipeline: 1) draft the plan with `concise-planning`; 2) always offer the choice between plan-canvas and Plan Builder (`visual-plan`) for rendering; 3) collect annotations and wait for the human's approval; 4) only after approval continue with `writing-plans` or `/bdb-aos-graph`. Topic: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "List all pb-* playbooks with duration, difficulty and requirements, and start one."
3
+ ---
4
+
5
+ Run `node <bdb-aos skill dir>/scripts/list-playbooks.mjs` and show its output (name, est_time, difficulty, requires of every `pb-*` skill). If the human names a playbook in the arguments or picks one, start that `pb-*` skill; start nothing otherwise. Each playbook keeps its own go-gate points. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Bring this machine to a complete, verified AOS installation (skills, MCP servers, gate hooks, memB, OpenWiki, Synapse)."
3
+ ---
4
+
5
+ Run the `aos-setup` skill for this machine. Report what is installed, what was repaired and what is still missing. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Pre-ship framing and the technical release gate, with approvals under the go-gate rules."
3
+ ---
4
+
5
+ Use `bdb-shipping-skill` for the pre-flight and `godmode-shipping` for the gate. The playbook `pb-ship` can drive the whole flow. Push, merge and publish need the human's GO under the go-gate rules; never request one on the human's behalf. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Start a new composition: project init, brainstorm, then ask whether graph or startcycle follows."
3
+ ---
4
+
5
+ Sequence: 1) `aos-project-init`; 2) `bdbrainstorm`; 3) stop and ask the human whether `/bdb-aos-graph` (startcycle-graph) or `startcycle` follows. Do not start a pipeline on your own. Idea: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Browse and preview AOS skills, agents and playbooks in the local store UI."
3
+ ---
4
+
5
+ Start the `aos-store` interface. Preview freely, but install nothing until the human says yes for that specific item. Arguments: $ARGUMENTS
@@ -23,8 +23,11 @@ import path from 'node:path';
23
23
  // so a broken installed hook fails loudly instead of silently falling back.
24
24
  const HOOKS = existsSync(new URL('./aos-hooks/go-gate.mjs', import.meta.url)) ? './aos-hooks/' : '../../.claude/hooks/';
25
25
  const hook = (f) => import(new URL(HOOKS + f, import.meta.url).href);
26
- const [{ GUARDED_PATTERNS, tokenGrantsGo }, { issueGoToken }, { checkConventionalCommit }, { envFileReason }, { buildMemoryBlock }, bus] =
27
- await Promise.all(['go-gate.mjs', 'go-token.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs', 'memb-inject.mjs', 'aos-bus.mjs'].map(hook));
26
+ // `opencode run "..."` is scriptable by an agent, so it never sets gogate modes or grants
27
+ const OPENCODE_RUN = process.argv.slice(1, 4).includes('run');
28
+ const [gate, { issueGoToken }, { checkConventionalCommit }, { envFileReason }, { buildMemoryBlock }, bus, { applyGogate }] =
29
+ await Promise.all(['go-gate.mjs', 'go-token.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs', 'memb-inject.mjs', 'aos-bus.mjs', 'go-grant.mjs'].map(hook));
30
+ const { GUARDED_PATTERNS, tokenGrantsGo, isHumanPart, parseGoText, goAllows, isGuardedCommand, gateStoreReason, effectiveGate, opencodeGogateCommands, grantsCover, sessionKey, gateLog } = gate;
28
31
 
29
32
  // ---------------------------------------------------------------------------
30
33
  // Graph gate (W-5, W-6)
@@ -131,9 +134,26 @@ async function ownSessionName(client, sessionID) {
131
134
  }
132
135
 
133
136
  // Only plain text parts are human turns: synthetic = model-only, ignored = display-only,
134
- // aos_bus = bus delivery (both copies). Each excluded on its own, so safety never hangs on one flag.
135
- const isHumanText = (p) => p && p.type === 'text' && typeof p.text === 'string' && !p.synthetic && !p.ignored && !p.metadata?.aos_bus;
136
- const isNonHuman = (p) => p && (p.synthetic || p.ignored || p.metadata?.aos_bus);
137
+ // aos_bus / aos_loop (any loop|aos_ metadata key) = plugin-injected. Each excluded on its own,
138
+ // so safety never hangs on one flag.
139
+ const isHumanText = isHumanPart;
140
+ const isNonHuman = (p) => !!p && !!(p.synthetic || p.ignored || (p.metadata && typeof p.metadata === 'object' && Object.keys(p.metadata).some((k) => /loop|aos_/i.test(k))));
141
+ const ocKey = (id) => sessionKey(`oc-${id}`);
142
+
143
+ // Root of a session tree (grants live on the root, subagents may use them). null when unknown.
144
+ async function rootSessionId(client, id, parentID) {
145
+ for (let i = 0; i < 10 && id; i++) {
146
+ if (parentID === null) return id;
147
+ try {
148
+ const res = await client?.session?.get?.({ path: { id } });
149
+ if (!res?.data) return null;
150
+ if (!res.data.parentID) return id;
151
+ id = res.data.parentID;
152
+ parentID = undefined;
153
+ } catch { return null; }
154
+ }
155
+ return null;
156
+ }
137
157
 
138
158
  // A plugin reload (SIGUSR2) re-runs init on the cached module in the same process:
139
159
  // stop the previous instance's timers and keep its bus names alive. Keyed by directory.
@@ -336,9 +356,10 @@ export default async function bdbAosPlugin(input) {
336
356
  if (nudges >= MAX_NUDGES) return;
337
357
 
338
358
  nudgeState.set(sessionID, { signature: gate.signature, nudges: nudges + 1 });
359
+ // Synthetic + aos_loop: the nudge is plugin-written and must never count as a human turn (GO, gogate).
339
360
  await input.client.session.prompt({
340
361
  path: { id: sessionID },
341
- body: { parts: [{ type: 'text', text: buildGraphNudge(gate) }] },
362
+ body: { parts: [{ type: 'text', text: buildGraphNudge(gate), synthetic: true, metadata: { aos_loop: { source: 'graph-gate', nudge: nudges + 1 } } }] },
342
363
  query: directory ? { directory } : undefined,
343
364
  });
344
365
  } catch {
@@ -361,6 +382,12 @@ export default async function bdbAosPlugin(input) {
361
382
  if (!s.parentID) issueGoToken(fullText, { session_id: msgInput.sessionID, message_id: msgInput.messageID || msgOutput.message?.id });
362
383
  } catch {}
363
384
 
385
+ // plain `gogate ...` message from the human in a root session: record it; the gate verifies it against opencode.db at use.
386
+ try {
387
+ const reply = s.parentID || OPENCODE_RUN ? null : applyGogate(fullText, { key: ocKey(msgInput.sessionID), source: `opencode:${msgInput.sessionID}`, uuid: msgInput.messageID || msgOutput.message?.id || null, opencode: true });
388
+ if (reply) msgOutput.parts.unshift({ id: `gogate-${Date.now()}`, sessionID: msgInput.sessionID, messageID: msgInput.messageID || '', type: 'text', text: reply, synthetic: true });
389
+ } catch {}
390
+
364
391
  try {
365
392
  const event = s.identitySent || s.parentID ? 'UserPromptSubmit' : 'SessionStart';
366
393
  s.identitySent = true;
@@ -415,7 +442,9 @@ export default async function bdbAosPlugin(input) {
415
442
  // Intercept bash, terminal, or command execution tools
416
443
  if (toolName === 'bash' || toolName === 'terminal' || toolName === 'shell' || toolName === 'exec' || toolName === 'run_command') {
417
444
  const cmd = toolOutput?.args?.command || toolOutput?.args?.cmd || toolOutput?.args?.script || '';
418
- if (typeof cmd === 'string' && GUARDED_PATTERNS.some((re) => re.test(cmd))) {
445
+ const storeReason = gateStoreReason(cmd);
446
+ if (storeReason) throw new Error(storeReason);
447
+ if (typeof cmd === 'string' && (GUARDED_PATTERNS.some((re) => re.test(cmd)) || isGuardedCommand(cmd))) {
419
448
  const s = sess(toolInput.sessionID);
420
449
  // An unanswered lookup is not proof of a root session: token-only for this call, not cached.
421
450
  let tokenOnly = !!s.parentID;
@@ -427,8 +456,9 @@ export default async function bdbAosPlugin(input) {
427
456
  } catch { tokenOnly = true; }
428
457
  tokenOnly = tokenOnly || !!s.parentID;
429
458
  }
430
- // Subagent prompts are agent-written, so only a token can open their gate.
431
- let authorized = !tokenOnly && s.prompt.toUpperCase() === 'GO';
459
+ // Subagent prompts are agent-written, so only a token or a grant can open their gate.
460
+ const goOk = (text) => goAllows(parseGoText(text), cmd);
461
+ let authorized = !tokenOnly && goOk(s.prompt);
432
462
 
433
463
  // If this session's cached prompt wasn't GO, query recent session messages via OpenCode client as fallback
434
464
  if (!authorized && !tokenOnly && input.client && toolInput.sessionID) {
@@ -440,13 +470,27 @@ export default async function bdbAosPlugin(input) {
440
470
  if ((m.info?.role ?? m.role) === 'user') {
441
471
  const parts = m.parts || [];
442
472
  const text = parts.filter(isHumanText).map((p) => p.text).join('\n').trim();
443
- authorized = text.toUpperCase() === 'GO';
473
+ authorized = goOk(text);
444
474
  break;
445
475
  }
446
476
  }
447
477
  } catch {}
448
478
  }
449
479
 
480
+ // Modes and grants of the root session (verified against opencode.db); usable by its subagents.
481
+ if (!authorized) {
482
+ const root = await rootSessionId(input.client, toolInput.sessionID, tokenOnly ? undefined : s.parentID);
483
+ if (root) {
484
+ const key = ocKey(root);
485
+ const eff = effectiveGate(key, { source: `opencode:${root}`, cmds: opencodeGogateCommands(root), opencode: true });
486
+ for (const r of eff.rejected) gateLog(key, `rejected: ${r}`);
487
+ if (eff.mode === 'soft' && grantsCover(cmd, eff.grants)) { // `off` is never honoured on OpenCode
488
+ gateLog(key, `grant: allowed ${JSON.stringify(cmd.slice(0, 200))}`);
489
+ authorized = true;
490
+ }
491
+ }
492
+ }
493
+
450
494
  if (!authorized) {
451
495
  authorized = tokenGrantsGo(await ownSessionName(input.client, toolInput.sessionID)).ok;
452
496
  }
package/README.md CHANGED
@@ -110,9 +110,9 @@ What the installer writes for each target. Paths are the defaults; the installer
110
110
  | Harness | Skills | Subagents | Hooks | Plugin / rules |
111
111
  |---|---|---|---|---|
112
112
  | Claude Code / Claude Desktop | `~/.claude/skills` | `~/.claude/agents` | `~/.claude/hooks` + `settings.json` (GO gate, graph gate, env-file protection, Conventional Commits, memB inject, trail relay) | `.claude-plugin/` manifest ships in the repo (see Contributing) |
113
- | Google Antigravity | `~/.gemini/config/skills` | `~/.gemini/config/agents` | `~/.gemini/config/hooks.json` and `~/.gemini/antigravity-cli/hooks.json` | — |
114
- | Codex CLI | `~/.codex/skills` | `~/.codex/agents` | `~/.codex/hooks` + `config.toml` | `.codex-plugin/` |
115
- | OpenCode | `~/.config/opencode/skills` | `~/.opencode/agents` | via plugin | `bdb-aos.js` plugin + `/startcycle-graph` command, registered in `opencode.jsonc`; keeps a `/startcycle-graph` run moving on `session.idle` |
113
+ | Google Antigravity | `~/.gemini/config/skills` | `~/.gemini/config/agents` | `~/.gemini/config/hooks.json` and `~/.gemini/antigravity-cli/hooks.json` | root `plugin.json` / `plugins/bdb-aos/plugin.json`, commands `/bdb-aos:<cmd>`, see [docs/codex-agy-setup.md](docs/codex-agy-setup.md) |
114
+ | Codex CLI | `~/.codex/skills` | `~/.codex/agents` | `~/.codex/hooks` + `config.toml` | `.codex-plugin/` + `.agents/plugins/marketplace.json`, commands `$bdb-aos:<cmd>`, see [docs/codex-agy-setup.md](docs/codex-agy-setup.md) |
115
+ | OpenCode | `~/.config/opencode/skills` | `~/.opencode/agents` | via plugin | `bdb-aos.js` plugin + `/startcycle-graph` command, registered in `opencode.jsonc`; keeps a `/startcycle-graph` run moving on `session.idle`; generated `/bdb-aos-<cmd>` commands; opt-in extras, see [docs/opencode-setup.md](docs/opencode-setup.md) |
116
116
  | Cursor | `~/.cursor/skills` | — | — | `.cursor/rules` (project) |
117
117
  | Windsurf | `~/.windsurf/bdb-skills` | — | — | `mcp.json` |
118
118
  | Roo Code / Cline | `~/.roo/skills` | — | — | `.roomodes` (project) |
@@ -431,7 +431,7 @@ aos-uninstall --purge # also removes ~/.MemBDB, ~/.openwiki, ~/.synapse, ~/
431
431
  aos-uninstall --dry-run # list everything, delete nothing
432
432
  ```
433
433
 
434
- The uninstaller works from the install manifest: a file that still matches the hash AOS wrote is removed, a file you edited is backed up instead, a file AOS never wrote is not touched. The same action is in the installer menu.
434
+ The uninstaller works from the install manifest: a file that still matches the hash AOS wrote is removed, a file you edited is backed up instead, a file AOS never wrote is not touched. The same action is in the installer menu. `aos-uninstall --restore-plugin-backup` restores the loose skill copies the installer removed when it registered the plugin; see [docs/plugin-migration.md](docs/plugin-migration.md).
435
435
 
436
436
  ---
437
437
 
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Run a prompt repeatedly on an interval, using the harness built-in plus loop templates."
3
+ ---
4
+
5
+ Unverified, no loop mechanism confirmed for agy. Manual repeat: fill a `loop-templates` template, re-send it yourself at each interval, and stop at its limits or stop condition. Safety: a loop prompt never issues a GO, never types or runs `gogate`, never sets a mode or grant, and never merges, pushes or publishes unless a valid grant covers the action. Act on a guarded step only if the human's immediately preceding message is a literal GO or a valid grant covers it; a loop iteration never satisfies the first condition, so it must STOP and report instead of continuing a blocked guarded step. Templates (CI until green, follow a PR to merge, review rounds until no blockers, daily session summary) live in the `loop-templates` skill with run and duration limits and a stop condition each. Arguments: whatever the user wrote after the command name
@@ -12,6 +12,7 @@ import { createRequire } from 'node:module';
12
12
  import os from 'node:os';
13
13
  import path from 'node:path';
14
14
 
15
+ const pm = createRequire(import.meta.url)('../lib/plugin-migration.js');
15
16
  const HOME = os.homedir();
16
17
  const JSON_OUT = process.argv.includes('--json');
17
18
  const NET = process.argv.includes('--net');
@@ -183,10 +184,11 @@ function checkHarnesses() {
183
184
  const exists = existsSync(hr.path);
184
185
  const count = dirCount(hr.path);
185
186
  const hasSentinel = existsSync(path.join(hr.path, SENTINEL, 'SKILL.md'));
186
- const ok = exists && count > 0 && hasSentinel;
187
+ const viaPlugin = hr.name === 'Claude Code' && !hasSentinel && pm.pluginInstalled(HOME, [SENTINEL]);
188
+ const ok = viaPlugin || (exists && count > 0 && hasSentinel);
187
189
  add('harnesses', `${hr.name} Skills`, ok,
188
- ok ? `${tilde(hr.path)} (${count} skills, sentinel verified)` : (exists ? `${tilde(hr.path)} (${count} skills, sentinel missing)` : `${tilde(hr.path)} not synced`),
189
- `Run 'npx @hybridlabor-api/aos@latest' and select ${hr.name} to sync skills.`, !exists);
190
+ viaPlugin ? 'delivered by the bdb-aos Claude Code plugin (installed_plugins.json / plugin cache)' : ok ? `${tilde(hr.path)} (${count} skills, sentinel verified)` : (exists ? `${tilde(hr.path)} (${count} skills, sentinel missing)` : `${tilde(hr.path)} not synced`),
191
+ `Run 'npx @hybridlabor-api/aos@latest' and select ${hr.name} to sync skills.`, !exists && !viaPlugin);
190
192
  }
191
193
 
192
194
  // OpenCode Plugin check
@@ -5,6 +5,9 @@
5
5
  // aos-uninstall --purge also the memory store, wikis and credentials
6
6
  // aos-uninstall --dry-run list everything, delete nothing
7
7
  // aos-uninstall --yes skip the confirmations (CI only)
8
+ // aos-uninstall --restore-plugin-backup
9
+ // put back the loose skill copies the installer removed when it
10
+ // registered the bdb-aos plugin (never overwrites an existing file)
8
11
  //
9
12
  // The file-level install manifest records every path AOS wrote together with
10
13
  // the sha256 it wrote. That is what makes a precise uninstall possible: a file
@@ -19,6 +22,9 @@ import { createHash } from 'node:crypto';
19
22
  import { createInterface } from 'node:readline/promises';
20
23
  import os from 'node:os';
21
24
  import path from 'node:path';
25
+ import { createRequire } from 'node:module';
26
+
27
+ const pm = createRequire(import.meta.url)('../lib/plugin-migration.js');
22
28
 
23
29
  const HOME = os.homedir();
24
30
  const h = (...p) => path.join(HOME, ...p);
@@ -27,6 +33,7 @@ const tilde = (p) => p.replace(HOME, '~');
27
33
  const PURGE = process.argv.includes('--purge');
28
34
  const DRY = process.argv.includes('--dry-run');
29
35
  const YES = process.argv.includes('--yes');
36
+ const RESTORE = process.argv.includes('--restore-plugin-backup');
30
37
 
31
38
  const MANIFEST = h('.agents', '.bdb-install-manifest.json');
32
39
 
@@ -238,13 +245,19 @@ function execute(p) {
238
245
  for (const l of legacy) { try { rmSync(l); } catch { /* already gone */ } }
239
246
  if (legacy.length) console.log(` ${legacy.length} Installations-Marker entfernt`);
240
247
 
248
+ const reg = pm.readState(HOME).registered.claudecode;
249
+ if (reg) {
250
+ const r = pm.deregisterClaude({ home: HOME, record: reg });
251
+ if (r.changed) console.log(` bdb-aos Plugin-Registrierung aus settings.json entfernt${reg.replaced ? ` (externer Marketplace ${reg.replaced.key} wiederhergestellt)` : ''}`);
252
+ }
253
+
241
254
  // Only the BDB hook entries leave settings.json; everything else in it is
242
255
  // the user's and must survive an uninstall exactly as it survives an install.
243
256
  const settings = h('.claude', 'settings.json');
244
257
  if (existsSync(settings)) {
245
258
  try {
246
259
  const s = JSON.parse(readFileSync(settings, 'utf8'));
247
- const bdb = ['go-gate.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'trail-autostart.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
260
+ const bdb = ['go-gate.mjs', 'go-token.mjs', 'go-grant.mjs', 'graph-gate.mjs', 'memb-inject.mjs', 'trail-relay.mjs', 'trail-autostart.mjs', 'conventional-commits.mjs', 'env-file-protection.mjs'];
248
261
  let touched = false;
249
262
  for (const [event, entries] of Object.entries(s.hooks || {})) {
250
263
  const kept = entries.filter((e) => !(e.hooks || []).some((x) => bdb.some((n) => String(x.command).includes(n))));
@@ -266,6 +279,7 @@ function execute(p) {
266
279
  for (const f of [MANIFEST, h('.agents', '.bdb-manifest.json')]) {
267
280
  try { rmSync(f); } catch { /* already gone */ }
268
281
  }
282
+ pm.retireState(HOME);
269
283
  }
270
284
 
271
285
  // ---------------------------------------------------------------------- main
@@ -276,6 +290,16 @@ const ask = async (q) => {
276
290
  return a;
277
291
  };
278
292
 
293
+ if (RESTORE) {
294
+ const r = pm.restorePluginBackups({ home: HOME });
295
+ if (!r.dirs.length) { console.log('Keine Plugin-Migrations-Sicherung gefunden.'); process.exit(1); }
296
+ console.log(` ${r.files.length} Dateien aus ${r.dirs.length} Sicherung(en) wiederhergestellt, im Install-Manifest erfasst`);
297
+ console.log('Vorhandene Dateien wurden nicht überschrieben.');
298
+ if (r.deregistered) console.log(' bdb-aos Plugin-Registrierung entfernt, damit die Skills nicht doppelt geladen werden. Setze AOS_PLUGIN_MIGRATION=off, damit der Installer sie nicht erneut entfernt.');
299
+ else if (r.files.length && r.stillEnabled) console.log(' WARNUNG: das bdb-aos Plugin ist weiterhin aktiv, die Skills erscheinen doppelt. Plugin in Claude Code deaktivieren und AOS_PLUGIN_MIGRATION=off setzen.');
300
+ process.exit(0);
301
+ }
302
+
279
303
  const p = plan();
280
304
  console.log(PURGE ? '\x1b[31mAOS UNINSTALL — PURGE\x1b[0m' : 'AOS Uninstall');
281
305
  if (!describe(p)) process.exit(1);
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Brainstorm a topic with the matching skill: bdbrainstorm, bdbmediastorm for event tech, grill-me for a stress test."
3
+ ---
4
+
5
+ Pick by topic: `bdbmediastorm` for live event technology and show control, `grill-me` when the human wants an existing idea stress-tested, `bdbrainstorm` otherwise. Say which one you chose. Topic: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Check the machine and the current project, and print suggested permissions.allow entries without writing any settings."
3
+ ---
4
+
5
+ Run `aos-doctor` (machine check) and report the result. If the current folder is an AOS project, also run the project check `node <aos-project-init skill dir>/scripts/aos-project-doctor.mjs` (it covers `.aos/project.json`, AGENTS.md and the wiki). Print suggested `permissions.allow` entries as text for the human to copy. Never write to settings.json or settings.local.json. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Run the startcycle-graph pipeline: durable state, repair loop, quality gate, escalation."
3
+ ---
4
+
5
+ Run the `startcycle-graph` skill; its contract defines the flow. Task: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Set up the current project folder for AOS: slug, wiki, memory, Synapse map, AGENTS.md."
3
+ ---
4
+
5
+ Run the `aos-project-init` skill for the current project folder. This is project setup, not machine setup (that is `/bdb-aos:setup`). Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Run a prompt repeatedly on an interval, using the harness built-in plus loop templates."
3
+ ---
4
+
5
+ Use the built-in `/loop <interval> <prompt>` (omit the interval to self-pace). Offer a `loop-templates` template when the human has no prompt yet. Safety: a loop prompt never issues a GO, never types or runs `gogate`, never sets a mode or grant, and never merges, pushes or publishes unless a valid grant covers the action. Act on a guarded step only if the human's immediately preceding message is a literal GO or a valid grant covers it; a loop iteration never satisfies the first condition, so it must STOP and report instead of continuing a blocked guarded step. Templates (CI until green, follow a PR to merge, review rounds until no blockers, daily session summary) live in the `loop-templates` skill with run and duration limits and a stop condition each. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Supervise other Claude Code sessions on this machine: roster, status requests, GO board, idle notices."
3
+ ---
4
+
5
+ Act as the master session with the `master-session` skill (ACP bridge: `bin/aos-acp.mjs`, see docs/master-session-acp.md). Keep the overview, relay the human's decisions, never decide for the human. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Query or store long-term memory with memB."
3
+ ---
4
+
5
+ Use `memb-skill` to search or remember and `memb-ingest` to import material. Never store secrets, tokens or credentials. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Coordinate parallel coding agents across repositories through the AO daemon."
3
+ ---
4
+
5
+ Use the `ao-orchestrator` skill. It needs the AO daemon; check that it is running first and stop with a clear message if not. Do not confuse it with `agent-orchestrator`. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Plan end to end: draft, render in plan-canvas or Plan Builder, annotate, await approval, then hand off."
3
+ ---
4
+
5
+ Run the full planning pipeline: 1) draft the plan with `concise-planning`; 2) always offer the choice between plan-canvas and Plan Builder (`visual-plan`) for rendering; 3) collect annotations and wait for the human's approval; 4) only after approval continue with `writing-plans` or `/bdb-aos:graph`. Topic: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "List all pb-* playbooks with duration, difficulty and requirements, and start one."
3
+ ---
4
+
5
+ Run `node <bdb-aos skill dir>/scripts/list-playbooks.mjs` and show its output (name, est_time, difficulty, requires of every `pb-*` skill). If the human names a playbook in the arguments or picks one, start that `pb-*` skill; start nothing otherwise. Each playbook keeps its own go-gate points. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Bring this machine to a complete, verified AOS installation (skills, MCP servers, gate hooks, memB, OpenWiki, Synapse)."
3
+ ---
4
+
5
+ Run the `aos-setup` skill for this machine. Report what is installed, what was repaired and what is still missing. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Pre-ship framing and the technical release gate, with approvals under the go-gate rules."
3
+ ---
4
+
5
+ Use `bdb-shipping-skill` for the pre-flight and `godmode-shipping` for the gate. The playbook `pb-ship` can drive the whole flow. Push, merge and publish need the human's GO under the go-gate rules; never request one on the human's behalf. Arguments: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Start a new composition: project init, brainstorm, then ask whether graph or startcycle follows."
3
+ ---
4
+
5
+ Sequence: 1) `aos-project-init`; 2) `bdbrainstorm`; 3) stop and ask the human whether `/bdb-aos:graph` (startcycle-graph) or `startcycle` follows. Do not start a pipeline on your own. Idea: $ARGUMENTS
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Browse and preview AOS skills, agents and playbooks in the local store UI."
3
+ ---
4
+
5
+ Start the `aos-store` interface. Preview freely, but install nothing until the human says yes for that specific item. Arguments: $ARGUMENTS
@@ -0,0 +1,16 @@
1
+ # Codex and Antigravity (agy) plugin setup
2
+
3
+ Both manifests are generated from `plugin-commands.json` by `node scripts/build-plugin-manifest.mjs` and checked by `--check` (stale or missing files, version drift against `package.json`, referenced files, no `disable-model-invocation` in any Codex output).
4
+
5
+ ## Codex
6
+
7
+ - **Load:** `codex plugin marketplace add <path-to-this-repo>` (it reads `.agents/plugins/marketplace.json`), then `codex plugin add bdb-aos@bdb-aos`.
8
+ - **Invoke:** `$bdb-aos:<cmd>`, for example `$bdb-aos:setup`. Codex has no `/` command for plugin skills.
9
+ - **How it is built:** each command becomes a wrapper skill in `plugins/bdb-aos-codex/skills/<cmd>/SKILL.md`. The plugin ships only these wrappers; the 200+ AOS skills reach Codex through the installer's `~/.codex/skills` copies, and a wrapper tells the model which one to run.
10
+ - **Bodies:** `bodies.codex` is optional; without it the Claude body is used with `/bdb-aos:<cmd>` rewritten to `$bdb-aos:<cmd>`.
11
+
12
+ ## Antigravity (agy)
13
+
14
+ - **Load:** `agy plugin install` against `plugins/bdb-aos` (its `plugin.json` sits at the plugin root), or `agy plugin validate plugins/bdb-aos` to check it.
15
+ - **Invoke:** `/bdb-aos:<cmd>`, for example `/bdb-aos:setup`.
16
+ - **Bodies:** commands reuse `commands/<cmd>.md`. A command with `bodies.agy` is written to `agy-commands/<cmd>.md` and wired instead.