@hybridlabor-api/aos 4.16.0 → 4.18.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 (147) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude/hooks/aos-bus.mjs +8 -4
  3. package/.claude/hooks/env-file-protection.mjs +19 -1
  4. package/.claude/hooks/go-gate.mjs +788 -69
  5. package/.claude/hooks/go-grant.mjs +88 -0
  6. package/.claude/hooks/go-token.mjs +17 -2
  7. package/.claude/hooks/memb-inject.mjs +61 -37
  8. package/.claude/settings.json +8 -0
  9. package/.codex-plugin/plugin.json +36 -6
  10. package/.opencode/commands/bdb-aos-brainstorm.md +5 -0
  11. package/.opencode/commands/bdb-aos-doctor.md +5 -0
  12. package/.opencode/commands/bdb-aos-graph.md +5 -0
  13. package/.opencode/commands/bdb-aos-init.md +5 -0
  14. package/.opencode/commands/bdb-aos-loop.md +5 -0
  15. package/.opencode/commands/bdb-aos-mastersession.md +5 -0
  16. package/.opencode/commands/bdb-aos-memb.md +5 -0
  17. package/.opencode/commands/bdb-aos-orchestrator.md +5 -0
  18. package/.opencode/commands/bdb-aos-plan.md +5 -0
  19. package/.opencode/commands/bdb-aos-playbooks.md +5 -0
  20. package/.opencode/commands/bdb-aos-setup.md +5 -0
  21. package/.opencode/commands/bdb-aos-shipping.md +5 -0
  22. package/.opencode/commands/bdb-aos-startproject.md +5 -0
  23. package/.opencode/commands/bdb-aos-store.md +5 -0
  24. package/.opencode/plugins/bdb-aos.js +54 -10
  25. package/README.md +5 -4
  26. package/THIRD_PARTY_NOTICES.md +2 -2
  27. package/agy-commands/loop.md +5 -0
  28. package/bin/aos-acp.mjs +27 -1
  29. package/bin/aos-doctor.mjs +41 -4
  30. package/bin/aos-uninstall.mjs +41 -3
  31. package/bin/go-check.mjs +79 -0
  32. package/bin/guarded-patterns.json +106 -0
  33. package/commands/brainstorm.md +5 -0
  34. package/commands/doctor.md +5 -0
  35. package/commands/graph.md +5 -0
  36. package/commands/init.md +5 -0
  37. package/commands/loop.md +5 -0
  38. package/commands/mastersession.md +5 -0
  39. package/commands/memb.md +5 -0
  40. package/commands/orchestrator.md +5 -0
  41. package/commands/plan.md +5 -0
  42. package/commands/playbooks.md +5 -0
  43. package/commands/setup.md +5 -0
  44. package/commands/shipping.md +5 -0
  45. package/commands/startproject.md +5 -0
  46. package/commands/store.md +5 -0
  47. package/docs/codenotch.md +44 -0
  48. package/docs/codex-agy-setup.md +16 -0
  49. package/docs/codex-gate-smoke.md +43 -0
  50. package/docs/delegation-routing.md +32 -0
  51. package/docs/go-check.md +60 -0
  52. package/docs/master-session-acp.md +2 -0
  53. package/docs/opencode-setup.md +54 -0
  54. package/docs/plugin-migration.md +61 -0
  55. package/installer.js +351 -121
  56. package/lib/codenotch.js +389 -0
  57. package/lib/plugin-migration.js +462 -0
  58. package/lib/retired-skills.js +101 -0
  59. package/lib/store-ui/index.html +9 -1
  60. package/lib/store-ui/server.mjs +2 -0
  61. package/mcps/mcsc/README.md +1 -1
  62. package/mcps/mcsc/packages/core/src/adapters/agy.js +3 -1
  63. package/mcps/mcsc/packages/core/src/adapters/codex.js +2 -1
  64. package/mcps/mcsc/packages/core/src/adapters/opencode.js +2 -1
  65. package/mcps/mcsc/packages/core/src/depth.js +16 -0
  66. package/mcps/mcsc/packages/mcp/server.js +15 -2
  67. package/package.json +7 -2
  68. package/plugin-commands.json +143 -0
  69. package/plugin.json +299 -0
  70. package/plugins/bdb-aos-codex/.codex-plugin/plugin.json +38 -0
  71. package/plugins/bdb-aos-codex/skills/brainstorm/SKILL.md +6 -0
  72. package/plugins/bdb-aos-codex/skills/doctor/SKILL.md +6 -0
  73. package/plugins/bdb-aos-codex/skills/graph/SKILL.md +6 -0
  74. package/plugins/bdb-aos-codex/skills/init/SKILL.md +6 -0
  75. package/plugins/bdb-aos-codex/skills/loop/SKILL.md +6 -0
  76. package/plugins/bdb-aos-codex/skills/mastersession/SKILL.md +6 -0
  77. package/plugins/bdb-aos-codex/skills/memb/SKILL.md +6 -0
  78. package/plugins/bdb-aos-codex/skills/orchestrator/SKILL.md +6 -0
  79. package/plugins/bdb-aos-codex/skills/plan/SKILL.md +6 -0
  80. package/plugins/bdb-aos-codex/skills/playbooks/SKILL.md +6 -0
  81. package/plugins/bdb-aos-codex/skills/setup/SKILL.md +6 -0
  82. package/plugins/bdb-aos-codex/skills/shipping/SKILL.md +6 -0
  83. package/plugins/bdb-aos-codex/skills/startproject/SKILL.md +6 -0
  84. package/plugins/bdb-aos-codex/skills/store/SKILL.md +6 -0
  85. package/scripts/build-plugin-manifest.mjs +202 -3
  86. package/scripts/codex-gate-smoke.mjs +73 -0
  87. package/skills/basic/master-session/SKILL.md +11 -0
  88. package/skills/bdb-aos/scripts/list-playbooks.mjs +72 -0
  89. package/skills/global_config/agenttrail/SKILL.md +3 -1
  90. package/skills/global_config/agenttrail/bin/agenttrail.mjs +255 -117
  91. package/skills/global_config/agenttrail/bin/ensure.mjs +60 -40
  92. package/skills/global_config/agenttrail/bin/repoid.mjs +70 -0
  93. package/skills/global_config/agenttrail/public/index.html +9 -1
  94. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
  95. package/skills/global_config/bdb-memb-mcp/SKILL.md +5 -4
  96. package/skills/global_config/bdb-visual-edit/SKILL.md +28 -32
  97. package/skills/global_config/bdb-visual-edit/references/vite-react-source-attr.md +2 -2
  98. package/skills/global_config/bdb-visual-edit/scripts/locate-source.mjs +135 -0
  99. package/skills/global_config/bdb-visual-edit/scripts/sanitize-element.mjs +30 -2
  100. package/skills/global_config/gogate/SKILL.md +123 -0
  101. package/skills/global_config/loop-templates/SKILL.md +27 -0
  102. package/skills/global_config/loop-templates/references/ci-until-green.md +27 -0
  103. package/skills/global_config/loop-templates/references/daily-summary.md +27 -0
  104. package/skills/global_config/loop-templates/references/pr-to-merge.md +27 -0
  105. package/skills/global_config/loop-templates/references/review-rounds.md +27 -0
  106. package/skills/global_config/mcsc/SKILL.md +9 -1
  107. package/skills/global_config/plan-arbiter/SKILL.md +1 -1
  108. package/skills/global_config/plan-canvas/SKILL.md +42 -4
  109. package/skills/global_config/plan-canvas/scripts/lib/plan-builder/README.md +1 -1
  110. package/skills/global_config/plan-canvas/scripts/lib/plan-builder/render.js +2 -2
  111. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/geometry.js +76 -0
  112. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/index.js +596 -0
  113. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/model.js +192 -0
  114. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-client/toolbar.js +99 -0
  115. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotate-server.js +282 -0
  116. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/annotation-schema.js +210 -0
  117. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/route.js +10 -0
  118. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sdk.js +6 -230
  119. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +45 -4
  120. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sessions.js +60 -21
  121. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/trail-on-approve.js +103 -0
  122. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +19 -7
  123. package/skills/global_config/plan-canvas/scripts/plan-canvas.js +118 -20
  124. package/skills/global_config/subagent-setup/SKILL.md +6 -0
  125. package/skills/global_config/subagent-setup/scripts/setup-subagents.mjs +20 -1
  126. package/skills/playbooks/pb-idea-to-launch/SKILL.md +2 -2
  127. package/skills/playbooks/pb-redesign-app/SKILL.md +3 -3
  128. package/skills/playbooks/pb-release-aos/SKILL.md +2 -2
  129. package/skills/playbooks/pb-ship/SKILL.md +2 -2
  130. package/skills/playbooks/pb-worktrees-land/SKILL.md +2 -2
  131. package/.codex-plugin/marketplace.json +0 -11
  132. package/skills/global_config/bdb-visual-edit/scripts/pick-snippet.js +0 -27
  133. package/skills/global_config/visual-edit/README.md +0 -96
  134. package/skills/global_config/visual-edit/SKILL.md +0 -615
  135. package/skills/global_config/visual-plan/README.md +0 -93
  136. package/skills/global_config/visual-plan/SKILL.md +0 -544
  137. package/skills/global_config/visual-plan/references/canvas.md +0 -139
  138. package/skills/global_config/visual-plan/references/connection.md +0 -51
  139. package/skills/global_config/visual-plan/references/document-quality.md +0 -186
  140. package/skills/global_config/visual-plan/references/exemplar.md +0 -62
  141. package/skills/global_config/visual-plan/references/local-files.md +0 -99
  142. package/skills/global_config/visual-plan/references/wireframe.md +0 -319
  143. package/skills/global_config/visual-recap/README.md +0 -103
  144. package/skills/global_config/visual-recap/SKILL.md +0 -560
  145. package/skills/global_config/visual-recap/references/connection.md +0 -51
  146. package/skills/global_config/visual-recap/references/local-files.md +0 -99
  147. package/skills/global_config/visual-recap/references/wireframe.md +0 -319
@@ -309,7 +309,7 @@ dependency.
309
309
 
310
310
  <https://github.com/BuilderIO/skills> — MIT. Copyright (c) 2026 Builder.io.
311
311
 
312
- Eleven skills are adapted under `skills/global_config/`: `read-the-damn-docs`, `stay-within-limits`, `quick-recap`, `plan-arbiter`, `factory-human-digest`, `factory-collect`, `factory-lookback`, `factory-review-prs`, `visual-plan`, `visual-recap`, and `visual-edit`. The four Factory skills and `plan-arbiter` have `installer-group` dropped and `category`/`source` added; in `factory-collect`, `factory-lookback` and `factory-review-prs` reply, close, approve, merge and publish actions are reduced to drafts or readiness reports that need the user's GO, and all of them carry an "AOS safety rules" section. `plan-arbiter` drops `agents/openai.yaml` and adds an AOS section on double plans. `stay-within-limits` and `quick-recap` are carried over with `category:` and `source:` added; `stay-within-limits` pins `ccusage` to 20.0.26 instead of `@latest`, and `quick-recap` no longer asks the installer to inject a managed `AGENTS.md` / `CLAUDE.md` block. The three visual skills (`visual-plan`, `visual-recap`, `visual-edit`) are optional and drive the Agent-Native Plan/Design services through MCP connectors (`plan` and `design`) that AOS does NOT register; you connect them yourself if needed. Each carries an "AOS notes" section. `read-the-damn-docs` is described below:
312
+ Eight skills are adapted under `skills/global_config/`: `read-the-damn-docs`, `stay-within-limits`, `quick-recap`, `plan-arbiter`, `factory-human-digest`, `factory-collect`, `factory-lookback`, and `factory-review-prs`. The four Factory skills and `plan-arbiter` have `installer-group` dropped and `category`/`source` added; in `factory-collect`, `factory-lookback` and `factory-review-prs` reply, close, approve, merge and publish actions are reduced to drafts or readiness reports that need the user's GO, and all of them carry an "AOS safety rules" section. `plan-arbiter` drops `agents/openai.yaml` and adds an AOS section on double plans. `stay-within-limits` and `quick-recap` are carried over with `category:` and `source:` added; `stay-within-limits` pins `ccusage` to 20.0.26 instead of `@latest`, and `quick-recap` no longer asks the installer to inject a managed `AGENTS.md` / `CLAUDE.md` block. `read-the-damn-docs` is described below:
313
313
  the docs-first trigger list, the source hierarchy, the required workflow, the
314
314
  must-trigger examples, and the "if docs are unavailable" rule. AOS additionally
315
315
  adds a Verification section, a `category:` key (Builder's own validator forbids
@@ -318,7 +318,7 @@ guesswork* rule, and a pointer to the installed `firecrawl-search` /
318
318
  `firecrawl-scrape` tools. Prose only — the upstream `agents/openai.yaml` and
319
319
  README were not carried over, and no executable from the repo is referenced.
320
320
 
321
- In the three visual skills, five path-like mentions (`/sign-in`, `/live-edit-bridge`, `/read-file`, `/write-file`, `/visualize-repo`) were reworded so the AOS validator does not read them as skill references. The other 13 skills in that repo (as of commit 0dba9ef) are **not** vendored. Three of them
321
+ The other 13 skills in that repo (as of commit 0dba9ef) are **not** vendored. Three of them
322
322
  (`an`, `turn-into-app`, `rewind`) plus three others
323
323
  are build artefacts synced from a different upstream by
324
324
  `scripts/sync-agent-native-skills.mjs`, and the repo's own documentation says
@@ -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
package/bin/aos-acp.mjs CHANGED
@@ -14,10 +14,11 @@
14
14
  import { spawn } from "node:child_process";
15
15
  import { appendFileSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
16
16
  import { homedir } from "node:os";
17
- import { dirname, join } from "node:path";
17
+ import { dirname, isAbsolute, join, resolve } from "node:path";
18
18
  import { fileURLToPath } from "node:url";
19
19
  import { createInterface } from "node:readline";
20
20
  import { GUARDED_PATTERNS, slug, tokenGrantsGo } from "../.claude/hooks/go-gate.mjs";
21
+ import { emitTrail } from "../mcps/mcsc/packages/core/src/trail.js";
21
22
 
22
23
  export const ADAPTERS = {
23
24
  claude: { cmd: ["npx", "-y", "@agentclientprotocol/claude-agent-acp"], consume: false },
@@ -113,6 +114,25 @@ export class AcpClient {
113
114
  notify(method, params) { this.write({ jsonrpc: "2.0", method, params }); }
114
115
  }
115
116
 
117
+ // Same agenttrail protocol as mcsc's adapters (SessionStart/SessionEnd, PreToolUse/PostToolUse),
118
+ // so the GO board reads ACP workers and mcsc runs from one source.
119
+ const cap = (t) => (t ? t.charAt(0).toUpperCase() + t.slice(1) : "Tool");
120
+
121
+ export function trailToolEvent(u, seen, cwd) {
122
+ const prev = seen.get(u.toolCallId) || {};
123
+ const info = { title: u.title ?? prev.title, kind: u.kind ?? prev.kind };
124
+ seen.set(u.toolCallId, info);
125
+ const done = u.status === "completed" || u.status === "failed";
126
+ if (u.sessionUpdate === "tool_call_update" && !done) return null;
127
+ const tool_input = {};
128
+ const file = u.locations?.[0]?.path ?? prev.file;
129
+ if (typeof file === "string" && file) tool_input.file_path = isAbsolute(file) ? file : resolve(cwd, file);
130
+ info.file = tool_input.file_path;
131
+ const command = commandOf(u);
132
+ if (command) tool_input.command = command;
133
+ return { hook_event_name: done ? "PostToolUse" : "PreToolUse", tool_name: cap(info.kind || info.title), tool_input };
134
+ }
135
+
116
136
  function textOf(block) {
117
137
  return block && block.type === "text" ? block.text : "";
118
138
  }
@@ -134,6 +154,9 @@ export async function run(opts) {
134
154
  const consume = opts.consume ?? adapter?.consume ?? true;
135
155
  log("start", { adapter: opts.adapter, cmd: cmd.join(" "), cwd: opts.cwd, allow_default: opts.allowDefault, consume, model: opts.model || "adapter default" });
136
156
 
157
+ const trail = { session_id: `aos-acp-${process.pid}-${Date.now()}`, cwd: opts.cwd, agent: `${opts.adapter}:${opts.name}` };
158
+ const seenTools = new Map();
159
+ emitTrail({ ...trail, hook_event_name: "SessionStart" });
137
160
  let sessionId;
138
161
  const out = opts.stdout || process.stdout;
139
162
  const client = new AcpClient(cmd, {
@@ -148,6 +171,8 @@ export async function run(opts) {
148
171
  if (t) { out.write(t); log("text", { text: t }); }
149
172
  } else if (u.sessionUpdate === "tool_call" || u.sessionUpdate === "tool_call_update") {
150
173
  log(u.sessionUpdate, { id: u.toolCallId, title: u.title, kind: u.kind, status: u.status, command: commandOf(u) || undefined });
174
+ const ev = trailToolEvent(u, seenTools, opts.cwd);
175
+ if (ev) emitTrail({ ...trail, ...ev });
151
176
  } else log("update", { kind: u.sessionUpdate });
152
177
  },
153
178
  onRequest: (method, p) => {
@@ -182,6 +207,7 @@ export async function run(opts) {
182
207
  } finally {
183
208
  if (timer) clearTimeout(timer);
184
209
  client.child.kill();
210
+ await emitTrail({ ...trail, hook_event_name: "SessionEnd" });
185
211
  }
186
212
  }
187
213
 
@@ -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');
@@ -168,6 +169,40 @@ function checkAosCore() {
168
169
  }
169
170
  }
170
171
 
172
+ // JSONC to JSON: drops comments (not inside strings) and trailing commas.
173
+ function parseJsonc(text) {
174
+ let out = '';
175
+ for (let i = 0, str = false; i < text.length; i++) {
176
+ const c = text[i];
177
+ if (str) { out += c; if (c === '\\') out += text[++i] ?? ''; else if (c === '"') str = false; continue; }
178
+ if (c === '"') { str = true; out += c; continue; }
179
+ if (c === '/' && text[i + 1] === '/') { while (i < text.length && text[i] !== '\n') i++; out += '\n'; continue; }
180
+ if (c === '/' && text[i + 1] === '*') { i += 2; while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) i++; i++; continue; }
181
+ out += c;
182
+ }
183
+ try { return JSON.parse(out.replace(/,(\s*[}\]])/g, '$1')); } catch { return null; }
184
+ }
185
+
186
+ // Zen gateway limit: a tool name (OpenCode builds it from the MCP server name) is at most 64 characters.
187
+ // Names only, no network: the tool part of the name is not known here.
188
+ const ZEN_TOOL_NAME_MAX = 64;
189
+
190
+ function checkOpencodeMcpNames() {
191
+ const dirs = [process.platform === 'win32' ? path.join(process.env.APPDATA || HOME, 'opencode') : h('.config', 'opencode')];
192
+ const file = firstExisting(dirs.flatMap((d) => ['opencode.jsonc', 'opencode.json'].map((f) => path.join(d, f))));
193
+ if (!file) return;
194
+ const conf = parseJsonc(readFileSync(file, 'utf8'));
195
+ if (!conf) {
196
+ add('harnesses', 'OpenCode MCP names', false, `${tilde(file)} is not valid JSON(C); names not checked`, 'Fix the OpenCode config syntax.', true);
197
+ return;
198
+ }
199
+ const names = Object.keys(conf.mcp && typeof conf.mcp === 'object' ? conf.mcp : {});
200
+ const tooLong = names.filter((n) => n.length > ZEN_TOOL_NAME_MAX);
201
+ add('harnesses', 'OpenCode MCP names', tooLong.length === 0,
202
+ tooLong.length ? `${tooLong.length} MCP name(s) over ${ZEN_TOOL_NAME_MAX} characters (Zen gateway limit): ${tooLong.join(', ')}` : `${names.length} MCP name(s) in ${tilde(file)}, none over ${ZEN_TOOL_NAME_MAX} characters`,
203
+ `Rename the MCP server(s) in ${tilde(file)} to a shorter name; tool names are built from them and a name over ${ZEN_TOOL_NAME_MAX} characters is rejected by the Zen gateway. Keeping the MCP set lean also helps.`, true);
204
+ }
205
+
171
206
  // ---------------------------------------------------------------- 3. Harness Placement & Skills Sync
172
207
  function checkHarnesses() {
173
208
  const harnesses = [
@@ -183,10 +218,11 @@ function checkHarnesses() {
183
218
  const exists = existsSync(hr.path);
184
219
  const count = dirCount(hr.path);
185
220
  const hasSentinel = existsSync(path.join(hr.path, SENTINEL, 'SKILL.md'));
186
- const ok = exists && count > 0 && hasSentinel;
221
+ const viaPlugin = hr.name === 'Claude Code' && !hasSentinel && pm.pluginInstalled(HOME, [SENTINEL]);
222
+ const ok = viaPlugin || (exists && count > 0 && hasSentinel);
187
223
  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);
224
+ 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`),
225
+ `Run 'npx @hybridlabor-api/aos@latest' and select ${hr.name} to sync skills.`, !exists && !viaPlugin);
190
226
  }
191
227
 
192
228
  // OpenCode Plugin check
@@ -197,12 +233,13 @@ function checkHarnesses() {
197
233
  add('harnesses', 'OpenCode Plugin', !!opencodePlugin,
198
234
  opencodePlugin ? tilde(opencodePlugin) : 'bdb-aos.js not installed in OpenCode plugins',
199
235
  'Run the AOS installer to wire OpenCode telemetry plugin.', true);
236
+ checkOpencodeMcpNames();
200
237
  }
201
238
 
202
239
  // ---------------------------------------------------------------- 4. Hooks & Security Gates
203
240
  function checkHooks() {
204
241
  const claudeHooksDir = h('.claude', 'hooks');
205
- const EXPECTED_VERSION = { 'memb-inject.mjs': 7 };
242
+ const EXPECTED_VERSION = { 'memb-inject.mjs': 8 };
206
243
  const versionOf = (text) => {
207
244
  const m = /^\/\/\s*aos-hook-version:\s*(\d+)/m.exec(text);
208
245
  return m ? Number(m[1]) : null;
@@ -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,10 @@ 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');
28
+ const cn = createRequire(import.meta.url)('../lib/codenotch.js');
22
29
 
23
30
  const HOME = os.homedir();
24
31
  const h = (...p) => path.join(HOME, ...p);
@@ -27,6 +34,7 @@ const tilde = (p) => p.replace(HOME, '~');
27
34
  const PURGE = process.argv.includes('--purge');
28
35
  const DRY = process.argv.includes('--dry-run');
29
36
  const YES = process.argv.includes('--yes');
37
+ const RESTORE = process.argv.includes('--restore-plugin-backup');
30
38
 
31
39
  const MANIFEST = h('.agents', '.bdb-install-manifest.json');
32
40
 
@@ -96,7 +104,9 @@ function plan() {
96
104
  const legacy = LEGACY_MARKERS.filter(existsSync);
97
105
  const data = PURGE ? DATA_PATHS.filter((d) => existsSync(d.path)) : [];
98
106
 
99
- return { manifest, ours, edited, gone, agents, modules, legacy, data };
107
+ const codenotch = cn.planCodenotchUninstall({ stateFile: cn.stateFilePath(HOME) });
108
+
109
+ return { manifest, ours, edited, gone, agents, modules, legacy, data, codenotch };
100
110
  }
101
111
 
102
112
  function describe(p) {
@@ -133,6 +143,12 @@ function describe(p) {
133
143
  for (const l of legacy) console.log(` ${tilde(l)}`);
134
144
  }
135
145
 
146
+ if (p.codenotch) {
147
+ const c = p.codenotch;
148
+ const note = { remove: 'is removed (still the recorded build)', keep: 'is kept (replaced or modified since AOS installed it)', gone: 'is already gone' }[c.action];
149
+ console.log(`\nBDB AO Codenotch ${tilde(c.state.path || c.state.uninstallPath)} ${note}`);
150
+ }
151
+
136
152
  console.log('\nBleibt erhalten:');
137
153
  if (!PURGE) for (const d of DATA_PATHS.filter((d) => existsSync(d.path))) console.log(` ${tilde(d.path).padEnd(18)} ${d.what}`);
138
154
  console.log(' jede Datei ohne Manifest-Eintrag — eigene Skills, fremde Hooks, alles Selbstgeschriebene');
@@ -162,7 +178,7 @@ function dirSize(p) {
162
178
 
163
179
  // ------------------------------------------------------------------ execute
164
180
  function execute(p) {
165
- const { ours, edited, agents, modules, legacy, data } = p;
181
+ const { ours, edited, agents, modules, legacy, data, codenotch } = p;
166
182
  const stamp = new Date().toISOString().replace(/[:.]/g, '').slice(0, 15);
167
183
  let removed = 0, backed = 0;
168
184
 
@@ -235,16 +251,27 @@ function execute(p) {
235
251
  } catch { console.log(` ${tilde(codexToml)} nicht lesbar — von Hand prüfen`); }
236
252
  }
237
253
 
254
+ if (codenotch) {
255
+ const r = cn.uninstallCodenotch(codenotch, { stateFile: cn.stateFilePath(HOME) });
256
+ console.log(` Codenotch: ${r === 'remove' ? 'removed' : r === 'keep' ? 'kept, not the recorded build' : r === 'error' ? 'uninstaller failed, left in place' : 'already gone'}`);
257
+ }
258
+
238
259
  for (const l of legacy) { try { rmSync(l); } catch { /* already gone */ } }
239
260
  if (legacy.length) console.log(` ${legacy.length} Installations-Marker entfernt`);
240
261
 
262
+ const reg = pm.readState(HOME).registered.claudecode;
263
+ if (reg) {
264
+ const r = pm.deregisterClaude({ home: HOME, record: reg });
265
+ if (r.changed) console.log(` bdb-aos Plugin-Registrierung aus settings.json entfernt${reg.replaced ? ` (externer Marketplace ${reg.replaced.key} wiederhergestellt)` : ''}`);
266
+ }
267
+
241
268
  // Only the BDB hook entries leave settings.json; everything else in it is
242
269
  // the user's and must survive an uninstall exactly as it survives an install.
243
270
  const settings = h('.claude', 'settings.json');
244
271
  if (existsSync(settings)) {
245
272
  try {
246
273
  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'];
274
+ 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
275
  let touched = false;
249
276
  for (const [event, entries] of Object.entries(s.hooks || {})) {
250
277
  const kept = entries.filter((e) => !(e.hooks || []).some((x) => bdb.some((n) => String(x.command).includes(n))));
@@ -266,6 +293,7 @@ function execute(p) {
266
293
  for (const f of [MANIFEST, h('.agents', '.bdb-manifest.json')]) {
267
294
  try { rmSync(f); } catch { /* already gone */ }
268
295
  }
296
+ pm.retireState(HOME);
269
297
  }
270
298
 
271
299
  // ---------------------------------------------------------------------- main
@@ -276,6 +304,16 @@ const ask = async (q) => {
276
304
  return a;
277
305
  };
278
306
 
307
+ if (RESTORE) {
308
+ const r = pm.restorePluginBackups({ home: HOME });
309
+ if (!r.dirs.length) { console.log('Keine Plugin-Migrations-Sicherung gefunden.'); process.exit(1); }
310
+ console.log(` ${r.files.length} Dateien aus ${r.dirs.length} Sicherung(en) wiederhergestellt, im Install-Manifest erfasst`);
311
+ console.log('Vorhandene Dateien wurden nicht überschrieben.');
312
+ 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.');
313
+ 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.');
314
+ process.exit(0);
315
+ }
316
+
279
317
  const p = plan();
280
318
  console.log(PURGE ? '\x1b[31mAOS UNINSTALL — PURGE\x1b[0m' : 'AOS Uninstall');
281
319
  if (!describe(p)) process.exit(1);
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ // go-check: one fixed answer to "may this worker run this guarded command?" for callers that
3
+ // are not a harness hook (AO). Installed at ~/.aos/bin/go-check.mjs.
4
+ //
5
+ // node go-check.mjs --session <Name> --command "<shell string>" [--consume]
6
+ // node go-check.mjs --write-patterns <file>
7
+ //
8
+ // stdout: {"guarded":bool,"ok":bool,"scope":[...]|null,"reason":"..."}
9
+ // exit 0 allowed (not guarded, or a valid GO token; --consume uses it up), 1 guarded without a
10
+ // valid GO, 2 error (the caller must treat 2 as denied). Only `GO <Name>` tokens count: mode
11
+ // soft grants and mode off belong to the human session and are never read here. Nothing in
12
+ // this file writes a token; the tool is synchronous, offline and reads no stdin.
13
+
14
+ import { existsSync, realpathSync, writeFileSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { fileURLToPath, pathToFileURL } from "node:url";
17
+
18
+ const here = dirname(fileURLToPath(import.meta.url));
19
+
20
+ async function loadGate() {
21
+ for (const f of [join(here, "go-gate.mjs"), join(here, "..", ".claude", "hooks", "go-gate.mjs")]) {
22
+ if (existsSync(f)) return import(pathToFileURL(f).href);
23
+ }
24
+ throw new Error("go-gate.mjs not found next to go-check.mjs or in ../.claude/hooks");
25
+ }
26
+
27
+ export const patternsJson = (gate) => ({
28
+ version: 1,
29
+ source: "go-gate.mjs GUARDED_PATTERNS (JavaScript RegExp syntax, lookaheads included; not RE2)",
30
+ scopes: gate.SCOPES,
31
+ patterns: gate.GUARDED_PATTERNS.map((r) => ({ source: r.source, flags: r.flags })),
32
+ });
33
+
34
+ export function check(gate, { session, command, consume = false }) {
35
+ if (typeof command !== "string" || !command.trim()) return { code: 2, out: { guarded: null, ok: false, scope: null, reason: "--command is empty" } };
36
+ const storeReason = gate.gateStoreReason(command);
37
+ if (storeReason) return { code: 1, out: { guarded: true, ok: false, scope: null, reason: storeReason } };
38
+ if (!gate.isGuardedCommand(command)) return { code: 0, out: { guarded: false, ok: true, scope: [], reason: "" } };
39
+ const scope = gate.commandScopes(command);
40
+ if (typeof session !== "string" || !gate.slug(session)) {
41
+ return { code: 2, out: { guarded: true, ok: false, scope, reason: "--session is missing or has no usable name" } };
42
+ }
43
+ const r = gate.tokenGrantsGo(session, { consume });
44
+ return r.ok
45
+ ? { code: 0, out: { guarded: true, ok: true, scope, reason: consume ? "GO token consumed" : "GO token valid (not consumed)" } }
46
+ : { code: 1, out: { guarded: true, ok: false, scope, reason: r.reason } };
47
+ }
48
+
49
+ export function parseArgs(argv) {
50
+ const o = {};
51
+ for (let i = 0; i < argv.length; i++) {
52
+ const a = argv[i];
53
+ if (a === "--session") o.session = argv[++i];
54
+ else if (a === "--command") o.command = argv[++i];
55
+ else if (a === "--write-patterns") o.writePatterns = argv[++i];
56
+ else if (a === "--consume") o.consume = true;
57
+ else throw new Error(`unknown argument ${a}`);
58
+ }
59
+ return o;
60
+ }
61
+
62
+ const done = (code, out) => process.stdout.write(JSON.stringify(out) + "\n", () => process.exit(code));
63
+
64
+ const invokedAs = (() => { try { return realpathSync(process.argv[1] || ""); } catch { return ""; } })();
65
+ if (invokedAs === fileURLToPath(import.meta.url)) {
66
+ try {
67
+ const o = parseArgs(process.argv.slice(2));
68
+ const gate = await loadGate();
69
+ if (o.writePatterns) {
70
+ writeFileSync(o.writePatterns, JSON.stringify(patternsJson(gate), null, 2) + "\n");
71
+ done(0, { ok: true, reason: `wrote ${o.writePatterns}` });
72
+ } else {
73
+ const { code, out } = check(gate, o);
74
+ done(code, out);
75
+ }
76
+ } catch (e) {
77
+ done(2, { guarded: null, ok: false, scope: null, reason: String(e?.message || e).slice(0, 300) });
78
+ }
79
+ }
@@ -0,0 +1,106 @@
1
+ {
2
+ "version": 1,
3
+ "source": "go-gate.mjs GUARDED_PATTERNS (JavaScript RegExp syntax, lookaheads included; not RE2)",
4
+ "scopes": [
5
+ "push-feature",
6
+ "push-main",
7
+ "merge",
8
+ "publish",
9
+ "destructive",
10
+ "github-write"
11
+ ],
12
+ "patterns": [
13
+ {
14
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:git\\s+push|git(?:\\s+(?:-[cC]\\s*(?:\"[^\"]*\"|'[^']*'|\\S+)|--[\\w-]+(?:=(?:\"[^\"]*\"|'[^']*'|\\S+))?))+\\s+push)\\b",
15
+ "flags": "i"
16
+ },
17
+ {
18
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?npm\\s+publish\\b",
19
+ "flags": "i"
20
+ },
21
+ {
22
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?npm\\s+version\\b",
23
+ "flags": "i"
24
+ },
25
+ {
26
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:gh\\s+pr\\s+merge|gh(?:\\s+(?:-R|--repo)(?:\\s+|=)\\S+)+\\s+pr\\s+merge)\\b",
27
+ "flags": "i"
28
+ },
29
+ {
30
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:gh\\s+release\\s+create|gh(?:\\s+(?:-R|--repo)(?:\\s+|=)\\S+)+\\s+release\\s+create)\\b",
31
+ "flags": "i"
32
+ },
33
+ {
34
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:git\\s+reset\\s+--hard|(?:git\\s+|git(?:\\s+(?:-[cC]\\s*(?:\"[^\"]*\"|'[^']*'|\\S+)|--[\\w-]+(?:=(?:\"[^\"]*\"|'[^']*'|\\S+))?))+\\s+)reset\\b[^;&|\\n]*\\s--hard)\\b",
35
+ "flags": "i"
36
+ },
37
+ {
38
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:git\\s+clean|git(?:\\s+(?:-[cC]\\s*(?:\"[^\"]*\"|'[^']*'|\\S+)|--[\\w-]+(?:=(?:\"[^\"]*\"|'[^']*'|\\S+))?))+\\s+clean)\\b[^;&|\\n]*\\s(?:-[a-zA-Z]*f[a-zA-Z]*|--force)\\b",
39
+ "flags": "i"
40
+ },
41
+ {
42
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?rm\\s+(?:[^;&|\\n]*\\s)?['\"]?(?:-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)\\b",
43
+ "flags": "i"
44
+ },
45
+ {
46
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:git\\s+branch|git(?:\\s+(?:-[cC]\\s*(?:\"[^\"]*\"|'[^']*'|\\S+)|--[\\w-]+(?:=(?:\"[^\"]*\"|'[^']*'|\\S+))?))+\\s+branch)\\b[^;&|\\n]*\\s(?:-[a-zA-Z]*D[a-zA-Z]*|-[a-zA-Z]*(?:df|fd)[a-zA-Z]*)(?=\\s|$)",
47
+ "flags": ""
48
+ },
49
+ {
50
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:git\\s+branch|git(?:\\s+(?:-[cC]\\s*(?:\"[^\"]*\"|'[^']*'|\\S+)|--[\\w-]+(?:=(?:\"[^\"]*\"|'[^']*'|\\S+))?))+\\s+branch)\\b(?=[^;&|\\n]*\\s(?:-d|--delete)\\b)(?=[^;&|\\n]*\\s(?:-f|--force)\\b)",
51
+ "flags": ""
52
+ },
53
+ {
54
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:git\\s+worktree|git(?:\\s+(?:-[cC]\\s*(?:\"[^\"]*\"|'[^']*'|\\S+)|--[\\w-]+(?:=(?:\"[^\"]*\"|'[^']*'|\\S+))?))+\\s+worktree)\\s+remove\\b",
55
+ "flags": "i"
56
+ },
57
+ {
58
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:gh\\s+|gh(?:\\s+(?:-R|--repo)(?:\\s+|=)\\S+)+\\s+)pr\\s+(?:create|comment|edit|review|close)\\b",
59
+ "flags": "i"
60
+ },
61
+ {
62
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:gh\\s+|gh(?:\\s+(?:-R|--repo)(?:\\s+|=)\\S+)+\\s+)issue\\s+(?:create|comment)\\b",
63
+ "flags": "i"
64
+ },
65
+ {
66
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:gh\\s+|gh(?:\\s+(?:-R|--repo)(?:\\s+|=)\\S+)+\\s+)release\\s+(?:edit|delete)\\b",
67
+ "flags": "i"
68
+ },
69
+ {
70
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:gh\\s+|gh(?:\\s+(?:-R|--repo)(?:\\s+|=)\\S+)+\\s+)repo\\s+(?:create|edit|delete)\\b",
71
+ "flags": "i"
72
+ },
73
+ {
74
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:gh\\s+|gh(?:\\s+(?:-R|--repo)(?:\\s+|=)\\S+)+\\s+)api\\b[^;&|\\n]*\\s(?:-X\\s*|--method[\\s=]+)(?:POST|PATCH|PUT|DELETE)\\b",
75
+ "flags": "i"
76
+ },
77
+ {
78
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?(?:gh\\s+|gh(?:\\s+(?:-R|--repo)(?:\\s+|=)\\S+)+\\s+)api\\b[^;&|\\n]*\\s(?:-[fF]|--field|--raw-field|--input)",
79
+ "flags": ""
80
+ },
81
+ {
82
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?git\\s+(?:-[cC]\\s*\\S+\\s+)*-c\\s*['\"]?alias\\.",
83
+ "flags": "i"
84
+ },
85
+ {
86
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?opencode\\s+run\\b[^;&|\\n]*\\s(?:-[a-zA-Z]*[sc]\\S*|--session|--continue)(?=[\\s=]|$)",
87
+ "flags": ""
88
+ },
89
+ {
90
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?claude\\b[^;&|\\n]*\\s(?:-[a-zA-Z]*[cr]\\S*|--resume|--continue)(?=[\\s=]|$)",
91
+ "flags": ""
92
+ },
93
+ {
94
+ "source": "(?:^|[;&|\\n({\\x60])\\s*(?:(?:sudo|env|command|builtin|exec|nohup|time|xargs|eval|then|do|else)(?:\\s+-{1,2}[\\w-]+(?:=\\S*)?(?:\\s+(?![-'\"])(?!(?:\\S*\\/)?(?:git|gh|npm|rm|opencode|curl|wget)\\b)[^\\s;&|'\"]+)?)*\\s+|(?:bash|sh|zsh|dash|ksh)(?:\\s+-\\w+)*\\s+-c\\s+|\\w+=\\S*\\s+)*['\"]?(?:[^\\s;&|()'\"]*\\/)?opencode\\s+attach\\b",
95
+ "flags": "i"
96
+ },
97
+ {
98
+ "source": "\\/session\\/[^\\s/'\"]+\\/(?:message|prompt_async|command|shell)\\b",
99
+ "flags": "i"
100
+ },
101
+ {
102
+ "source": "\\/\\/[^\\s'\"]*\\/tui\\/|\\/tui\\/(?:append|submit|clear|execute|open|show|publish|control)",
103
+ "flags": "i"
104
+ }
105
+ ]
106
+ }
@@ -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 (`aos-plan-canvas open <dir> --mode bdb-plan-builder`) 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,44 @@
1
+ # BDB AO Codenotch (macOS and Windows, default on)
2
+
3
+ The AOS installer installs the BDB AO Codenotch desktop app by default on **macOS and Windows**. On Linux the step is never offered and no code of it runs.
4
+
5
+ ## Controlling it
6
+
7
+ | Mode | Behavior |
8
+ |---|---|
9
+ | Interactive | Prompt "Install BDB AO Codenotch?", default **yes** |
10
+ | Non-interactive (`-y`, no TTY) | Installs unless opted out |
11
+ | Opt out | `AOS_CODENOTCH=0` or `--no-codenotch` (wins over everything) |
12
+ | Force on | `AOS_CODENOTCH=1` or `--codenotch` (skips the question) |
13
+
14
+ A failure at any step (no release yet, 404, offline, rate limit, checksum mismatch, mount error) prints a warning and the AOS install continues; the exit code is never affected. The installer ends with one line saying whether Codenotch was installed, skipped or failed, and how to remove it.
15
+
16
+ ## Windows
17
+
18
+ - Asset: `Codenotch-Setup-<version>.exe` plus `Codenotch-Setup-<version>.exe.sha256` from the same release. If the exact name is absent, the single `*Setup*.exe` that has a matching `.sha256` is used; anything ambiguous is refused.
19
+ - The SHA-256 is verified before the installer runs; a mismatch only warns.
20
+ - Runs the NSIS installer silently as `Setup.exe /S`, per-user, no admin, no elevation, with a 5 minute timeout, then checks the version in the per-user uninstall registry key (`HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall`, key `Codenotch`, fallback scan for a subkey whose DisplayName is Codenotch).
21
+ - Installed version same or newer: skipped. An older install AOS did not record is kept unless you confirm interactively.
22
+ - The path of the uninstaller and the version are recorded in `~/.agents/.bdb-codenotch.json` (`%USERPROFILE%` on Windows).
23
+ - `aos-uninstall` runs `<recorded uninstall.exe> /S` only if that file still exists and the registry version and uninstaller path still match the record; otherwise the install is left alone.
24
+ - Default install dir is `%LOCALAPPDATA%\Codenotch` (codenotch.exe, codenotch-hook.exe, uninstall.exe). The installer is unsigned, so Windows SmartScreen may warn.
25
+ - **Unverified:** the registry key (`Codenotch`), `DisplayVersion`, `UninstallString`, and the silent `/S` install and uninstall come from Tauri's stock NSIS template, not from a real build. They must be checked on Windows with the first CI-built installer.
26
+
27
+ ## What it does (macOS)
28
+
29
+ 1. Reads the release list of the public repo `hybridlabor-api/bdb-ao-codenotch` (`https://api.github.com/repos/hybridlabor-api/bdb-ao-codenotch/releases`, unauthenticated) and picks the newest release that is neither draft nor prerelease and carries this platform's asset plus a `.sha256`; other releases are ignored. A missing repo (404), no matching release, or a GitHub rate limit prints a friendly note and the install continues; nothing fails.
30
+ 2. Downloads the `.dmg` and verifies its `.sha256` asset from the same release **before** mounting. A mismatch or a missing checksum refuses the install.
31
+ 3. Mounts with `hdiutil attach -nobrowse -readonly -mountpoint <temp dir>` and always detaches again, even on failure.
32
+ 4. Copies the single `.app` from the DMG to `/Applications`, or to `~/Applications` when `/Applications` is not writable. Never uses sudo.
33
+ 5. Runs `xattr -dr com.apple.quarantine` on the copy. The app is **ad-hoc signed** until Apple developer certificates are in place, so Gatekeeper would otherwise block it.
34
+ 6. Records path, version and bundle id in `~/.agents/.bdb-codenotch.json`.
35
+
36
+ ## Idempotence and safety
37
+
38
+ - Installed version same or newer (`CFBundleShortVersionString`): skipped.
39
+ - Older and installed by AOS (recorded path, bundle id and version still match): asked in interactive mode, replaced automatically with `AOS_CODENOTCH=1`. The old copy is restored if the new copy fails.
40
+ - Older and not installed by AOS: kept with a note in non-interactive mode; in interactive mode replaced only after you confirm.
41
+
42
+ ## Uninstall
43
+
44
+ `aos-uninstall` removes only the recorded `.app`, and only while its bundle id and version still match the record. A replaced or otherwise unknown app is kept. `--dry-run` lists the decision.