@akagilnc/pi-workflow-roles 0.1.4444 → 0.1.4489

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 (44) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +2 -0
  3. package/dist/acp-host/description.js +2 -3
  4. package/dist/acp-host/production-host.js +140 -107
  5. package/dist/auditor-soul.js +8 -1
  6. package/dist/headless-host/description.js +3 -3
  7. package/dist/headless-host/production-host.js +147 -76
  8. package/dist/host-descriptions.js +3 -18
  9. package/dist/method-host-plugin/.claude-plugin/plugin.json +5 -0
  10. package/dist/method-host-plugin/skills/ak-cross-m-review/CONTEXT.md +48 -0
  11. package/dist/method-host-plugin/skills/ak-cross-m-review/LICENSE +21 -0
  12. package/dist/method-host-plugin/skills/ak-cross-m-review/SKILL.md +170 -0
  13. package/dist/method-host-plugin/skills/ak-cross-m-review/prompts/cmr-completeness.md +118 -0
  14. package/dist/method-host-plugin/skills/ak-cross-m-review/prompts/cmr-reviewer.md +128 -0
  15. package/dist/method-host-plugin/skills/ak-cross-m-review/provenance.json +41 -0
  16. package/dist/method-host-plugin/skills/diagnosing-bugs/SKILL.md +134 -0
  17. package/dist/method-host-plugin/skills/diagnosing-bugs/agents/openai.yaml +3 -0
  18. package/dist/method-host-plugin/skills/diagnosing-bugs/provenance.json +31 -0
  19. package/dist/method-host-plugin/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  20. package/dist/method-host-plugin/skills/resolving-merge-conflicts/SKILL.md +14 -0
  21. package/dist/method-host-plugin/skills/resolving-merge-conflicts/agents/openai.yaml +3 -0
  22. package/dist/method-host-plugin/skills/resolving-merge-conflicts/provenance.json +26 -0
  23. package/dist/method-host-plugin/skills/tdd/SKILL.md +38 -0
  24. package/dist/method-host-plugin/skills/tdd/agents/openai.yaml +3 -0
  25. package/dist/method-host-plugin/skills/tdd/mocking.md +59 -0
  26. package/dist/method-host-plugin/skills/tdd/provenance.json +36 -0
  27. package/dist/method-host-plugin/skills/tdd/tests.md +77 -0
  28. package/dist/public-cli/main.js +14 -21
  29. package/dist/session-opening-materials.js +17 -5
  30. package/extensions/role-runtime.ts +16 -6
  31. package/package.json +1 -1
  32. package/resources/method-host-plugin/.claude-plugin/plugin.json +5 -0
  33. package/scripts/build-package.mjs +6 -1
  34. package/src/acp-host/description.ts +2 -4
  35. package/src/acp-host/production-host.ts +0 -1
  36. package/src/auditor-soul.ts +10 -1
  37. package/src/headless-host/description.ts +4 -3
  38. package/src/headless-host/role-turn-host.ts +27 -4
  39. package/src/host-descriptions.ts +3 -23
  40. package/src/host-native-method.ts +67 -0
  41. package/src/role-envelope.ts +17 -47
  42. package/src/role-runtime-dependencies.ts +17 -2
  43. package/src/role-runtime.ts +12 -7
  44. package/src/session-opening-materials.ts +27 -11
@@ -66,9 +66,16 @@ export async function loadAuditorSoul(role) {
66
66
  if (soul.trim().length === 0) {
67
67
  throw new Error(`The ${role} auditor Soul is blank`);
68
68
  }
69
- return joinPackageMaterials(materials);
69
+ return soul;
70
+ }
71
+ export function loadAuditorReferenceMaterials(role) {
72
+ const soulPath = auditorSoulRelativePath(role);
73
+ return joinPackageMaterials(AUDITOR_SESSION_MATERIALS[role].filter((path) => path !== soulPath));
70
74
  }
71
75
  /** Runtime loader: subject input decides which soul file to assemble. */
72
76
  export async function loadAuditorSoulFromSubjectInput(raw) {
73
77
  return loadAuditorSoul(resolveAuditorSubject(raw));
74
78
  }
79
+ export function loadAuditorReferenceMaterialsFromSubjectInput(raw) {
80
+ return loadAuditorReferenceMaterials(resolveAuditorSubject(raw));
81
+ }
@@ -30,6 +30,8 @@ export function headlessTurnArgs(options) {
30
30
  description.jsonSchemaFlag,
31
31
  JSON.stringify(options.jsonSchema),
32
32
  ];
33
+ if (options.pluginDir)
34
+ args.push("--plugin-dir", options.pluginDir);
33
35
  if (options.mcpConfigPath !== undefined && options.mcpConfigPath !== "") {
34
36
  args.push(description.mcpConfigFlag, options.mcpConfigPath);
35
37
  }
@@ -321,9 +323,7 @@ export function codexTurnArgs(options) {
321
323
  }
322
324
  // JSONL event stream: thread_id + final agent_message + turn.completed/failed.
323
325
  args.push("--json");
324
- // Operator config/MCP off; auth still uses CODEX_HOME (official).
325
- // Project/system config and AGENTS.md have no official suppression switch.
326
- args.push("--ignore-user-config", "--ignore-rules");
326
+ // Operator config/skills stay open (#922 host-native-loader). Auth uses CODEX_HOME.
327
327
  const roots = (options.writableRoots ?? []).filter((root) => root !== "");
328
328
  if (roots.length > 0) {
329
329
  // Resume has no --add-dir; the config key keeps extra roots available on both paths.
@@ -1559,10 +1559,10 @@ __export(session_assistant_usage_exports, {
1559
1559
  });
1560
1560
  import { join as join5 } from "node:path";
1561
1561
  async function readAssistantUsageFromSessionFile(sessionFile) {
1562
- const { readFile: readFile24 } = await import("node:fs/promises");
1562
+ const { readFile: readFile23 } = await import("node:fs/promises");
1563
1563
  let text;
1564
1564
  try {
1565
- text = await readFile24(sessionFile, "utf8");
1565
+ text = await readFile23(sessionFile, "utf8");
1566
1566
  } catch (error) {
1567
1567
  if (error?.code === "ENOENT") return void 0;
1568
1568
  throw error;
@@ -3801,13 +3801,10 @@ function assertRegisteredHostName(host) {
3801
3801
  }
3802
3802
  throw new Error(`unregistered host: ${host}`);
3803
3803
  }
3804
- var PRIVATE_COMPAT_ENV, DEFAULT_ROLE_TURN_HOST, HOST_DESCRIPTIONS, HEADLESS_HOST_DESCRIPTIONS;
3804
+ var DEFAULT_ROLE_TURN_HOST, HOST_DESCRIPTIONS, HEADLESS_HOST_DESCRIPTIONS;
3805
3805
  var init_host_descriptions = __esm({
3806
3806
  "src/host-descriptions.ts"() {
3807
3807
  "use strict";
3808
- PRIVATE_COMPAT_ENV = Object.fromEntries(
3809
- ["CLAUDE", "CURSOR", "CODEX"].flatMap((vendor) => ["SKILLS", "RULES", "AGENTS", "MCPS", "HOOKS", "SESSIONS"].map((kind) => [`GROK_${vendor}_${kind}_ENABLED`, "false"]))
3810
- );
3811
3808
  DEFAULT_ROLE_TURN_HOST = "pi";
3812
3809
  HOST_DESCRIPTIONS = Object.freeze({
3813
3810
  /** Operator home `~/.grok`, native session/load resume, `agent [--model X] stdio`. */
@@ -3820,12 +3817,7 @@ var init_host_descriptions = __esm({
3820
3817
  }),
3821
3818
  modelPassing: "argv",
3822
3819
  boundResume: "session/load",
3823
- sessionBindingFile: "grok-acp-session.json",
3824
- childEnv: Object.freeze({
3825
- ...PRIVATE_COMPAT_ENV,
3826
- GROK_MEMORY: "0",
3827
- GROK_SUBAGENTS: "0"
3828
- })
3820
+ sessionBindingFile: "grok-acp-session.json"
3829
3821
  }),
3830
3822
  /**
3831
3823
  * Operator home `~/.hermes`, native session/load resume, `acp` subcommand.
@@ -3844,7 +3836,6 @@ var init_host_descriptions = __esm({
3844
3836
  modelPassing: "set_model",
3845
3837
  boundResume: "session/load",
3846
3838
  sessionBindingFile: "hermes-acp-session.json",
3847
- childEnv: Object.freeze({}),
3848
3839
  seatProfileSoul: Object.freeze({
3849
3840
  flag: "-p",
3850
3841
  namePrefix: "ak-",
@@ -3865,12 +3856,7 @@ var init_host_descriptions = __esm({
3865
3856
  // Intermediate assistant/tool/system events require verbose with stream-json.
3866
3857
  "--verbose",
3867
3858
  "--permission-mode",
3868
- "bypassPermissions",
3869
- // Empty sources: no user/project/local operator surface (envelope owns materials).
3870
- "--setting-sources",
3871
- "",
3872
- // With adapter-supplied --mcp-config only (AK relay); drops operator + claude.ai MCP.
3873
- "--strict-mcp-config"
3859
+ "bypassPermissions"
3874
3860
  ]),
3875
3861
  promptFlag: "-p",
3876
3862
  modelFlag: "--model",
@@ -10765,10 +10751,10 @@ function pairGateRounds(volumes) {
10765
10751
  return rounds.sort((a, b) => a.officerStartedAt.localeCompare(b.officerStartedAt)).map((round, index) => ({ ...round, roundIndex: index + 1 }));
10766
10752
  }
10767
10753
  async function resolveOfficerSessionFromPointerFile(pointerPath) {
10768
- const { readFile: readFile24 } = await import("node:fs/promises");
10754
+ const { readFile: readFile23 } = await import("node:fs/promises");
10769
10755
  let raw;
10770
10756
  try {
10771
- raw = JSON.parse(await readFile24(pointerPath, "utf8"));
10757
+ raw = JSON.parse(await readFile23(pointerPath, "utf8"));
10772
10758
  } catch (error) {
10773
10759
  throw new Error(
10774
10760
  `direct officer run pointer unreadable in ${pointerPath}: ${error instanceof Error ? error.message : String(error)}`,
@@ -11369,16 +11355,27 @@ async function readPackageMaterial(relativePath) {
11369
11355
  }
11370
11356
  async function joinPackageMaterials(relativePaths) {
11371
11357
  const chunks = [];
11372
- for (const relativePath of relativePaths) {
11373
- chunks.push(await readPackageMaterial(relativePath));
11374
- }
11358
+ for (const relativePath of relativePaths) chunks.push(await readPackageMaterial(relativePath));
11375
11359
  return chunks.join("\n\n");
11376
11360
  }
11361
+ function roleSoulPath(role, materials) {
11362
+ const suffix = `/souls/${role}.md`;
11363
+ const path = materials.find((candidate) => `/${candidate}`.endsWith(suffix));
11364
+ if (path === void 0) throw new Error(`session materials omit the ${role} Soul`);
11365
+ return path;
11366
+ }
11367
+ async function loadSeparatedSessionPart(role, materials, part) {
11368
+ const soul = roleSoulPath(role, materials);
11369
+ return part === "soul" ? readPackageMaterial(soul) : joinPackageMaterials(materials.filter((path) => path !== soul));
11370
+ }
11377
11371
  function loadMainRoleSessionMaterials(role) {
11378
- return joinPackageMaterials(MAIN_ROLE_SESSION_MATERIALS[role]);
11372
+ return loadSeparatedSessionPart(role, MAIN_ROLE_SESSION_MATERIALS[role], "soul");
11373
+ }
11374
+ function loadMainRoleReferenceMaterials(role) {
11375
+ return loadSeparatedSessionPart(role, MAIN_ROLE_SESSION_MATERIALS[role], "references");
11379
11376
  }
11380
11377
  function loadGatekeeperSessionMaterials(role) {
11381
- return joinPackageMaterials(GATEKEEPER_SESSION_MATERIALS[role]);
11378
+ return loadSeparatedSessionPart(role, GATEKEEPER_SESSION_MATERIALS[role], "soul");
11382
11379
  }
11383
11380
  var packageRootUrl, MAIN_ROLE_SESSION_MATERIALS, GATEKEEPER_SESSION_MATERIALS;
11384
11381
  var init_session_opening_materials = __esm({
@@ -11408,6 +11405,8 @@ __export(auditor_soul_exports, {
11408
11405
  AUDITOR_SESSION_MATERIALS: () => AUDITOR_SESSION_MATERIALS,
11409
11406
  AUDITOR_SOUL_ROLES: () => AUDITOR_SOUL_ROLES,
11410
11407
  isAuditorSoulRole: () => isAuditorSoulRole,
11408
+ loadAuditorReferenceMaterials: () => loadAuditorReferenceMaterials,
11409
+ loadAuditorReferenceMaterialsFromSubjectInput: () => loadAuditorReferenceMaterialsFromSubjectInput,
11411
11410
  loadAuditorSoul: () => loadAuditorSoul,
11412
11411
  loadAuditorSoulFromSubjectInput: () => loadAuditorSoulFromSubjectInput,
11413
11412
  resolveAuditorSubject: () => resolveAuditorSubject
@@ -11434,11 +11433,18 @@ async function loadAuditorSoul(role) {
11434
11433
  if (soul.trim().length === 0) {
11435
11434
  throw new Error(`The ${role} auditor Soul is blank`);
11436
11435
  }
11437
- return joinPackageMaterials(materials);
11436
+ return soul;
11437
+ }
11438
+ function loadAuditorReferenceMaterials(role) {
11439
+ const soulPath = auditorSoulRelativePath(role);
11440
+ return joinPackageMaterials(AUDITOR_SESSION_MATERIALS[role].filter((path) => path !== soulPath));
11438
11441
  }
11439
11442
  async function loadAuditorSoulFromSubjectInput(raw) {
11440
11443
  return loadAuditorSoul(resolveAuditorSubject(raw));
11441
11444
  }
11445
+ function loadAuditorReferenceMaterialsFromSubjectInput(raw) {
11446
+ return loadAuditorReferenceMaterials(resolveAuditorSubject(raw));
11447
+ }
11442
11448
  var AUDITOR_SOUL_ROLES, AK_ROLE_AUDITOR_SUBJECT_ENV, AK_ROLE_AUDITOR_SOURCE_RUN_ENV, AUDITOR_SESSION_MATERIALS;
11443
11449
  var init_auditor_soul = __esm({
11444
11450
  "src/auditor-soul.ts"() {
@@ -25266,6 +25272,7 @@ function createRoleRuntimeExtension(dependencies) {
25266
25272
  });
25267
25273
  let admitted = false;
25268
25274
  let selectedRole;
25275
+ let roleReferenceMaterials = "";
25269
25276
  let activeReviewerParent;
25270
25277
  let reviewerOriginalRequest;
25271
25278
  let reviewerExpansionCaptured = false;
@@ -25359,10 +25366,10 @@ function createRoleRuntimeExtension(dependencies) {
25359
25366
  activeReviewerParent.skillBinding.name,
25360
25367
  text
25361
25368
  ) ?? text;
25362
- return text === event.text ? { action: "continue" } : { action: "transform", text };
25363
25369
  }
25364
- return text === event.text ? { action: "continue" } : { action: "transform", text };
25370
+ return { action: "continue" };
25365
25371
  });
25372
+ roleHost.on("before_agent_start", () => roleReferenceMaterials === "" ? void 0 : { readingMaterial: { kind: "role-reference-materials", content: roleReferenceMaterials } });
25366
25373
  roleHost.on("before_agent_start", async (event, ctx) => {
25367
25374
  const role = roleHost.getFlag(ROLE_FLAG.name);
25368
25375
  const prompt = event.prompt;
@@ -25928,6 +25935,7 @@ function createRoleRuntimeExtension(dependencies) {
25928
25935
  }
25929
25936
  admitted = false;
25930
25937
  selectedRole = void 0;
25938
+ roleReferenceMaterials = "";
25931
25939
  activeReviewerParent = void 0;
25932
25940
  reviewerOriginalRequest = void 0;
25933
25941
  reviewerExpansionCaptured = false;
@@ -26062,6 +26070,7 @@ function createRoleRuntimeExtension(dependencies) {
26062
26070
  }
26063
26071
  }
26064
26072
  await executeActivationStage(entry.role, activationStage(entry.role, runtime), { clock, writeTrace });
26073
+ roleReferenceMaterials = await dependencies.loadRoleReferenceMaterials?.(entry.role) ?? "";
26065
26074
  if (!engineDetourRegistered) {
26066
26075
  engineDetourRegistered = registerEngineDetourTool(roleHost, hostActions);
26067
26076
  }
@@ -26101,11 +26110,15 @@ var navigatorRoutePlaybookPath = fileURLToPath2(
26101
26110
  var collectorHandbookSeedPath = fileURLToPath2(
26102
26111
  new URL("../resources/collector-bot-handbook.md", import.meta.url)
26103
26112
  );
26113
+ function loadPackagedRoleReferenceMaterials(role) {
26114
+ return role === "auditor" ? loadAuditorReferenceMaterialsFromSubjectInput() : loadMainRoleReferenceMaterials(role);
26115
+ }
26104
26116
  function createRoleRuntimeDependencies(packageRoot) {
26105
26117
  const doctorAuditor = createPiDoctorAuditor();
26106
26118
  const navigatorSessionFactory = createNativeNavigatorSessionFactory();
26107
26119
  return {
26108
26120
  packageRoot,
26121
+ loadRoleReferenceMaterials: loadPackagedRoleReferenceMaterials,
26109
26122
  loadJudgeSoul: () => loadMainRoleSessionMaterials("judge"),
26110
26123
  loadFixerSoul: () => loadMainRoleSessionMaterials("fixer"),
26111
26124
  loadFixPacket: (path) => readFile22(path, "utf8"),
@@ -26166,9 +26179,9 @@ function createRoleRuntimeDependencies(packageRoot) {
26166
26179
  // src/role-envelope.ts
26167
26180
  init_engine_detour();
26168
26181
  import { randomUUID as randomUUID8 } from "node:crypto";
26169
- import { mkdir as mkdir5, readFile as readFile23, writeFile as writeFile12 } from "node:fs/promises";
26182
+ import { mkdir as mkdir6, writeFile as writeFile12 } from "node:fs/promises";
26170
26183
  import { createServer } from "node:net";
26171
- import { basename as basename9, dirname as dirname23, join as join40 } from "node:path";
26184
+ import { dirname as dirname24, join as join41 } from "node:path";
26172
26185
  import { fileURLToPath as fileURLToPath3 } from "node:url";
26173
26186
 
26174
26187
  // src/gatekeeper-pass-envelope.ts
@@ -26253,25 +26266,68 @@ async function requireGatekeeperPass(options) {
26253
26266
  }
26254
26267
  }
26255
26268
 
26269
+ // src/host-native-method.ts
26270
+ import { lstat as lstat7, mkdir as mkdir5, readlink, realpath as realpath7, symlink } from "node:fs/promises";
26271
+ import { basename as basename9, dirname as dirname23, join as join40 } from "node:path";
26272
+ var HOST_METHOD_PLUGIN_NAME = "ak-methods";
26273
+ var packagedMethodsDir = (root) => join40(root, "resources", "methods");
26274
+ var packagedMethodPluginDir = (root) => join40(root, "dist", "method-host-plugin");
26275
+ function hostMethodSkills(methods) {
26276
+ return Object.freeze(methods.flatMap((method) => {
26277
+ if (method.kind !== "skill") return [];
26278
+ const name = basename9(dirname23(method.path));
26279
+ return name ? [Object.freeze({ name })] : [];
26280
+ }));
26281
+ }
26282
+ var alreadyPrefixed = (prompt, token) => {
26283
+ const text = prompt.trimStart();
26284
+ return text === token || text.startsWith(`${token} `) || text.startsWith(`${token}
26285
+ `);
26286
+ };
26287
+ function prefixMethodInvocation(token, prompt) {
26288
+ if (alreadyPrefixed(prompt, token)) return prompt;
26289
+ return prompt ? `${token} ${prompt}` : token;
26290
+ }
26291
+ function applyClaudeSkillInvocation(methods, prompt) {
26292
+ const skills = hostMethodSkills(methods);
26293
+ if (skills.length !== 1) return prompt;
26294
+ return prefixMethodInvocation(`/${HOST_METHOD_PLUGIN_NAME}:${skills[0].name}`, prompt);
26295
+ }
26296
+ function applyCodexSkillInvocation(methods, prompt) {
26297
+ return hostMethodSkills(methods).reduceRight(
26298
+ (invocation, skill) => prefixMethodInvocation(`$${skill.name}`, invocation),
26299
+ prompt
26300
+ );
26301
+ }
26302
+ async function installWorkspaceMethodSkills(cwd, packageRoot) {
26303
+ const target = await realpath7(packagedMethodsDir(packageRoot));
26304
+ const link = join40(cwd, ".agents", "skills");
26305
+ try {
26306
+ const stat2 = await lstat7(link);
26307
+ if (!stat2.isSymbolicLink() || await realpath7(link) !== target) {
26308
+ const detail = stat2.isSymbolicLink() ? `symlink to ${await readlink(link)}` : "non-symlink entry";
26309
+ throw new Error(`workspace method catalog conflict at ${link}: ${detail}`);
26310
+ }
26311
+ return;
26312
+ } catch (error) {
26313
+ if (error.code !== "ENOENT") throw error;
26314
+ }
26315
+ await mkdir5(dirname23(link), { recursive: true });
26316
+ try {
26317
+ await symlink(target, link);
26318
+ } catch (error) {
26319
+ if (error.code !== "EEXIST") throw error;
26320
+ if (await realpath7(link).catch(() => "") !== target) {
26321
+ throw new Error(`workspace method catalog conflict at ${link}`);
26322
+ }
26323
+ }
26324
+ }
26325
+
26256
26326
  // src/role-envelope.ts
26257
26327
  init_packaged_role_registry();
26258
- init_method_skill();
26259
26328
  init_role_activation_flags();
26260
26329
  init_submission_correctable_error();
26261
26330
  init_navigator_invocation_identity();
26262
- function buildSkillExpansion(methodSkills, prompt) {
26263
- if (methodSkills.size !== 1) return void 0;
26264
- const entry = methodSkills.entries().next().value;
26265
- if (entry === void 0) return void 0;
26266
- const [name, method] = entry;
26267
- return Object.freeze({
26268
- name,
26269
- location: method.path,
26270
- content: method.body,
26271
- // Non-pi typed chain keeps original task bytes (ticket #822 r3); no consumer trim.
26272
- userMessage: prompt
26273
- });
26274
- }
26275
26331
  async function listen(server, path) {
26276
26332
  await new Promise((resolve21, reject) => {
26277
26333
  server.once("error", reject);
@@ -26306,21 +26362,14 @@ async function prepareRoleEnvelope(options) {
26306
26362
  const calls = [];
26307
26363
  const customEntries = [];
26308
26364
  const sessionEntries = [];
26309
- const methodSkills = /* @__PURE__ */ new Map();
26310
26365
  let preferredTools = [];
26311
26366
  let rejection;
26312
26367
  let infrastructureRoundFailure;
26313
26368
  const hostAbort = new AbortController();
26314
26369
  const runId = request.runDirectory.split("/").filter(Boolean).at(-1) ?? randomUUID8();
26315
- await mkdir5(request.runDirectory, { recursive: true });
26316
- for (const method of request.methods) {
26317
- if (method.kind !== "skill") continue;
26318
- const name = basename9(dirname23(method.path));
26319
- const raw = await readFile23(method.path, "utf8");
26320
- methodSkills.set(name, { path: method.path, body: stripSkillFrontmatter(raw).trim() });
26321
- }
26322
- let sessionFile = options.sessionFile ?? join40(request.runDirectory, "session", "session.jsonl");
26323
- await mkdir5(dirname23(sessionFile), { recursive: true });
26370
+ await mkdir6(request.runDirectory, { recursive: true });
26371
+ let sessionFile = options.sessionFile ?? join41(request.runDirectory, "session", "session.jsonl");
26372
+ await mkdir6(dirname24(sessionFile), { recursive: true });
26324
26373
  if (request.continuation.kind !== "resume") {
26325
26374
  try {
26326
26375
  await writeFile12(
@@ -26351,7 +26400,7 @@ async function prepareRoleEnvelope(options) {
26351
26400
  getLeafEntry: () => sessionEntries.at(-1),
26352
26401
  getLeafId: () => runId,
26353
26402
  getEntries: () => sessionEntries,
26354
- getSessionDir: () => dirname23(sessionFile),
26403
+ getSessionDir: () => dirname24(sessionFile),
26355
26404
  getSessionFile: () => sessionFile,
26356
26405
  getHeader: () => ({ type: "session", id: runId }),
26357
26406
  setSessionFile(path) {
@@ -26386,8 +26435,9 @@ async function prepareRoleEnvelope(options) {
26386
26435
  deliverSubmissionRejection(_value) {
26387
26436
  },
26388
26437
  capabilities: {
26389
- skillExpansion(prompt) {
26390
- return buildSkillExpansion(methodSkills, prompt);
26438
+ // #922: native loaders own expansion; package does not pre-read bodies (ADR 0032).
26439
+ skillExpansion() {
26440
+ return void 0;
26391
26441
  }
26392
26442
  },
26393
26443
  registerFlag(name, definition) {
@@ -26726,10 +26776,9 @@ async function prepareRoleEnvelope(options) {
26726
26776
  message: { role: "user", content: prompt }
26727
26777
  });
26728
26778
  }
26729
- const methodPrompt = (await Promise.all(request.methods.map(({ path }) => readFile23(path, "utf8")))).join("\n\n");
26730
26779
  const promptResults = await emit("before_agent_start", {
26731
26780
  prompt,
26732
- systemPrompt: methodPrompt,
26781
+ systemPrompt: "",
26733
26782
  systemPromptOptions: {}
26734
26783
  });
26735
26784
  const systemPromptParts = promptResults.flatMap((value) => {
@@ -26737,7 +26786,7 @@ async function prepareRoleEnvelope(options) {
26737
26786
  if (!("systemPrompt" in value) || typeof value.systemPrompt !== "string") return [];
26738
26787
  return [value.systemPrompt];
26739
26788
  });
26740
- const systemPromptBody = systemPromptParts.length > 0 ? systemPromptParts.join("\n\n") : methodPrompt;
26789
+ const systemPromptBody = systemPromptParts.join("\n\n");
26741
26790
  const readingMaterials = [];
26742
26791
  for (const value of promptResults) {
26743
26792
  if (typeof value !== "object" || value === null) continue;
@@ -26750,6 +26799,13 @@ async function prepareRoleEnvelope(options) {
26750
26799
  throw new Error(`terminating tool not registered after activation: ${terminatingToolName}`);
26751
26800
  }
26752
26801
  const jsonSchema = terminatingToolJsonSchema(terminating.parameters);
26802
+ if (request.host === "codex" && hostMethodSkills(request.methods).length > 0) {
26803
+ const packageRoot = options.dependencies.packageRoot;
26804
+ if (typeof packageRoot !== "string" || packageRoot === "") {
26805
+ throw new Error("codex project Skill catalog requires packageRoot");
26806
+ }
26807
+ await installWorkspaceMethodSkills(request.cwd, packageRoot);
26808
+ }
26753
26809
  return {
26754
26810
  mcpServers: [{
26755
26811
  name: `ak-${request.activation.role}`,
@@ -26787,7 +26843,7 @@ async function prepareRoleEnvelope(options) {
26787
26843
  init_session_identity();
26788
26844
 
26789
26845
  // src/headless-host/description.ts
26790
- import { join as join41 } from "node:path";
26846
+ import { join as join42 } from "node:path";
26791
26847
  function isClaudePrintDescription(description) {
26792
26848
  return description.protocol === "claude-print";
26793
26849
  }
@@ -26795,7 +26851,7 @@ function isCodexExecDescription(description) {
26795
26851
  return description.protocol === "codex-exec";
26796
26852
  }
26797
26853
  function resolveHeadlessBinary(description, operatorHome) {
26798
- return join41(operatorHome, ...description.binaryFromHome);
26854
+ return join42(operatorHome, ...description.binaryFromHome);
26799
26855
  }
26800
26856
  function headlessTurnArgs(options) {
26801
26857
  const { description } = options;
@@ -26807,6 +26863,7 @@ function headlessTurnArgs(options) {
26807
26863
  description.jsonSchemaFlag,
26808
26864
  JSON.stringify(options.jsonSchema)
26809
26865
  ];
26866
+ if (options.pluginDir) args.push("--plugin-dir", options.pluginDir);
26810
26867
  if (options.mcpConfigPath !== void 0 && options.mcpConfigPath !== "") {
26811
26868
  args.push(description.mcpConfigFlag, options.mcpConfigPath);
26812
26869
  }
@@ -27003,7 +27060,6 @@ function codexTurnArgs(options) {
27003
27060
  args.push("resume", options.session.id);
27004
27061
  }
27005
27062
  args.push("--json");
27006
- args.push("--ignore-user-config", "--ignore-rules");
27007
27063
  const roots = (options.writableRoots ?? []).filter((root) => root !== "");
27008
27064
  if (roots.length > 0) {
27009
27065
  args.push(
@@ -27051,7 +27107,7 @@ import { spawn as spawn4, spawnSync } from "node:child_process";
27051
27107
  import { randomUUID as randomUUID9 } from "node:crypto";
27052
27108
  import { existsSync as existsSync13 } from "node:fs";
27053
27109
  import { writeFile as writeFile13 } from "node:fs/promises";
27054
- import { dirname as dirname24, isAbsolute as isAbsolute11, join as join42, resolve as resolve20 } from "node:path";
27110
+ import { dirname as dirname25, isAbsolute as isAbsolute11, join as join43, resolve as resolve20 } from "node:path";
27055
27111
 
27056
27112
  // src/external-host-turn-loop.ts
27057
27113
  init_host_contracts();
@@ -27270,8 +27326,8 @@ function formatCodexFailurePayload(payload) {
27270
27326
  function cwdIsGitWorkTree(cwd) {
27271
27327
  let dir = cwd;
27272
27328
  for (; ; ) {
27273
- if (existsSync13(join42(dir, ".git"))) return true;
27274
- const parent = dirname24(dir);
27329
+ if (existsSync13(join43(dir, ".git"))) return true;
27330
+ const parent = dirname25(dir);
27275
27331
  if (parent === dir) return false;
27276
27332
  dir = parent;
27277
27333
  }
@@ -27460,7 +27516,8 @@ function buildTurnArgs(options) {
27460
27516
  mcpConfigPath: options.mcpConfigPath,
27461
27517
  ...options.model === void 0 ? {} : { model: options.model },
27462
27518
  ...options.effort === void 0 ? {} : { effort: options.effort },
27463
- session: { kind: options.sessionKind, id: options.sessionId }
27519
+ session: { kind: options.sessionKind, id: options.sessionId },
27520
+ ...options.pluginDir === void 0 ? {} : { pluginDir: options.pluginDir }
27464
27521
  });
27465
27522
  }
27466
27523
  function createHeadlessRoleTurnHost(config) {
@@ -27477,23 +27534,36 @@ function createHeadlessRoleTurnHost(config) {
27477
27534
  await config.sessionIdentity.bind(request.principal, sessionId);
27478
27535
  }
27479
27536
  const env = { ...process.env, ...config.env ?? {} };
27480
- const systemPromptPath = join42(request.runDirectory, "headless-system-prompt.txt");
27537
+ const systemPromptPath = join43(request.runDirectory, "headless-system-prompt.txt");
27481
27538
  await writeFile13(systemPromptPath, systemPrompt, "utf8");
27482
27539
  let mcpConfigPath;
27483
27540
  let outputSchemaPath;
27541
+ let pluginDir;
27542
+ let applyMethodPrompt = codex ? (prompt) => applyCodexSkillInvocation(request.methods, prompt) : (prompt) => prompt;
27484
27543
  if (codex) {
27485
- outputSchemaPath = join42(request.runDirectory, "headless-output-schema.json");
27486
- const closed = closeJsonSchemaForCodex(prepared.jsonSchema);
27487
- await writeFile13(outputSchemaPath, `${JSON.stringify(closed, null, 2)}
27488
- `, "utf8");
27544
+ outputSchemaPath = join43(request.runDirectory, "headless-output-schema.json");
27545
+ await writeFile13(
27546
+ outputSchemaPath,
27547
+ `${JSON.stringify(closeJsonSchemaForCodex(prepared.jsonSchema), null, 2)}
27548
+ `,
27549
+ "utf8"
27550
+ );
27489
27551
  } else {
27490
- mcpConfigPath = join42(request.runDirectory, "headless-mcp-config.json");
27552
+ mcpConfigPath = join43(request.runDirectory, "headless-mcp-config.json");
27491
27553
  await writeFile13(
27492
27554
  mcpConfigPath,
27493
27555
  `${JSON.stringify(headlessMcpConfigDocument(prepared.mcpServers), null, 2)}
27494
27556
  `,
27495
27557
  "utf8"
27496
27558
  );
27559
+ if (hostMethodSkills(request.methods).length > 0) {
27560
+ const packageRoot = env.AK_PACKAGE_ROOT ?? process.env.AK_PACKAGE_ROOT;
27561
+ if (typeof packageRoot !== "string" || packageRoot === "") {
27562
+ throw new Error("claude method plugin-dir requires AK_PACKAGE_ROOT");
27563
+ }
27564
+ pluginDir = packagedMethodPluginDir(packageRoot);
27565
+ applyMethodPrompt = (prompt) => applyClaudeSkillInvocation(request.methods, prompt);
27566
+ }
27497
27567
  }
27498
27568
  const sessionParent = config.sessionIdentity.resolveSessionFile(request.principal);
27499
27569
  outcome = await driveExternalRoleTurnRounds(prepared, request, {
@@ -27518,7 +27588,8 @@ function createHeadlessRoleTurnHost(config) {
27518
27588
  sessionId,
27519
27589
  sessionKind,
27520
27590
  cwd: request.cwd,
27521
- ...gitCommonDir === void 0 ? {} : { writableRoots: [gitCommonDir] }
27591
+ ...gitCommonDir === void 0 ? {} : { writableRoots: [gitCommonDir] },
27592
+ ...pluginDir === void 0 ? {} : { pluginDir }
27522
27593
  });
27523
27594
  } catch (error) {
27524
27595
  const message = error instanceof Error ? error.message : String(error);
@@ -27535,7 +27606,7 @@ function createHeadlessRoleTurnHost(config) {
27535
27606
  args,
27536
27607
  cwd: request.cwd,
27537
27608
  env,
27538
- stdin: prompt,
27609
+ stdin: applyMethodPrompt(prompt),
27539
27610
  ...abortSignal === void 0 ? {} : { signal: abortSignal },
27540
27611
  ...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
27541
27612
  onStdoutLine(line2) {
@@ -1,5 +1,3 @@
1
- /** Grok CLI reads vendor-private compat surfaces unless each is disabled by name. */
2
- const PRIVATE_COMPAT_ENV = Object.fromEntries(["CLAUDE", "CURSOR", "CODEX"].flatMap((vendor) => ["SKILLS", "RULES", "AGENTS", "MCPS", "HOOKS", "SESSIONS"].map((kind) => [`GROK_${vendor}_${kind}_ENABLED`, "false"])));
3
1
  export const DEFAULT_ROLE_TURN_HOST = "pi";
4
2
  export const HOST_DESCRIPTIONS = Object.freeze({
5
3
  /** Operator home `~/.grok`, native session/load resume, `agent [--model X] stdio`. */
@@ -13,11 +11,6 @@ export const HOST_DESCRIPTIONS = Object.freeze({
13
11
  modelPassing: "argv",
14
12
  boundResume: "session/load",
15
13
  sessionBindingFile: "grok-acp-session.json",
16
- childEnv: Object.freeze({
17
- ...PRIVATE_COMPAT_ENV,
18
- GROK_MEMORY: "0",
19
- GROK_SUBAGENTS: "0",
20
- }),
21
14
  }),
22
15
  /**
23
16
  * Operator home `~/.hermes`, native session/load resume, `acp` subcommand.
@@ -36,7 +29,6 @@ export const HOST_DESCRIPTIONS = Object.freeze({
36
29
  modelPassing: "set_model",
37
30
  boundResume: "session/load",
38
31
  sessionBindingFile: "hermes-acp-session.json",
39
- childEnv: Object.freeze({}),
40
32
  seatProfileSoul: Object.freeze({
41
33
  flag: "-p",
42
34
  namePrefix: "ak-",
@@ -49,12 +41,9 @@ export const HOST_DESCRIPTIONS = Object.freeze({
49
41
  * Headless CLI family (#645 / #646). Claude print-mode is the first row;
50
42
  * codex exec (#646) adds another. Protocol-specific argv/parse live in
51
43
  * headless-host helpers (#752 per-host impl).
52
- * Claude fixedArgs: print mode, isolation without `--bare` (OAuth stays), full
53
- * permissions. stream-json + verbose: live host events for sitian records
54
- * (#811); result is last line. `--setting-sources` empty = load no
55
- * user/project/local CLAUDE.md/hooks/skills (role envelope is delivered via
56
- * `--system-prompt` wholesale replace). `--strict-mcp-config` with no
57
- * `--mcp-config` drops operator MCP + claude.ai connectors.
44
+ * Claude fixedArgs: print mode, full permissions. stream-json + verbose: live
45
+ * host events for sitian records (#811); result is last line. Forced methods
46
+ * ride `--plugin-dir` (#922); operator skill/setting surfaces stay open.
58
47
  */
59
48
  export const HEADLESS_HOST_DESCRIPTIONS = Object.freeze({
60
49
  "claude": Object.freeze({
@@ -67,10 +56,6 @@ export const HEADLESS_HOST_DESCRIPTIONS = Object.freeze({
67
56
  // Intermediate assistant/tool/system events require verbose with stream-json.
68
57
  "--verbose",
69
58
  "--permission-mode", "bypassPermissions",
70
- // Empty sources: no user/project/local operator surface (envelope owns materials).
71
- "--setting-sources", "",
72
- // With adapter-supplied --mcp-config only (AK relay); drops operator + claude.ai MCP.
73
- "--strict-mcp-config",
74
59
  ]),
75
60
  promptFlag: "-p",
76
61
  modelFlag: "--model",
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "ak-methods",
3
+ "version": "0.0.0",
4
+ "description": "ak-roles packaged role method skills"
5
+ }
@@ -0,0 +1,48 @@
1
+ # ak-cross-m-review
2
+
3
+ Local, pre-PR review gate: two independent lenses against a pinned diff, one verdict each; a single lens runs in the invoking session, `all` runs both as parallel sub-agent legs. `SKILL.md` plus the selected lens prompt is the complete active authority; this file is vocabulary only.
4
+
5
+ ## Language
6
+
7
+ **Fixed target**:
8
+ The pinned base-to-HEAD snapshot under review, resolved to two literal SHAs
9
+ before anything is dispatched.
10
+ _Avoid_: range, worktree diff, working changes
11
+
12
+ **Authority set**:
13
+ The ordered sources that govern the review — user decisions first, then
14
+ ratified ADRs / specs, then repository contracts.
15
+ _Avoid_: spec (alone), reference docs
16
+
17
+ **Lens**:
18
+ One review question with its own prompt file — `completeness` (was the
19
+ authority delivered?) or `correctness` (is what exists right?).
20
+ _Avoid_: axis, gate, mode, pass
21
+
22
+ **Leg**:
23
+ One independent sub-agent running exactly one lens inside an independent copy of the target at `PRE_HEAD`, dispatched only by `all`; the copy is provided by the harness when it can, otherwise created by the caller. A single-lens invocation has no leg: the invoking session applies the lens itself.
24
+ _Avoid_: panel, member, reviewer squad, vendor leg
25
+
26
+ **Candidate**:
27
+ An evidence-backed claim a leg submits for judgment; never a verdict.
28
+ _Avoid_: finding (before judgment), vote
29
+
30
+ **Judge**:
31
+ The invoking session, which verifies each candidate against the fixed target
32
+ and authority set and disposes it as live or refuted.
33
+ _Avoid_: orchestrator, runner, merger
34
+
35
+ **Verdict**:
36
+ The single terminal line a lens ends with, labelled by lens
37
+ (`CMR-VERDICT: completeness=…` / `CMR-VERDICT: correctness=…`).
38
+ _Avoid_: gate result, concur, convergence
39
+
40
+ **Preset**:
41
+ A named wrapper skill that invokes the engine with one lens and returns its
42
+ report unchanged.
43
+ _Avoid_: gate skill, entry point
44
+
45
+ **Review only**:
46
+ The outcome boundary — the invocation reports and stops; the caller owns every
47
+ repair, commit, retry, and later review.
48
+ _Avoid_: read-only (that is a filesystem property, not this boundary)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Akagi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.