@orchestrator-claude/definitions 3.36.0 → 3.37.1

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 (69) hide show
  1. package/agents/adversarial-reviewer.md +6 -1
  2. package/agents/api-extractor.md +6 -0
  3. package/agents/azure-devops-engineer.md +1 -2
  4. package/agents/business-rule-miner.md +6 -0
  5. package/agents/cicd-platform-engineer.md +1 -2
  6. package/agents/ciclo-engineer.md +15 -2
  7. package/agents/code-archaeologist.md +6 -0
  8. package/agents/debug-sidecar.md +6 -0
  9. package/agents/doc-sidecar.md +6 -0
  10. package/agents/docs-guardian.md +9 -3
  11. package/agents/frontend-engineer.md +1 -2
  12. package/agents/{ai-architect.md → harness-architect.md} +26 -5
  13. package/agents/implementer.md +6 -1
  14. package/agents/legacy-discoverer.md +6 -0
  15. package/agents/legacy-synthesizer.md +6 -0
  16. package/agents/methodology-researcher.md +6 -1
  17. package/agents/operational-artifact-builder.md +6 -1
  18. package/agents/orchestrator.md +27 -87
  19. package/agents/planner.md +6 -1
  20. package/agents/presentation-engineer.md +5 -4
  21. package/agents/python-data-engineer.md +0 -1
  22. package/agents/researcher.md +6 -0
  23. package/agents/reviewer.md +6 -1
  24. package/agents/schema-extractor.md +6 -0
  25. package/agents/software-architect.md +30 -7
  26. package/agents/specifier.md +6 -1
  27. package/agents/task-generator.md +6 -1
  28. package/agents/test-sidecar.md +6 -0
  29. package/dist/scripts/generate-hooks-matcher.d.ts +106 -0
  30. package/dist/scripts/generate-hooks-matcher.d.ts.map +1 -0
  31. package/dist/scripts/generate-hooks-matcher.js +153 -0
  32. package/dist/scripts/generate-hooks-matcher.js.map +1 -0
  33. package/dist/scripts/generate-manifest.d.ts +22 -0
  34. package/dist/scripts/generate-manifest.d.ts.map +1 -1
  35. package/dist/scripts/generate-manifest.js +67 -2
  36. package/dist/scripts/generate-manifest.js.map +1 -1
  37. package/hooks/agent-git-guard.mjs +63 -11
  38. package/hooks/dangling-guard.mjs +42 -12
  39. package/hooks/gate-guardian.mjs +38 -8
  40. package/hooks/hooks.json +3 -9
  41. package/hooks/lib/api-client.ts +128 -9
  42. package/hooks/lib/capability-scanner.ts +391 -55
  43. package/hooks/lib/dispatch-marker.ts +214 -0
  44. package/hooks/lib/git-checkpoint.ts +34 -37
  45. package/hooks/lib/observations-helper.ts +14 -1
  46. package/hooks/mailbox-listener.mjs +69 -17
  47. package/hooks/playwright-output-guard.mjs +40 -10
  48. package/hooks/post-implement.mjs +38 -8
  49. package/hooks/pre-agent-dispatch.mjs +137 -16
  50. package/hooks/pre-agent-dispatch.ts +82 -15
  51. package/hooks/session-start.mjs +274 -103
  52. package/hooks/session-start.ts +250 -69
  53. package/hooks/subagent-start.mjs +7665 -22
  54. package/hooks/subagent-start.ts +367 -16
  55. package/hooks/subagent-stop.mjs +102 -62
  56. package/hooks/subagent-stop.ts +124 -28
  57. package/hooks/user-prompt.mjs +43 -13
  58. package/hooks/verification-before-completion.mjs +63 -11
  59. package/hooks/workflow-guard.mjs +45 -11
  60. package/hooks-settings-template.json +1 -7
  61. package/manifest.json +62 -62
  62. package/package.json +4 -2
  63. package/skills/checkpoint-protocol/SKILL.md +19 -4
  64. package/skills/ciclo-lane-dispatch/SKILL.md +24 -0
  65. package/skills/fase-1b-projecao/SKILL.md +146 -0
  66. package/skills/orchestrator/SKILL.md +11 -3
  67. package/skills/workflow-coordination/SKILL.md +3 -3
  68. package/hooks/post-compact.mjs +0 -7931
  69. package/hooks/post-compact.ts +0 -170
@@ -7324,38 +7324,68 @@ var require_dist = __commonJS({
7324
7324
  var import_yaml2 = __toESM(require_dist(), 1);
7325
7325
  import { readFileSync as readFileSync4, readdirSync as readdirSync3, existsSync as existsSync4 } from "node:fs";
7326
7326
  import { join as join5, dirname as dirname3 } from "node:path";
7327
- import { fileURLToPath as fileURLToPath2 } from "node:url";
7327
+ import { fileURLToPath } from "node:url";
7328
7328
 
7329
7329
  // hooks/lib/api-client.ts
7330
7330
  import { readFileSync, writeFileSync, mkdirSync, statSync, unlinkSync, readdirSync } from "node:fs";
7331
- import { join as join2, dirname as dirname2 } from "node:path";
7332
- import { fileURLToPath } from "node:url";
7331
+ import { join as join2 } from "node:path";
7332
+ import { tmpdir } from "node:os";
7333
+ import { createHash } from "node:crypto";
7333
7334
 
7334
7335
  // ../project-root-resolver/dist/index.js
7335
7336
  import { existsSync } from "node:fs";
7336
7337
  import { dirname, join } from "node:path";
7338
+ function hasProjectRootMarker(candidate, sentinel) {
7339
+ return existsSync(join(candidate, ".git")) || existsSync(join(candidate, "package.json")) || existsSync(join(candidate, sentinel, ".root"));
7340
+ }
7337
7341
  function resolveProjectRoot(cwd, sentinel = ".orchestrator") {
7342
+ const candidates = [];
7338
7343
  let currentDir = cwd;
7339
7344
  for (; ; ) {
7340
7345
  if (existsSync(join(currentDir, sentinel))) {
7341
- return currentDir;
7346
+ candidates.push(currentDir);
7342
7347
  }
7343
7348
  const parentDir = dirname(currentDir);
7344
7349
  if (parentDir === currentDir) {
7345
- return null;
7350
+ break;
7346
7351
  }
7347
7352
  currentDir = parentDir;
7348
7353
  }
7354
+ const [innermost] = candidates;
7355
+ if (innermost === void 0) {
7356
+ return null;
7357
+ }
7358
+ for (const candidate of candidates) {
7359
+ if (hasProjectRootMarker(candidate, sentinel)) {
7360
+ return candidate;
7361
+ }
7362
+ }
7363
+ return innermost;
7349
7364
  }
7350
7365
 
7351
7366
  // hooks/lib/api-client.ts
7352
- var __filename = fileURLToPath(import.meta.url);
7353
- var __dirname = dirname2(__filename);
7354
- var PROJECT_ROOT = join2(__dirname, "..", "..", "..");
7355
- var STATE_DIR = join2(PROJECT_ROOT, ".orchestrator", ".state");
7367
+ function resolveFallbackStateDir(cwd) {
7368
+ const cwdHash = createHash("sha256").update(cwd).digest("hex").slice(0, 16);
7369
+ return join2(tmpdir(), "orchestrator-hook-fallback", cwdHash);
7370
+ }
7371
+ var PROJECT_ROOT = (() => {
7372
+ const resolved = resolveHookProjectRoot(process.cwd());
7373
+ if (resolved !== null && hasProjectRootMarker(resolved, ".orchestrator")) {
7374
+ return resolved;
7375
+ }
7376
+ return null;
7377
+ })();
7378
+ var STATE_DIR_OVERRIDE = process.env.ORCHESTRATOR_STATE_DIR;
7379
+ var STATE_DIR = STATE_DIR_OVERRIDE !== void 0 && STATE_DIR_OVERRIDE !== "" ? STATE_DIR_OVERRIDE : PROJECT_ROOT !== null ? join2(PROJECT_ROOT, ".orchestrator", ".state") : resolveFallbackStateDir(process.cwd());
7356
7380
  var JWT_CACHE = join2(STATE_DIR, "jwt-token");
7357
7381
  var DEBUG_LOG = join2(STATE_DIR, "hook-debug.log");
7358
7382
  mkdirSync(STATE_DIR, { recursive: true });
7383
+ if ((STATE_DIR_OVERRIDE === void 0 || STATE_DIR_OVERRIDE === "") && PROJECT_ROOT === null) {
7384
+ log(
7385
+ "API-CLIENT",
7386
+ `PROJECT_ROOT: no marker-verified ".orchestrator" project root found above ${process.cwd()} \u2014 falling back to isolated state dir ${STATE_DIR} (RF-003)`
7387
+ );
7388
+ }
7359
7389
  var API_URL = process.env.ORCHESTRATOR_API_URL || "http://localhost:3001";
7360
7390
  var AUTH_EMAIL = process.env.ORCHESTRATOR_ADMIN_EMAIL || process.env.ORCHESTRATOR_AUTH_EMAIL || "system@orchestrator.local";
7361
7391
  var AUTH_PASSWORD = process.env.ORCHESTRATOR_ADMIN_PASSWORD || process.env.ORCHESTRATOR_AUTH_PASSWORD || "system123";
@@ -7533,6 +7563,19 @@ async function allocateSessionNumber(projectId) {
7533
7563
  projectId
7534
7564
  });
7535
7565
  }
7566
+ async function readStdin() {
7567
+ const chunks = [];
7568
+ for await (const chunk of process.stdin) {
7569
+ chunks.push(chunk);
7570
+ }
7571
+ const raw = Buffer.concat(chunks).toString("utf-8").trim();
7572
+ if (!raw) return {};
7573
+ try {
7574
+ return JSON.parse(raw);
7575
+ } catch {
7576
+ return {};
7577
+ }
7578
+ }
7536
7579
  function outputContext(hookEvent, context) {
7537
7580
  console.log(
7538
7581
  JSON.stringify({
@@ -7546,8 +7589,8 @@ function outputContext(hookEvent, context) {
7546
7589
 
7547
7590
  // hooks/lib/capability-scanner.ts
7548
7591
  var import_yaml = __toESM(require_dist(), 1);
7549
- import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
7550
- import { join as join3 } from "node:path";
7592
+ import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync2, statSync as statSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
7593
+ import { join as join3, dirname as dirname2 } from "node:path";
7551
7594
  function pluginNameFromKey(key) {
7552
7595
  return key.split("@")[0] ?? "";
7553
7596
  }
@@ -7728,13 +7771,14 @@ function buildCapabilityIndex(opts) {
7728
7771
  gaps
7729
7772
  };
7730
7773
  }
7731
- function writeCapabilitiesToVault(vaultConfigPath, index) {
7774
+ var CAPABILITY_CACHE_FILENAME = "capability-cache.yaml";
7775
+ function resolveCapabilityCachePath(projectRoot, env = process.env) {
7776
+ const override = env.ORCHESTRATOR_STATE_DIR;
7777
+ const stateDir = override !== void 0 && override !== "" ? override : join3(projectRoot, ".orchestrator", ".state");
7778
+ return join3(stateDir, CAPABILITY_CACHE_FILENAME);
7779
+ }
7780
+ function writeCapabilityCache(cachePath, index) {
7732
7781
  try {
7733
- let config = {};
7734
- if (existsSync2(vaultConfigPath)) {
7735
- const raw = readFileSync2(vaultConfigPath, "utf-8");
7736
- config = (0, import_yaml.parse)(raw) ?? {};
7737
- }
7738
7782
  const pluginEntries = index.plugins.map((p) => {
7739
7783
  const entry = { name: p.name };
7740
7784
  if (p.version !== "unknown") entry.version = p.version;
@@ -7745,25 +7789,27 @@ function writeCapabilitiesToVault(vaultConfigPath, index) {
7745
7789
  if (p.hasHooks) entry.hooks = true;
7746
7790
  return entry;
7747
7791
  });
7748
- config.capabilities = {
7792
+ const payload = {
7793
+ schema: "capability-cache/v1",
7749
7794
  last_scanned: index.lastScanned,
7750
7795
  installed_plugins_mtime: index.installedPluginsMtime,
7751
7796
  plugins: pluginEntries,
7752
7797
  routing: index.routing,
7753
7798
  gaps: index.gaps
7754
7799
  };
7755
- writeFileSync2(vaultConfigPath, (0, import_yaml.stringify)(config, { lineWidth: 120 }));
7800
+ mkdirSync2(dirname2(cachePath), { recursive: true });
7801
+ writeFileSync2(cachePath, (0, import_yaml.stringify)(payload, { lineWidth: 120 }));
7756
7802
  } catch {
7757
7803
  }
7758
7804
  }
7759
- function readCapabilitiesFromVault(vaultConfigPath) {
7805
+ function readCapabilityCache(cachePath, vaultConfigPath) {
7760
7806
  try {
7761
- if (!existsSync2(vaultConfigPath)) return null;
7762
- const raw = readFileSync2(vaultConfigPath, "utf-8");
7763
- const config = (0, import_yaml.parse)(raw);
7764
- const caps = config?.capabilities;
7765
- if (!caps) return null;
7766
- const rawPlugins = Array.isArray(caps.plugins) ? caps.plugins : [];
7807
+ if (!existsSync2(cachePath)) return null;
7808
+ const raw = readFileSync2(cachePath, "utf-8");
7809
+ const cache = (0, import_yaml.parse)(raw);
7810
+ if (!cache || typeof cache !== "object") return null;
7811
+ if (cache.last_scanned === void 0 && cache.plugins === void 0) return null;
7812
+ const rawPlugins = Array.isArray(cache.plugins) ? cache.plugins : [];
7767
7813
  const plugins = rawPlugins.map((p) => ({
7768
7814
  name: p.name ?? "",
7769
7815
  version: p.version ?? "unknown",
@@ -7774,14 +7820,14 @@ function readCapabilitiesFromVault(vaultConfigPath) {
7774
7820
  hasHooks: p.hooks ?? false
7775
7821
  }));
7776
7822
  return {
7777
- lastScanned: caps.last_scanned ?? "",
7778
- installedPluginsMtime: caps.installed_plugins_mtime ?? "",
7823
+ lastScanned: cache.last_scanned ?? "",
7824
+ installedPluginsMtime: cache.installed_plugins_mtime ?? "",
7779
7825
  plugins,
7780
7826
  agents: [],
7781
7827
  // agents are always scanned fresh (fast, <10ms)
7782
7828
  vault: readVaultSummary(vaultConfigPath),
7783
- routing: caps.routing ?? {},
7784
- gaps: caps.gaps ?? []
7829
+ routing: cache.routing ?? {},
7830
+ gaps: cache.gaps ?? []
7785
7831
  };
7786
7832
  } catch {
7787
7833
  return null;
@@ -7809,32 +7855,94 @@ function formatCapabilityMatrix(index) {
7809
7855
  `gaps: ${gapsLine}`
7810
7856
  ].join("\n");
7811
7857
  }
7812
- function formatMemoryContext(entries) {
7813
- if (entries.length === 0) return "";
7814
- const byType = {};
7815
- for (const e of entries) {
7816
- byType[e.type] = (byType[e.type] ?? 0) + 1;
7858
+ var PER_ENTRY_SUMMARY_CHAR_CAP = 300;
7859
+ var AGGREGATE_CHAR_BUDGET = 8e3;
7860
+ var SUFFIX_RESERVE = 64;
7861
+ var MEMORY_TYPES = ["feedback", "project", "reference", "user"];
7862
+ function truncateSummary(summary, cap) {
7863
+ return summary.length > cap ? `${summary.slice(0, cap)}\u2026` : summary;
7864
+ }
7865
+ function countByType(list) {
7866
+ const counts = {};
7867
+ for (const e of list) {
7868
+ counts[e.type] = (counts[e.type] ?? 0) + 1;
7817
7869
  }
7818
- const typeSummary = Object.entries(byType).map(([t, n]) => `${n} ${t}`).join(", ");
7819
- const recentNames = entries.slice(0, 5).map((e) => e.file.replace(/\.md$/, "").replace(/^(feedback|project|reference|user)_/, "")).join(", ");
7820
- return [
7821
- `[MEMORY-CONTEXT] ${entries.length} entries: ${typeSummary}`,
7822
- `recent: ${recentNames}`
7823
- ].join("\n");
7870
+ return counts;
7824
7871
  }
7825
- function readMemoryIndex(indexPath) {
7872
+ function formatMemoryContext(selected, allEntries) {
7873
+ if (selected.length === 0) return "";
7874
+ const selectedCounts = countByType(selected);
7875
+ const totalCounts = countByType(allEntries);
7876
+ const perTypeSummary = MEMORY_TYPES.map(
7877
+ (t) => `${t}: ${selectedCounts[t] ?? 0}/${totalCounts[t] ?? 0}`
7878
+ ).join(", ");
7879
+ const headerBase = `[MEMORY-CONTEXT] ${selected.length} de ${allEntries.length} mem\xF3rias (${perTypeSummary}); crit\xE9rio: cota m\xEDnima de 2 por tipo n\xE3o-feedback + prioridade feedback\u2192rec\xEAncia no restante`;
7880
+ const availableForLines = AGGREGATE_CHAR_BUDGET - headerBase.length - SUFFIX_RESERVE;
7881
+ const lines = [];
7882
+ let used = 0;
7883
+ let droppedByBudget = 0;
7884
+ for (let i = 0; i < selected.length; i++) {
7885
+ const entry = selected[i];
7886
+ const name = entry.file.replace(/\.md$/, "");
7887
+ const truncated = truncateSummary(entry.summary, PER_ENTRY_SUMMARY_CHAR_CAP);
7888
+ const line = `- [${entry.type}] ${name}: ${truncated}`;
7889
+ if (used + line.length + 1 > availableForLines) {
7890
+ droppedByBudget = selected.length - i;
7891
+ break;
7892
+ }
7893
+ lines.push(line);
7894
+ used += line.length + 1;
7895
+ }
7896
+ const header = droppedByBudget > 0 ? `${headerBase}; +${droppedByBudget} truncadas por or\xE7amento de caracteres` : headerBase;
7897
+ return [header, ...lines].join("\n");
7898
+ }
7899
+ function isWellFormedMemoryEntry(value) {
7900
+ return typeof value === "object" && value !== null && typeof value.file === "string";
7901
+ }
7902
+ function readMemoryIndex(indexPath, diag) {
7826
7903
  try {
7827
7904
  if (!existsSync2(indexPath)) return null;
7828
7905
  const raw = readFileSync2(indexPath, "utf-8");
7829
- const parsed = (0, import_yaml.parse)(raw);
7830
- return parsed?.entries ?? null;
7831
- } catch {
7906
+ let parsed;
7907
+ try {
7908
+ parsed = (0, import_yaml.parse)(raw);
7909
+ } catch (err) {
7910
+ if (diag) diag.reason = `YAML inv\xE1lido em ${indexPath}: ${String(err).split("\n")[0]}`;
7911
+ return null;
7912
+ }
7913
+ let candidate = null;
7914
+ if (Array.isArray(parsed)) {
7915
+ candidate = parsed;
7916
+ } else if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.entries)) {
7917
+ candidate = parsed.entries;
7918
+ }
7919
+ if (candidate === null) {
7920
+ const chaves = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? Object.keys(parsed).join(", ") : typeof parsed;
7921
+ if (diag) {
7922
+ diag.reason = `formato n\xE3o reconhecido em ${indexPath} \u2014 topo tem [${chaves}]; esperado uma lista no topo ou a chave 'entries:'`;
7923
+ }
7924
+ return null;
7925
+ }
7926
+ const bemFormadas = candidate.filter(isWellFormedMemoryEntry);
7927
+ if (diag && candidate.length > 0) {
7928
+ const descartadas = candidate.length - bemFormadas.length;
7929
+ if (bemFormadas.length === 0) {
7930
+ diag.reason = `${candidate.length} entrada(s) lidas e NENHUMA bem-formada em ${indexPath} \u2014 toda entrada precisa da chave 'file:'`;
7931
+ } else if (descartadas > 0) {
7932
+ diag.reason = `${descartadas} de ${candidate.length} entrada(s) descartadas em ${indexPath} (falta a chave 'file:')`;
7933
+ }
7934
+ }
7935
+ return bemFormadas;
7936
+ } catch (err) {
7937
+ if (diag) diag.reason = `falha ao ler ${indexPath}: ${String(err).split("\n")[0]}`;
7832
7938
  return null;
7833
7939
  }
7834
7940
  }
7835
7941
  function filterRelevantMemories(entries, contextTags = []) {
7836
7942
  const now = Date.now();
7837
7943
  const fourteenDays = 14 * 24 * 60 * 60 * 1e3;
7944
+ const MAX_TOTAL = 20;
7945
+ const RESERVED_SLOTS_PER_TYPE = 2;
7838
7946
  const relevant = entries.filter((e) => {
7839
7947
  if (e.type === "feedback") return true;
7840
7948
  const accessed = new Date(e.last_accessed).getTime();
@@ -7848,17 +7956,32 @@ function filterRelevantMemories(entries, contextTags = []) {
7848
7956
  reference: 2,
7849
7957
  user: 3
7850
7958
  };
7851
- relevant.sort((a, b) => {
7959
+ const byPriorityThenRecency = (a, b) => {
7852
7960
  const pa = typePriority[a.type] ?? 9;
7853
7961
  const pb = typePriority[b.type] ?? 9;
7854
7962
  if (pa !== pb) return pa - pb;
7855
7963
  return b.last_accessed.localeCompare(a.last_accessed);
7856
- });
7857
- return relevant.slice(0, 20);
7964
+ };
7965
+ relevant.sort(byPriorityThenRecency);
7966
+ const reserved = /* @__PURE__ */ new Set();
7967
+ const nonFeedbackTypes = new Set(entries.filter((e) => e.type !== "feedback").map((e) => e.type));
7968
+ for (const type of nonFeedbackTypes) {
7969
+ const ofType = entries.filter((e) => e.type === type).sort((a, b) => b.last_accessed.localeCompare(a.last_accessed));
7970
+ for (const entry of ofType.slice(0, RESERVED_SLOTS_PER_TYPE)) {
7971
+ reserved.add(entry);
7972
+ }
7973
+ }
7974
+ const selected = new Set(reserved);
7975
+ for (const entry of relevant) {
7976
+ if (selected.size >= MAX_TOTAL) break;
7977
+ if (reserved.has(entry)) continue;
7978
+ selected.add(entry);
7979
+ }
7980
+ return [...selected].sort(byPriorityThenRecency);
7858
7981
  }
7859
7982
 
7860
7983
  // hooks/lib/session-selection.ts
7861
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "node:fs";
7984
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
7862
7985
  import { join as join4 } from "node:path";
7863
7986
  function selectionFilePath(stateDir, sessionId) {
7864
7987
  const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -7879,8 +8002,8 @@ function readSessionSelection(stateDir, sessionId) {
7879
8002
 
7880
8003
  // hooks/session-start.ts
7881
8004
  var HOOK = "SESSION-START";
7882
- var __filename2 = fileURLToPath2(import.meta.url);
7883
- var __dirname2 = dirname3(__filename2);
8005
+ var __filename = fileURLToPath(import.meta.url);
8006
+ var __dirname = dirname3(__filename);
7884
8007
  var PROJECT_ROOT2 = resolveHookProjectRoot(process.cwd());
7885
8008
  var PROJECT_ID2 = process.env.ORCHESTRATOR_PROJECT_ID ?? "";
7886
8009
  var HOME = process.env.HOME ?? process.env.USERPROFILE ?? "/home/user";
@@ -7889,18 +8012,35 @@ var PROJECT_SELECTION_ENABLED = process.env.ORCHESTRATOR_ENABLE_PROJECT_SELECTIO
7889
8012
  var SESSION_SELECTION_STATE_DIR = join5(HOME, ".orchestrator", "session-selection");
7890
8013
  var AGENTS_DIR = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, ".claude", "agents") : null;
7891
8014
  var VAULT_CONFIG_PATH = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, "project-guidelines", ".vault-config.yaml") : null;
8015
+ var CAPABILITY_CACHE_PATH = PROJECT_ROOT2 ? resolveCapabilityCachePath(PROJECT_ROOT2) : null;
7892
8016
  var PROFILES_PATH = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, ".claude", "plugin-profiles.yaml") : null;
7893
8017
  var MEMORY_INDEX_PATH = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, ".claude", "agent-memory", "orchestrator", "memory-index.yaml") : null;
7894
- function readSessionId() {
8018
+ async function readSessionStartPayload() {
7895
8019
  try {
7896
- const raw = readFileSync4("/dev/stdin", "utf-8").trim();
7897
- if (!raw) return null;
7898
- const payload = JSON.parse(raw);
7899
- return payload.session_id ?? null;
7900
- } catch {
7901
- return null;
8020
+ const payload = await readStdin();
8021
+ log(HOOK, `stdin keys: [${Object.keys(payload).join(", ")}]`);
8022
+ if (!payload.session_id && !payload.source) {
8023
+ log(
8024
+ HOOK,
8025
+ `[WARN] stdin payload has neither session_id nor source \u2014 read likely empty or unparseable (readStdin() collapses both cases to {}); raw keys=[${Object.keys(payload).join(", ")}]`
8026
+ );
8027
+ }
8028
+ return {
8029
+ sessionId: payload.session_id ?? null,
8030
+ source: payload.source ?? null
8031
+ };
8032
+ } catch (err) {
8033
+ log(HOOK, `[WARN] readStdin() itself threw (not just empty/malformed stdin) \u2014 ${String(err)}`);
8034
+ return { sessionId: null, source: null };
7902
8035
  }
7903
8036
  }
8037
+ function isRecoverySource(source) {
8038
+ return source === "compact";
8039
+ }
8040
+ var RECOVERY_DIRECTIVE = [
8041
+ "[ORCHESTRATOR] Context was compacted mid-session. This is NOT a session start.",
8042
+ "Do NOT greet. Do NOT call AskUserQuestion. Resume the in-flight task."
8043
+ ];
7904
8044
  var PROJECT_STATE_PATH = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, "project-guidelines", "project-state.yaml") : null;
7905
8045
  function synthesizeStatus(pending) {
7906
8046
  const { critical, high, medium, ci_gating: ciGating } = pending;
@@ -8057,42 +8197,61 @@ async function buildProjectSelectionPrompt() {
8057
8197
  return "";
8058
8198
  }
8059
8199
  }
8200
+ function buildMatrixLine(vaultConfigPath, agentsDir, profilesPath, cachePath) {
8201
+ let index = readCapabilityCache(cachePath, vaultConfigPath);
8202
+ const cachedMtime = index?.installedPluginsMtime ?? "";
8203
+ if (!index || needsRescan(INSTALLED_PLUGINS_PATH, cachedMtime)) {
8204
+ log(HOOK, "Capability rescan triggered");
8205
+ index = buildCapabilityIndex({
8206
+ installedPluginsPath: INSTALLED_PLUGINS_PATH,
8207
+ agentsDir,
8208
+ vaultConfigPath,
8209
+ profilesPath
8210
+ });
8211
+ writeCapabilityCache(cachePath, index);
8212
+ } else {
8213
+ index.agents = scanAgentDefinitions(agentsDir);
8214
+ log(HOOK, "Using cached capability index");
8215
+ }
8216
+ return formatCapabilityMatrix(index);
8217
+ }
8218
+ function buildMemoryLine(memoryIndexPath) {
8219
+ const diag = {};
8220
+ const memoryEntries = readMemoryIndex(memoryIndexPath, diag);
8221
+ if (diag.reason) log(HOOK, `Memory index: ${diag.reason}`);
8222
+ if (diag.reason && (!memoryEntries || memoryEntries.length === 0)) {
8223
+ return `[MEMORY-CONTEXT] INDISPON\xCDVEL \u2014 nenhuma mem\xF3ria injetada. ${diag.reason}`;
8224
+ }
8225
+ if (!memoryEntries) return "";
8226
+ const filtered = filterRelevantMemories(memoryEntries);
8227
+ return formatMemoryContext(filtered, memoryEntries);
8228
+ }
8060
8229
  function buildCapabilityMatrixContext() {
8230
+ if (VAULT_CONFIG_PATH === null || AGENTS_DIR === null || PROFILES_PATH === null || MEMORY_INDEX_PATH === null || CAPABILITY_CACHE_PATH === null) {
8231
+ log(
8232
+ HOOK,
8233
+ "buildCapabilityMatrixContext: PROJECT_ROOT unresolved (unregistered cwd) \u2014 skipping"
8234
+ );
8235
+ return "";
8236
+ }
8237
+ let matrixLine = "";
8061
8238
  try {
8062
- if (VAULT_CONFIG_PATH === null || AGENTS_DIR === null || PROFILES_PATH === null || MEMORY_INDEX_PATH === null) {
8063
- log(
8064
- HOOK,
8065
- "buildCapabilityMatrixContext: PROJECT_ROOT unresolved (unregistered cwd) \u2014 skipping"
8066
- );
8067
- return "";
8068
- }
8069
- let index = readCapabilitiesFromVault(VAULT_CONFIG_PATH);
8070
- const cachedMtime = index?.installedPluginsMtime ?? "";
8071
- if (!index || needsRescan(INSTALLED_PLUGINS_PATH, cachedMtime)) {
8072
- log(HOOK, "Capability rescan triggered");
8073
- index = buildCapabilityIndex({
8074
- installedPluginsPath: INSTALLED_PLUGINS_PATH,
8075
- agentsDir: AGENTS_DIR,
8076
- vaultConfigPath: VAULT_CONFIG_PATH,
8077
- profilesPath: PROFILES_PATH
8078
- });
8079
- writeCapabilitiesToVault(VAULT_CONFIG_PATH, index);
8080
- } else {
8081
- index.agents = scanAgentDefinitions(AGENTS_DIR);
8082
- log(HOOK, "Using cached capability index");
8083
- }
8084
- const matrixLine = formatCapabilityMatrix(index);
8085
- const memoryEntries = readMemoryIndex(MEMORY_INDEX_PATH);
8086
- let memoryLine = "";
8087
- if (memoryEntries) {
8088
- const filtered = filterRelevantMemories(memoryEntries);
8089
- memoryLine = formatMemoryContext(filtered);
8090
- }
8091
- return [matrixLine, memoryLine].filter(Boolean).join("\n");
8239
+ matrixLine = buildMatrixLine(
8240
+ VAULT_CONFIG_PATH,
8241
+ AGENTS_DIR,
8242
+ PROFILES_PATH,
8243
+ CAPABILITY_CACHE_PATH
8244
+ );
8092
8245
  } catch (err) {
8093
8246
  log(HOOK, `Capability matrix failed: ${err}`);
8094
- return "";
8095
8247
  }
8248
+ let memoryLine = "";
8249
+ try {
8250
+ memoryLine = buildMemoryLine(MEMORY_INDEX_PATH);
8251
+ } catch (err) {
8252
+ log(HOOK, `Memory context failed: ${err}`);
8253
+ }
8254
+ return [matrixLine, memoryLine].filter(Boolean).join("\n");
8096
8255
  }
8097
8256
  function checkSprintInbox() {
8098
8257
  const sprintId = process.env.SPRINT_ID;
@@ -8140,9 +8299,11 @@ async function main() {
8140
8299
  "PROJECT_ROOT unresolved: cwd has no .orchestrator sentinel up to the filesystem root \u2014 vault-dependent sections (tier, capability matrix, BACKLOG fallback) will be skipped this session (RFC-040 C5, AC-C5-2)."
8141
8300
  );
8142
8301
  }
8143
- const sessionId = readSessionId();
8302
+ const { sessionId, source } = await readSessionStartPayload();
8303
+ const isRecovery = isRecoverySource(source);
8144
8304
  const sessionSegment = sessionId ? ` | session=${sessionId}` : "";
8145
8305
  if (sessionId) log(HOOK, `Claude session: ${sessionId}`);
8306
+ log(HOOK, `source=${source ?? "unknown"} mode=${isRecovery ? "recovery" : "boot"}`);
8146
8307
  const backlog = getBacklogSummary();
8147
8308
  const tierInfo = await checkTier();
8148
8309
  const tierSegment = formatTierSegment(tierInfo);
@@ -8157,21 +8318,24 @@ async function main() {
8157
8318
  });
8158
8319
  const projectBindingSegment = formatProjectBindingSegment(projectBinding);
8159
8320
  setEffectiveProjectId(projectBinding.projectId);
8160
- const projectSelectionPrompt = PROJECT_SELECTION_ENABLED && projectBinding.source === "none" ? await buildProjectSelectionPrompt() : "";
8321
+ const projectSelectionPrompt = PROJECT_SELECTION_ENABLED && !isRecovery && projectBinding.source === "none" ? await buildProjectSelectionPrompt() : "";
8161
8322
  const sessionAllocation = await allocateSessionNumber(projectBinding.projectId);
8162
8323
  const sessionNumberSegment = formatSessionNumberSegment(sessionAllocation);
8163
8324
  const workflow = await getActiveWorkflow();
8164
8325
  if (!workflow) {
8165
- log(HOOK, "No active workflow, injecting greeting data");
8326
+ log(HOOK, `No active workflow, injecting ${isRecovery ? "recovery" : "greeting"} data`);
8327
+ const stateLine2 = `version=${backlog.version}${sessionSegment} | workflow=none${tierSegment}${sessionNumberSegment}${projectBindingSegment} | next=${backlog.next}`;
8166
8328
  outputContext(
8167
8329
  "SessionStart",
8168
8330
  [
8169
- `[ORCHESTRATOR-DATA] version=${backlog.version}${sessionSegment} | workflow=none${tierSegment}${sessionNumberSegment}${projectBindingSegment} | next=${backlog.next}`,
8170
- backlog.lastSession ? `[LAST-SESSION] ${backlog.lastSession}` : "",
8171
- backlog.status ? `[PENDENTE] ${backlog.status}` : "",
8172
- `[ORCHESTRATOR] Greet the user via AskUserQuestion using the data above (CLAUDE.md Rule #1).`,
8173
- "Do NOT invoke /orchestrator skill for the greeting \u2014 use AskUserQuestion directly.",
8174
- "Invoke /orchestrator skill only when the user chooses to start a workflow.",
8331
+ `${isRecovery ? "[ORCHESTRATOR-RECOVERY]" : "[ORCHESTRATOR-DATA]"} ${stateLine2}`,
8332
+ ...isRecovery ? RECOVERY_DIRECTIVE : [
8333
+ backlog.lastSession ? `[LAST-SESSION] ${backlog.lastSession}` : "",
8334
+ backlog.status ? `[PENDENTE] ${backlog.status}` : "",
8335
+ `[ORCHESTRATOR] Greet the user via AskUserQuestion using the data above (orchestrator identity, MUST rule #1).`,
8336
+ "Do NOT invoke /orchestrator skill for the greeting \u2014 use AskUserQuestion directly.",
8337
+ "Invoke /orchestrator skill only when the user chooses to start a workflow."
8338
+ ],
8175
8339
  capabilityContext,
8176
8340
  sprintInboxContext,
8177
8341
  projectSelectionPrompt
@@ -8181,14 +8345,20 @@ async function main() {
8181
8345
  }
8182
8346
  const action = await getNextAction(workflow.id);
8183
8347
  const actionHint = action?.hasAction && action.pendingAction ? ` | pending=${action.pendingAction.agent} (${action.pendingAction.status})` : "";
8184
- log(HOOK, `Injecting workflow context (wf=${workflow.id} phase=${workflow.currentPhase})`);
8348
+ log(
8349
+ HOOK,
8350
+ `Injecting workflow ${isRecovery ? "recovery" : "context"} (wf=${workflow.id} phase=${workflow.currentPhase})`
8351
+ );
8352
+ const stateLine = `version=${backlog.version}${sessionSegment} | workflow=${workflow.id} | type=${workflow.type} | phase=${workflow.currentPhase} | status=${workflow.status}${actionHint}${tierSegment}${sessionNumberSegment}${projectBindingSegment}`;
8185
8353
  outputContext(
8186
8354
  "SessionStart",
8187
8355
  [
8188
- `[ORCHESTRATOR-DATA] version=${backlog.version}${sessionSegment} | workflow=${workflow.id} | type=${workflow.type} | phase=${workflow.currentPhase} | status=${workflow.status}${actionHint}${tierSegment}${sessionNumberSegment}${projectBindingSegment}`,
8189
- `[ORCHESTRATOR] Active workflow detected. Greet the user via AskUserQuestion showing workflow state (CLAUDE.md Rule #1).`,
8190
- "Do NOT invoke /orchestrator skill for the greeting \u2014 use AskUserQuestion directly.",
8191
- "Invoke /orchestrator skill when the user chooses to continue the workflow.",
8356
+ `${isRecovery ? "[ORCHESTRATOR-RECOVERY]" : "[ORCHESTRATOR-DATA]"} ${stateLine}`,
8357
+ ...isRecovery ? RECOVERY_DIRECTIVE : [
8358
+ `[ORCHESTRATOR] Active workflow detected. Greet the user via AskUserQuestion showing workflow state (orchestrator identity, MUST rule #1).`,
8359
+ "Do NOT invoke /orchestrator skill for the greeting \u2014 use AskUserQuestion directly.",
8360
+ "Invoke /orchestrator skill when the user chooses to continue the workflow."
8361
+ ],
8192
8362
  capabilityContext,
8193
8363
  projectSelectionPrompt
8194
8364
  ].filter(Boolean).join("\n")
@@ -8196,6 +8366,7 @@ async function main() {
8196
8366
  }
8197
8367
  main().catch(() => process.exit(0));
8198
8368
  export {
8369
+ buildCapabilityMatrixContext,
8199
8370
  buildProjectSelectionPrompt,
8200
8371
  formatProjectBindingSegment,
8201
8372
  formatSessionNumberSegment,