@sabaiway/agent-workflow-kit 5.11.2 → 7.0.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 (38) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/README.md +3 -2
  3. package/SKILL.md +5 -1
  4. package/bridges/antigravity-cli-bridge/bin/agy-review-await-guard.test.mjs +176 -0
  5. package/bridges/antigravity-cli-bridge/bin/agy-review.sh +61 -14
  6. package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +606 -467
  7. package/bridges/antigravity-cli-bridge/references/review-prompt.md +42 -4
  8. package/bridges/codex-cli-bridge/SKILL.md +18 -5
  9. package/bridges/codex-cli-bridge/bin/codex-await-guard.test.mjs +161 -0
  10. package/bridges/codex-cli-bridge/bin/codex-exec.sh +22 -17
  11. package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +356 -363
  12. package/bridges/codex-cli-bridge/bin/codex-review.sh +6 -6
  13. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +275 -286
  14. package/bridges/codex-cli-bridge/capability.json +1 -1
  15. package/bridges/codex-cli-bridge/references/driving-codex.md +4 -2
  16. package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +3 -2
  17. package/bridges/codex-cli-bridge/setup/README.md +3 -1
  18. package/capability.json +1 -1
  19. package/package.json +1 -1
  20. package/references/hooks/gate-approve.mjs +1 -1
  21. package/references/modes/grounding.md +1 -1
  22. package/references/modes/mcp.md +37 -0
  23. package/references/modes/procedures.md +3 -3
  24. package/references/modes/recommendations.md +1 -0
  25. package/references/modes/uninstall.md +2 -1
  26. package/references/templates/agent_rules.md +4 -5
  27. package/tools/commands.mjs +7 -0
  28. package/tools/direct-run.mjs +3 -0
  29. package/tools/doc-parity.mjs +18 -2
  30. package/tools/grounding.mjs +10 -20
  31. package/tools/inject-methodology.mjs +2 -0
  32. package/tools/mcp-registration.mjs +283 -0
  33. package/tools/mcp-server.mjs +314 -0
  34. package/tools/mcp-stdio.mjs +229 -0
  35. package/tools/mcp.mjs +299 -0
  36. package/tools/procedures.mjs +7 -8
  37. package/tools/recommendations.mjs +90 -1
  38. package/tools/uninstall.mjs +356 -45
@@ -59,6 +59,13 @@ import { shellQuoteArg } from './review-state.mjs';
59
59
  import { isFinalCapableDeclaration } from './run-gates.mjs';
60
60
  import { loadDeclaration, canonicalCheckerGates, coverageProducerPrecedes, isKitOwnedCheckerGate, GATES_REL, LCOV_PRODUCER_KEY } from './gates-declaration.mjs';
61
61
  import { readRegularFileNoFollow } from './fs-read-nofollow.mjs';
62
+ // The typed channel's READ-ONLY leaf only — never tools/mcp.mjs, which reaches the write core.
63
+ import {
64
+ MCP_JSON_REL,
65
+ SERVER_NAME as MCP_SERVER_NAME,
66
+ STATE as MCP_STATE,
67
+ readRegistration,
68
+ } from './mcp-registration.mjs';
62
69
  import { matchesCoverageProducer, isCoverageProducerGate } from './coverage-producer.mjs';
63
70
  // How much of the TRACKED tree the changed-line coverage domain can assess at all — the fact that
64
71
  // turns "the checker certifies" into "the checker certifies the assessable minority".
@@ -143,6 +150,13 @@ export const SEVERITIES = Object.freeze({
143
150
  'read-lane.stale': SEVERITY_ATTENTION,
144
151
  'read-lane.missing': SEVERITY_ATTENTION,
145
152
  'state-block': SEVERITY_OPTIONAL,
153
+ // The typed channel. The base arm is an ordinary offer; `.masked` stays an offer too (nothing is
154
+ // broken — the kit simply cannot write through a device node, so the remedy is handed over);
155
+ // `.differing` reports a CONFIGURED declaration that would launch something else, which is the
156
+ // one state here a maintainer must actually look at.
157
+ 'mcp-channel': SEVERITY_OPTIONAL,
158
+ 'mcp-channel.masked': SEVERITY_OPTIONAL,
159
+ 'mcp-channel.differing': SEVERITY_ATTENTION,
146
160
  agents: SEVERITY_OPTIONAL,
147
161
  'family-freshness': SEVERITY_ATTENTION,
148
162
  'adr-store-migration': SEVERITY_ATTENTION,
@@ -210,6 +224,9 @@ export const WHATS = Object.freeze({
210
224
  'read-lane.stale': 'the read-lane is ON but the placed gate hook is stale — an old hook never reads lanes.json, so the lane is silently dark; reseed it',
211
225
  'read-lane.missing': 'the gate hook is wired but its placed file is missing — every Bash call errors and the read-lane is dark; re-place it',
212
226
  'state-block': 'nothing checks the closing state block — a turn that ends on «nothing needed from you», or on a promise it never started, passes unseen',
227
+ 'mcp-channel': "the kit's read-only MCP server is not registered here — path questions and literal searches stay shell strings",
228
+ 'mcp-channel.masked': '{rel} is a {className} here (a sandbox device mask is the usual cause), so the entry to merge is printed instead',
229
+ 'mcp-channel.differing': 'an "{server}" MCP entry is already declared here and DIFFERS from the registration this kit copy would write',
213
230
  agents: '{n} read-only subagent(s) not placed (Claude Code) — no shell-free vehicle for that work; the apply PREVIEWS first',
214
231
  'family-freshness': '{parts}',
215
232
  'adr-store-migration': 'still on the retired 3-tier ADR layout — {shape}',
@@ -266,6 +283,7 @@ export const BENEFITS = Object.freeze({
266
283
  'commit-guard': 'integrity — commits require the ONE green --final receipt at the exact staged fingerprint (consented pre-commit arm)',
267
284
  'read-lane': 'velocity — pipes/chains of your seeded read-only commands auto-approve instead of prompting (opt-in, conservatively classified)',
268
285
  'state-block': 'no silent stalls — a turn ending on «you are not needed», or on work it never started, warns at once instead of waiting to be spotted',
286
+ 'mcp-channel': 'velocity — path facts and literal searches arrive as typed tool calls whose arguments are JSON fields, never a shell string',
269
287
  agents: 'cost and quiet — mechanical work runs on a cheap model, and no vehicle has a shell, so a read-only fan-out cannot flood you with prompts',
270
288
  'family-freshness': 'currency — placed family members carry the latest shipped fixes and features',
271
289
  'adr-store-migration': 'durability — every decision becomes its own file with a generated navigator, instead of one hand-rotated pile',
@@ -311,6 +329,7 @@ export const OPT_IN_CAPABILITIES = Object.freeze([
311
329
  { id: 'commit-guard', mode: 'commit-guard', advisorKey: 'commit-guard' },
312
330
  { id: 'state-block', mode: 'state-block-guard', advisorKey: 'state-block' },
313
331
  { id: 'sandbox-masks', mode: 'sandbox-masks', advisorKey: 'sandbox-masks' },
332
+ { id: 'mcp-channel', mode: 'mcp', advisorKey: 'mcp-channel' },
314
333
  { id: 'worktrees-dir', mode: 'worktrees', advisorKey: 'worktrees-dir' },
315
334
  { id: 'family-freshness', mode: 'upgrade', advisorKey: 'family-freshness' },
316
335
  { id: 'adr-store-migration', mode: 'migrate-adr-store', advisorKey: 'adr-store-migration' },
@@ -1189,7 +1208,7 @@ const readReadLaneToggle = (root, deps) => {
1189
1208
  // D3: the risk-marked keys — every key here has a per-item posture note in the mode doc, surfaced
1190
1209
  // at the consent moment; the static contract test asserts EXACT bidirectional coverage
1191
1210
  // (risk-marked keys == mode-doc note keys — a dropped note goes red, not silent).
1192
- export const RISK_NOTED_KEYS = Object.freeze(['sandbox-lane', 'read-lane', 'worktrees-dir', 'adr-store-migration', 'gates-inert', 'source-size', 'gate-hook']);
1211
+ export const RISK_NOTED_KEYS = Object.freeze(['sandbox-lane', 'read-lane', 'worktrees-dir', 'adr-store-migration', 'gates-inert', 'source-size', 'gate-hook', 'mcp-channel']);
1193
1212
 
1194
1213
  const probeSandboxLane = ({ root, deps, add, skip }) => {
1195
1214
  try {
@@ -1384,6 +1403,75 @@ export const probeAdrStore = ({ root, deps, add, skip }) => {
1384
1403
  }
1385
1404
  };
1386
1405
 
1406
+ // The typed-channel item. It asks the READ-ONLY registration leaf and nothing else — importing the
1407
+ // writer would pull the atomic-write core into the advisor's graph (read-graph-purity.test.mjs).
1408
+ // The rendered apply is the mode's FLAGLESS preview on purpose: registering an MCP server means the
1409
+ // client will RUN that command, so the entry is read before it is declared and the `--apply` stays
1410
+ // the maintainer's separate step.
1411
+ const probeMcpChannel = ({ root, deps, add, skip }) => {
1412
+ try {
1413
+ const registration = readRegistration(root, deps);
1414
+ // A target we could not read is never turned into a verdict about what it contains.
1415
+ const assertReadable = (target) => {
1416
+ if (target.state === MCP_STATE.FOREIGN) throw new Error(`${target.rel} is a ${target.className} — refusing to read through it`);
1417
+ if (target.state === MCP_STATE.MALFORMED) throw new Error(`${target.rel} is ${target.reason}`);
1418
+ if (target.state === MCP_STATE.UNREADABLE) throw new Error(`${target.rel} cannot be read (${target.reason})`);
1419
+ };
1420
+ const preview = `node ${q(toolPath('mcp.mjs'))} --cwd ${q(root)}`;
1421
+ // ORDER: the `.mcp.json` half is judged and REPORTED on its own before anything about the
1422
+ // settings half can end the probe. A differing entry is fully observable, and an unreadable
1423
+ // settings file says nothing about it — returning on the settings mask first hid it.
1424
+ assertReadable(registration.mcpJson);
1425
+ const settingsUsable = registration.settings.state !== MCP_STATE.MASKED
1426
+ && registration.settings.state !== MCP_STATE.FOREIGN
1427
+ && registration.settings.state !== MCP_STATE.UNREADABLE
1428
+ && registration.settings.state !== MCP_STATE.MALFORMED;
1429
+ if (registration.mcpJson.differs) {
1430
+ // The remedy is the maintainer's edit either way; the "then run" tail is dropped where that
1431
+ // command could not succeed, so the item never hands over a line that exits 1.
1432
+ const tail = settingsUsable ? `, then run ${preview}` : '';
1433
+ add(
1434
+ 'mcp-channel',
1435
+ fillTemplate(WHATS['mcp-channel.differing'], { server: MCP_SERVER_NAME }),
1436
+ `HAND-APPLY: edit ${q(join(root, MCP_JSON_REL))} → remove or rename the "${MCP_SERVER_NAME}" entry${tail}`,
1437
+ 'mcp-channel.differing',
1438
+ );
1439
+ return;
1440
+ }
1441
+ assertReadable(registration.settings);
1442
+ // The HAND-APPLY arm renders the MODE's own preview, so it may fire only where that command can
1443
+ // actually run — and the writer refuses a masked settings.json outright (it can neither write it
1444
+ // nor merge into what it cannot read). A masked settings half is therefore a stated SKIP: the
1445
+ // completeness this item decides on is unknowable, and offering a command that exits 1 is worse
1446
+ // than saying nothing.
1447
+ if (registration.settings.state === MCP_STATE.MASKED) {
1448
+ skip('mcp-channel', new Error(`${registration.settings.rel} is a ${registration.settings.className} here (a sandbox device mask is the usual cause) — the registration cannot be judged or written from in here; verify it outside the sandbox`));
1449
+ return;
1450
+ }
1451
+ if (registration.mcpJson.state === MCP_STATE.MASKED) {
1452
+ // The kit cannot write through the mask either, so the remedy is HAND-APPLY. But when the
1453
+ // settings half is already complete, the registration was almost certainly made from outside
1454
+ // the sandbox: what cannot be observed becomes a stated SKIP (optimality withheld), never the
1455
+ // same offer again on every single upgrade.
1456
+ if (registration.settings.complete) {
1457
+ skip('mcp-channel', new Error(`${registration.mcpJson.rel} is a ${registration.mcpJson.className} here (a sandbox device mask is the usual cause) and the settings half is already complete — verify the entry outside the sandbox`));
1458
+ return;
1459
+ }
1460
+ add(
1461
+ 'mcp-channel',
1462
+ fillTemplate(WHATS['mcp-channel.masked'], { rel: registration.mcpJson.rel, className: registration.mcpJson.className }),
1463
+ `HAND-APPLY: ${preview}`,
1464
+ 'mcp-channel.masked',
1465
+ );
1466
+ return;
1467
+ }
1468
+ if (registration.registered) return;
1469
+ add('mcp-channel', fillTemplate(WHATS['mcp-channel'], {}), preview);
1470
+ } catch (err) {
1471
+ skip('mcp-channel', err);
1472
+ }
1473
+ };
1474
+
1387
1475
  // ── assembly (frozen presentation order) ─────────────────────────────────────────────────────────
1388
1476
  const PROBES = Object.freeze([
1389
1477
  probeVelocityItems,
@@ -1402,6 +1490,7 @@ const PROBES = Object.freeze([
1402
1490
  probeMasksItem,
1403
1491
  probeSandboxLane,
1404
1492
  probeWorktreesDir,
1493
+ probeMcpChannel,
1405
1494
  ]);
1406
1495
 
1407
1496
  export const buildRecommendations = ({ cwd, deps = {} } = {}) => {