@orchestrator-claude/definitions 3.37.0 → 3.37.2
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.
- package/agents/adversarial-reviewer.md +6 -1
- package/agents/api-extractor.md +6 -0
- package/agents/azure-devops-engineer.md +1 -2
- package/agents/business-rule-miner.md +6 -0
- package/agents/cicd-platform-engineer.md +1 -2
- package/agents/ciclo-engineer.md +50 -21
- package/agents/code-archaeologist.md +6 -0
- package/agents/debug-sidecar.md +6 -0
- package/agents/doc-sidecar.md +6 -0
- package/agents/docs-guardian.md +9 -3
- package/agents/frontend-engineer.md +1 -2
- package/agents/{ai-architect.md → harness-architect.md} +8 -3
- package/agents/implementer.md +6 -1
- package/agents/legacy-discoverer.md +6 -0
- package/agents/legacy-synthesizer.md +6 -0
- package/agents/methodology-researcher.md +6 -1
- package/agents/operational-artifact-builder.md +6 -1
- package/agents/orchestrator.md +27 -87
- package/agents/planner.md +6 -1
- package/agents/presentation-engineer.md +29 -15
- package/agents/python-data-engineer.md +0 -1
- package/agents/researcher.md +6 -0
- package/agents/reviewer.md +6 -1
- package/agents/schema-extractor.md +6 -0
- package/agents/software-architect.md +10 -5
- package/agents/specifier.md +6 -1
- package/agents/task-generator.md +6 -1
- package/agents/test-sidecar.md +6 -0
- package/dist/scripts/generate-hooks-matcher.d.ts +106 -0
- package/dist/scripts/generate-hooks-matcher.d.ts.map +1 -0
- package/dist/scripts/generate-hooks-matcher.js +153 -0
- package/dist/scripts/generate-hooks-matcher.js.map +1 -0
- package/dist/scripts/generate-manifest.d.ts +22 -0
- package/dist/scripts/generate-manifest.d.ts.map +1 -1
- package/dist/scripts/generate-manifest.js +67 -2
- package/dist/scripts/generate-manifest.js.map +1 -1
- package/hooks/agent-git-guard.mjs +63 -11
- package/hooks/dangling-guard.mjs +42 -12
- package/hooks/gate-guardian.mjs +38 -8
- package/hooks/hooks.json +3 -9
- package/hooks/lib/api-client.ts +128 -9
- package/hooks/lib/capability-scanner.ts +462 -55
- package/hooks/lib/dispatch-marker.ts +214 -0
- package/hooks/lib/git-checkpoint.ts +34 -37
- package/hooks/lib/observations-helper.ts +14 -1
- package/hooks/lib/security-gate-core.ts +271 -0
- package/hooks/mailbox-listener.mjs +69 -17
- package/hooks/playwright-output-guard.mjs +40 -10
- package/hooks/post-implement.mjs +38 -8
- package/hooks/pre-agent-dispatch.mjs +137 -16
- package/hooks/pre-agent-dispatch.ts +82 -15
- package/hooks/security-gate-honest.mjs +302 -0
- package/hooks/security-gate-honest.ts +148 -0
- package/hooks/session-start.mjs +304 -106
- package/hooks/session-start.ts +317 -71
- package/hooks/subagent-start.mjs +7673 -22
- package/hooks/subagent-start.ts +367 -16
- package/hooks/subagent-stop.mjs +102 -62
- package/hooks/subagent-stop.ts +124 -28
- package/hooks/user-prompt.mjs +43 -13
- package/hooks/verification-before-completion.mjs +63 -11
- package/hooks/workflow-guard.mjs +45 -11
- package/hooks-settings-template.json +1 -7
- package/manifest.json +77 -61
- package/package.json +4 -2
- package/skills/checkpoint-protocol/SKILL.md +19 -4
- package/skills/ciclo-lane-dispatch/SKILL.md +117 -8
- package/skills/fase-1b-projecao/SKILL.md +150 -0
- package/skills/lane-1-entrevista/SKILL.md +173 -0
- package/skills/orchestrator/SKILL.md +11 -3
- package/skills/workflow-coordination/SKILL.md +3 -3
- package/hooks/post-compact.mjs +0 -7931
- package/hooks/post-compact.ts +0 -170
package/hooks/session-start.mjs
CHANGED
|
@@ -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
|
|
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
|
|
7332
|
-
import {
|
|
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
|
-
|
|
7346
|
+
candidates.push(currentDir);
|
|
7342
7347
|
}
|
|
7343
7348
|
const parentDir = dirname(currentDir);
|
|
7344
7349
|
if (parentDir === currentDir) {
|
|
7345
|
-
|
|
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
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
7805
|
+
function readCapabilityCache(cachePath, vaultConfigPath) {
|
|
7760
7806
|
try {
|
|
7761
|
-
if (!existsSync2(
|
|
7762
|
-
const raw = readFileSync2(
|
|
7763
|
-
const
|
|
7764
|
-
|
|
7765
|
-
if (
|
|
7766
|
-
const rawPlugins = Array.isArray(
|
|
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:
|
|
7778
|
-
installedPluginsMtime:
|
|
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:
|
|
7784
|
-
gaps:
|
|
7829
|
+
routing: cache.routing ?? {},
|
|
7830
|
+
gaps: cache.gaps ?? []
|
|
7785
7831
|
};
|
|
7786
7832
|
} catch {
|
|
7787
7833
|
return null;
|
|
@@ -7809,32 +7855,102 @@ function formatCapabilityMatrix(index) {
|
|
|
7809
7855
|
`gaps: ${gapsLine}`
|
|
7810
7856
|
].join("\n");
|
|
7811
7857
|
}
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
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 hasRenderableSummary(entry) {
|
|
7866
|
+
return typeof entry.summary === "string";
|
|
7867
|
+
}
|
|
7868
|
+
var MEMORY_SKIPPED_WARNING_PREFIX = "- [AVISO] ";
|
|
7869
|
+
function countByType(list) {
|
|
7870
|
+
const counts = {};
|
|
7871
|
+
for (const e of list) {
|
|
7872
|
+
counts[e.type] = (counts[e.type] ?? 0) + 1;
|
|
7817
7873
|
}
|
|
7818
|
-
|
|
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");
|
|
7874
|
+
return counts;
|
|
7824
7875
|
}
|
|
7825
|
-
function
|
|
7876
|
+
function formatMemoryContext(selected, allEntries) {
|
|
7877
|
+
const renderable = selected.filter(hasRenderableSummary);
|
|
7878
|
+
const skipped = selected.filter((e) => !hasRenderableSummary(e));
|
|
7879
|
+
const warning = skipped.length > 0 ? `${MEMORY_SKIPPED_WARNING_PREFIX}${String(skipped.length)} entrada(s) sem 'summary:' pulada(s) \u2014 conserte o \xEDndice: ${skipped.map((e) => e.file).join(", ")}` : "";
|
|
7880
|
+
if (renderable.length === 0) return warning ? `[MEMORY-CONTEXT] 0 mem\xF3rias
|
|
7881
|
+
${warning}` : "";
|
|
7882
|
+
const selectedCounts = countByType(renderable);
|
|
7883
|
+
const totalCounts = countByType(allEntries);
|
|
7884
|
+
const perTypeSummary = MEMORY_TYPES.map(
|
|
7885
|
+
(t) => `${t}: ${selectedCounts[t] ?? 0}/${totalCounts[t] ?? 0}`
|
|
7886
|
+
).join(", ");
|
|
7887
|
+
const headerBase = `[MEMORY-CONTEXT] ${renderable.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`;
|
|
7888
|
+
const availableForLines = AGGREGATE_CHAR_BUDGET - headerBase.length - SUFFIX_RESERVE - (warning ? warning.length + 1 : 0);
|
|
7889
|
+
const lines = [];
|
|
7890
|
+
let used = 0;
|
|
7891
|
+
let droppedByBudget = 0;
|
|
7892
|
+
for (let i = 0; i < renderable.length; i++) {
|
|
7893
|
+
const entry = renderable[i];
|
|
7894
|
+
const name = entry.file.replace(/\.md$/, "");
|
|
7895
|
+
const truncated = truncateSummary(entry.summary, PER_ENTRY_SUMMARY_CHAR_CAP);
|
|
7896
|
+
const line = `- [${entry.type}] ${name}: ${truncated}`;
|
|
7897
|
+
if (used + line.length + 1 > availableForLines) {
|
|
7898
|
+
droppedByBudget = renderable.length - i;
|
|
7899
|
+
break;
|
|
7900
|
+
}
|
|
7901
|
+
lines.push(line);
|
|
7902
|
+
used += line.length + 1;
|
|
7903
|
+
}
|
|
7904
|
+
const header = droppedByBudget > 0 ? `${headerBase}; +${droppedByBudget} truncadas por or\xE7amento de caracteres` : headerBase;
|
|
7905
|
+
return [header, ...lines, warning].filter(Boolean).join("\n");
|
|
7906
|
+
}
|
|
7907
|
+
function isWellFormedMemoryEntry(value) {
|
|
7908
|
+
return typeof value === "object" && value !== null && typeof value.file === "string";
|
|
7909
|
+
}
|
|
7910
|
+
function readMemoryIndex(indexPath, diag) {
|
|
7826
7911
|
try {
|
|
7827
7912
|
if (!existsSync2(indexPath)) return null;
|
|
7828
7913
|
const raw = readFileSync2(indexPath, "utf-8");
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7914
|
+
let parsed;
|
|
7915
|
+
try {
|
|
7916
|
+
parsed = (0, import_yaml.parse)(raw);
|
|
7917
|
+
} catch (err) {
|
|
7918
|
+
if (diag) diag.reason = `YAML inv\xE1lido em ${indexPath}: ${String(err).split("\n")[0]}`;
|
|
7919
|
+
return null;
|
|
7920
|
+
}
|
|
7921
|
+
let candidate = null;
|
|
7922
|
+
if (Array.isArray(parsed)) {
|
|
7923
|
+
candidate = parsed;
|
|
7924
|
+
} else if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.entries)) {
|
|
7925
|
+
candidate = parsed.entries;
|
|
7926
|
+
}
|
|
7927
|
+
if (candidate === null) {
|
|
7928
|
+
const chaves = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? Object.keys(parsed).join(", ") : typeof parsed;
|
|
7929
|
+
if (diag) {
|
|
7930
|
+
diag.reason = `formato n\xE3o reconhecido em ${indexPath} \u2014 topo tem [${chaves}]; esperado uma lista no topo ou a chave 'entries:'`;
|
|
7931
|
+
}
|
|
7932
|
+
return null;
|
|
7933
|
+
}
|
|
7934
|
+
const bemFormadas = candidate.filter(isWellFormedMemoryEntry);
|
|
7935
|
+
if (diag && candidate.length > 0) {
|
|
7936
|
+
const descartadas = candidate.length - bemFormadas.length;
|
|
7937
|
+
if (bemFormadas.length === 0) {
|
|
7938
|
+
diag.reason = `${candidate.length} entrada(s) lidas e NENHUMA bem-formada em ${indexPath} \u2014 toda entrada precisa da chave 'file:'`;
|
|
7939
|
+
} else if (descartadas > 0) {
|
|
7940
|
+
diag.reason = `${descartadas} de ${candidate.length} entrada(s) descartadas em ${indexPath} (falta a chave 'file:')`;
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
return bemFormadas;
|
|
7944
|
+
} catch (err) {
|
|
7945
|
+
if (diag) diag.reason = `falha ao ler ${indexPath}: ${String(err).split("\n")[0]}`;
|
|
7832
7946
|
return null;
|
|
7833
7947
|
}
|
|
7834
7948
|
}
|
|
7835
7949
|
function filterRelevantMemories(entries, contextTags = []) {
|
|
7836
7950
|
const now = Date.now();
|
|
7837
7951
|
const fourteenDays = 14 * 24 * 60 * 60 * 1e3;
|
|
7952
|
+
const MAX_TOTAL = 20;
|
|
7953
|
+
const RESERVED_SLOTS_PER_TYPE = 2;
|
|
7838
7954
|
const relevant = entries.filter((e) => {
|
|
7839
7955
|
if (e.type === "feedback") return true;
|
|
7840
7956
|
const accessed = new Date(e.last_accessed).getTime();
|
|
@@ -7848,17 +7964,32 @@ function filterRelevantMemories(entries, contextTags = []) {
|
|
|
7848
7964
|
reference: 2,
|
|
7849
7965
|
user: 3
|
|
7850
7966
|
};
|
|
7851
|
-
|
|
7967
|
+
const byPriorityThenRecency = (a, b) => {
|
|
7852
7968
|
const pa = typePriority[a.type] ?? 9;
|
|
7853
7969
|
const pb = typePriority[b.type] ?? 9;
|
|
7854
7970
|
if (pa !== pb) return pa - pb;
|
|
7855
7971
|
return b.last_accessed.localeCompare(a.last_accessed);
|
|
7856
|
-
}
|
|
7857
|
-
|
|
7972
|
+
};
|
|
7973
|
+
relevant.sort(byPriorityThenRecency);
|
|
7974
|
+
const reserved = /* @__PURE__ */ new Set();
|
|
7975
|
+
const nonFeedbackTypes = new Set(entries.filter((e) => e.type !== "feedback").map((e) => e.type));
|
|
7976
|
+
for (const type of nonFeedbackTypes) {
|
|
7977
|
+
const ofType = entries.filter((e) => e.type === type).sort((a, b) => b.last_accessed.localeCompare(a.last_accessed));
|
|
7978
|
+
for (const entry of ofType.slice(0, RESERVED_SLOTS_PER_TYPE)) {
|
|
7979
|
+
reserved.add(entry);
|
|
7980
|
+
}
|
|
7981
|
+
}
|
|
7982
|
+
const selected = new Set(reserved);
|
|
7983
|
+
for (const entry of relevant) {
|
|
7984
|
+
if (selected.size >= MAX_TOTAL) break;
|
|
7985
|
+
if (reserved.has(entry)) continue;
|
|
7986
|
+
selected.add(entry);
|
|
7987
|
+
}
|
|
7988
|
+
return [...selected].sort(byPriorityThenRecency);
|
|
7858
7989
|
}
|
|
7859
7990
|
|
|
7860
7991
|
// hooks/lib/session-selection.ts
|
|
7861
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as
|
|
7992
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
7862
7993
|
import { join as join4 } from "node:path";
|
|
7863
7994
|
function selectionFilePath(stateDir, sessionId) {
|
|
7864
7995
|
const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -7879,8 +8010,8 @@ function readSessionSelection(stateDir, sessionId) {
|
|
|
7879
8010
|
|
|
7880
8011
|
// hooks/session-start.ts
|
|
7881
8012
|
var HOOK = "SESSION-START";
|
|
7882
|
-
var
|
|
7883
|
-
var
|
|
8013
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
8014
|
+
var __dirname = dirname3(__filename);
|
|
7884
8015
|
var PROJECT_ROOT2 = resolveHookProjectRoot(process.cwd());
|
|
7885
8016
|
var PROJECT_ID2 = process.env.ORCHESTRATOR_PROJECT_ID ?? "";
|
|
7886
8017
|
var HOME = process.env.HOME ?? process.env.USERPROFILE ?? "/home/user";
|
|
@@ -7889,18 +8020,35 @@ var PROJECT_SELECTION_ENABLED = process.env.ORCHESTRATOR_ENABLE_PROJECT_SELECTIO
|
|
|
7889
8020
|
var SESSION_SELECTION_STATE_DIR = join5(HOME, ".orchestrator", "session-selection");
|
|
7890
8021
|
var AGENTS_DIR = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, ".claude", "agents") : null;
|
|
7891
8022
|
var VAULT_CONFIG_PATH = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, "project-guidelines", ".vault-config.yaml") : null;
|
|
8023
|
+
var CAPABILITY_CACHE_PATH = PROJECT_ROOT2 ? resolveCapabilityCachePath(PROJECT_ROOT2) : null;
|
|
7892
8024
|
var PROFILES_PATH = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, ".claude", "plugin-profiles.yaml") : null;
|
|
7893
8025
|
var MEMORY_INDEX_PATH = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, ".claude", "agent-memory", "orchestrator", "memory-index.yaml") : null;
|
|
7894
|
-
function
|
|
8026
|
+
async function readSessionStartPayload() {
|
|
7895
8027
|
try {
|
|
7896
|
-
const
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
8028
|
+
const payload = await readStdin();
|
|
8029
|
+
log(HOOK, `stdin keys: [${Object.keys(payload).join(", ")}]`);
|
|
8030
|
+
if (!payload.session_id && !payload.source) {
|
|
8031
|
+
log(
|
|
8032
|
+
HOOK,
|
|
8033
|
+
`[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(", ")}]`
|
|
8034
|
+
);
|
|
8035
|
+
}
|
|
8036
|
+
return {
|
|
8037
|
+
sessionId: payload.session_id ?? null,
|
|
8038
|
+
source: payload.source ?? null
|
|
8039
|
+
};
|
|
8040
|
+
} catch (err) {
|
|
8041
|
+
log(HOOK, `[WARN] readStdin() itself threw (not just empty/malformed stdin) \u2014 ${String(err)}`);
|
|
8042
|
+
return { sessionId: null, source: null };
|
|
7902
8043
|
}
|
|
7903
8044
|
}
|
|
8045
|
+
function isRecoverySource(source) {
|
|
8046
|
+
return source === "compact";
|
|
8047
|
+
}
|
|
8048
|
+
var RECOVERY_DIRECTIVE = [
|
|
8049
|
+
"[ORCHESTRATOR] Context was compacted mid-session. This is NOT a session start.",
|
|
8050
|
+
"Do NOT greet. Do NOT call AskUserQuestion. Resume the in-flight task."
|
|
8051
|
+
];
|
|
7904
8052
|
var PROJECT_STATE_PATH = PROJECT_ROOT2 ? join5(PROJECT_ROOT2, "project-guidelines", "project-state.yaml") : null;
|
|
7905
8053
|
function synthesizeStatus(pending) {
|
|
7906
8054
|
const { critical, high, medium, ci_gating: ciGating } = pending;
|
|
@@ -8057,43 +8205,76 @@ async function buildProjectSelectionPrompt() {
|
|
|
8057
8205
|
return "";
|
|
8058
8206
|
}
|
|
8059
8207
|
}
|
|
8060
|
-
function
|
|
8208
|
+
function buildMatrixLine(vaultConfigPath, agentsDir, profilesPath, cachePath) {
|
|
8209
|
+
let index = readCapabilityCache(cachePath, vaultConfigPath);
|
|
8210
|
+
const cachedMtime = index?.installedPluginsMtime ?? "";
|
|
8211
|
+
if (!index || needsRescan(INSTALLED_PLUGINS_PATH, cachedMtime)) {
|
|
8212
|
+
log(HOOK, "Capability rescan triggered");
|
|
8213
|
+
index = buildCapabilityIndex({
|
|
8214
|
+
installedPluginsPath: INSTALLED_PLUGINS_PATH,
|
|
8215
|
+
agentsDir,
|
|
8216
|
+
vaultConfigPath,
|
|
8217
|
+
profilesPath
|
|
8218
|
+
});
|
|
8219
|
+
writeCapabilityCache(cachePath, index);
|
|
8220
|
+
} else {
|
|
8221
|
+
index.agents = scanAgentDefinitions(agentsDir);
|
|
8222
|
+
log(HOOK, "Using cached capability index");
|
|
8223
|
+
}
|
|
8224
|
+
return formatCapabilityMatrix(index);
|
|
8225
|
+
}
|
|
8226
|
+
function buildMemoryLine(memoryIndexPath) {
|
|
8227
|
+
const diag = {};
|
|
8228
|
+
const memoryEntries = readMemoryIndex(memoryIndexPath, diag);
|
|
8229
|
+
if (diag.reason) log(HOOK, `Memory index: ${diag.reason}`);
|
|
8230
|
+
if (diag.reason && (!memoryEntries || memoryEntries.length === 0)) {
|
|
8231
|
+
return `[MEMORY-CONTEXT] INDISPON\xCDVEL \u2014 nenhuma mem\xF3ria injetada. ${diag.reason}`;
|
|
8232
|
+
}
|
|
8233
|
+
if (!memoryEntries) return "";
|
|
8234
|
+
const filtered = filterRelevantMemories(memoryEntries);
|
|
8235
|
+
return formatMemoryContext(filtered, memoryEntries);
|
|
8236
|
+
}
|
|
8237
|
+
function resolveVaultPaths(caller) {
|
|
8238
|
+
if (VAULT_CONFIG_PATH === null || AGENTS_DIR === null || PROFILES_PATH === null || MEMORY_INDEX_PATH === null || CAPABILITY_CACHE_PATH === null) {
|
|
8239
|
+
log(HOOK, `${caller}: PROJECT_ROOT unresolved (unregistered cwd) \u2014 skipping`);
|
|
8240
|
+
return null;
|
|
8241
|
+
}
|
|
8242
|
+
return {
|
|
8243
|
+
vaultConfigPath: VAULT_CONFIG_PATH,
|
|
8244
|
+
agentsDir: AGENTS_DIR,
|
|
8245
|
+
profilesPath: PROFILES_PATH,
|
|
8246
|
+
memoryIndexPath: MEMORY_INDEX_PATH,
|
|
8247
|
+
capabilityCachePath: CAPABILITY_CACHE_PATH
|
|
8248
|
+
};
|
|
8249
|
+
}
|
|
8250
|
+
function buildMatrixContext() {
|
|
8251
|
+
const paths = resolveVaultPaths("buildMatrixContext");
|
|
8252
|
+
if (!paths) return "";
|
|
8061
8253
|
try {
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
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");
|
|
8254
|
+
return buildMatrixLine(
|
|
8255
|
+
paths.vaultConfigPath,
|
|
8256
|
+
paths.agentsDir,
|
|
8257
|
+
paths.profilesPath,
|
|
8258
|
+
paths.capabilityCachePath
|
|
8259
|
+
);
|
|
8092
8260
|
} catch (err) {
|
|
8093
8261
|
log(HOOK, `Capability matrix failed: ${err}`);
|
|
8094
8262
|
return "";
|
|
8095
8263
|
}
|
|
8096
8264
|
}
|
|
8265
|
+
function buildMemoryContext() {
|
|
8266
|
+
const paths = resolveVaultPaths("buildMemoryContext");
|
|
8267
|
+
if (!paths) return "";
|
|
8268
|
+
try {
|
|
8269
|
+
return buildMemoryLine(paths.memoryIndexPath);
|
|
8270
|
+
} catch (err) {
|
|
8271
|
+
log(HOOK, `Memory context failed: ${err}`);
|
|
8272
|
+
return "";
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8275
|
+
function buildCapabilityMatrixContext() {
|
|
8276
|
+
return [buildMatrixContext(), buildMemoryContext()].filter(Boolean).join("\n");
|
|
8277
|
+
}
|
|
8097
8278
|
function checkSprintInbox() {
|
|
8098
8279
|
const sprintId = process.env.SPRINT_ID;
|
|
8099
8280
|
const paneRole = process.env.PANE_ROLE;
|
|
@@ -8140,13 +8321,16 @@ async function main() {
|
|
|
8140
8321
|
"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
8322
|
);
|
|
8142
8323
|
}
|
|
8143
|
-
const sessionId =
|
|
8324
|
+
const { sessionId, source } = await readSessionStartPayload();
|
|
8325
|
+
const isRecovery = isRecoverySource(source);
|
|
8144
8326
|
const sessionSegment = sessionId ? ` | session=${sessionId}` : "";
|
|
8145
8327
|
if (sessionId) log(HOOK, `Claude session: ${sessionId}`);
|
|
8328
|
+
log(HOOK, `source=${source ?? "unknown"} mode=${isRecovery ? "recovery" : "boot"}`);
|
|
8146
8329
|
const backlog = getBacklogSummary();
|
|
8147
8330
|
const tierInfo = await checkTier();
|
|
8148
8331
|
const tierSegment = formatTierSegment(tierInfo);
|
|
8149
|
-
const
|
|
8332
|
+
const memoryContext = buildMemoryContext();
|
|
8333
|
+
const matrixContext = buildMatrixContext();
|
|
8150
8334
|
const sprintInboxContext = checkSprintInbox();
|
|
8151
8335
|
await getToken();
|
|
8152
8336
|
const selection = PROJECT_SELECTION_ENABLED && sessionId ? readSessionSelection(SESSION_SELECTION_STATE_DIR, sessionId) : void 0;
|
|
@@ -8157,22 +8341,26 @@ async function main() {
|
|
|
8157
8341
|
});
|
|
8158
8342
|
const projectBindingSegment = formatProjectBindingSegment(projectBinding);
|
|
8159
8343
|
setEffectiveProjectId(projectBinding.projectId);
|
|
8160
|
-
const projectSelectionPrompt = PROJECT_SELECTION_ENABLED && projectBinding.source === "none" ? await buildProjectSelectionPrompt() : "";
|
|
8344
|
+
const projectSelectionPrompt = PROJECT_SELECTION_ENABLED && !isRecovery && projectBinding.source === "none" ? await buildProjectSelectionPrompt() : "";
|
|
8161
8345
|
const sessionAllocation = await allocateSessionNumber(projectBinding.projectId);
|
|
8162
8346
|
const sessionNumberSegment = formatSessionNumberSegment(sessionAllocation);
|
|
8163
8347
|
const workflow = await getActiveWorkflow();
|
|
8164
8348
|
if (!workflow) {
|
|
8165
|
-
log(HOOK,
|
|
8349
|
+
log(HOOK, `No active workflow, injecting ${isRecovery ? "recovery" : "greeting"} data`);
|
|
8350
|
+
const stateLine2 = `version=${backlog.version}${sessionSegment} | workflow=none${tierSegment}${sessionNumberSegment}${projectBindingSegment} | next=${backlog.next}`;
|
|
8166
8351
|
outputContext(
|
|
8167
8352
|
"SessionStart",
|
|
8168
8353
|
[
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
8354
|
+
`${isRecovery ? "[ORCHESTRATOR-RECOVERY]" : "[ORCHESTRATOR-DATA]"} ${stateLine2}`,
|
|
8355
|
+
memoryContext,
|
|
8356
|
+
...isRecovery ? RECOVERY_DIRECTIVE : [
|
|
8357
|
+
backlog.lastSession ? `[LAST-SESSION] ${backlog.lastSession}` : "",
|
|
8358
|
+
backlog.status ? `[PENDENTE] ${backlog.status}` : "",
|
|
8359
|
+
`[ORCHESTRATOR] Greet the user via AskUserQuestion using the data above (orchestrator identity, MUST rule #1).`,
|
|
8360
|
+
"Do NOT invoke /orchestrator skill for the greeting \u2014 use AskUserQuestion directly.",
|
|
8361
|
+
"Invoke /orchestrator skill only when the user chooses to start a workflow."
|
|
8362
|
+
],
|
|
8363
|
+
matrixContext,
|
|
8176
8364
|
sprintInboxContext,
|
|
8177
8365
|
projectSelectionPrompt
|
|
8178
8366
|
].filter(Boolean).join("\n")
|
|
@@ -8181,21 +8369,31 @@ async function main() {
|
|
|
8181
8369
|
}
|
|
8182
8370
|
const action = await getNextAction(workflow.id);
|
|
8183
8371
|
const actionHint = action?.hasAction && action.pendingAction ? ` | pending=${action.pendingAction.agent} (${action.pendingAction.status})` : "";
|
|
8184
|
-
log(
|
|
8372
|
+
log(
|
|
8373
|
+
HOOK,
|
|
8374
|
+
`Injecting workflow ${isRecovery ? "recovery" : "context"} (wf=${workflow.id} phase=${workflow.currentPhase})`
|
|
8375
|
+
);
|
|
8376
|
+
const stateLine = `version=${backlog.version}${sessionSegment} | workflow=${workflow.id} | type=${workflow.type} | phase=${workflow.currentPhase} | status=${workflow.status}${actionHint}${tierSegment}${sessionNumberSegment}${projectBindingSegment}`;
|
|
8185
8377
|
outputContext(
|
|
8186
8378
|
"SessionStart",
|
|
8187
8379
|
[
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8380
|
+
`${isRecovery ? "[ORCHESTRATOR-RECOVERY]" : "[ORCHESTRATOR-DATA]"} ${stateLine}`,
|
|
8381
|
+
memoryContext,
|
|
8382
|
+
...isRecovery ? RECOVERY_DIRECTIVE : [
|
|
8383
|
+
`[ORCHESTRATOR] Active workflow detected. Greet the user via AskUserQuestion showing workflow state (orchestrator identity, MUST rule #1).`,
|
|
8384
|
+
"Do NOT invoke /orchestrator skill for the greeting \u2014 use AskUserQuestion directly.",
|
|
8385
|
+
"Invoke /orchestrator skill when the user chooses to continue the workflow."
|
|
8386
|
+
],
|
|
8387
|
+
matrixContext,
|
|
8193
8388
|
projectSelectionPrompt
|
|
8194
8389
|
].filter(Boolean).join("\n")
|
|
8195
8390
|
);
|
|
8196
8391
|
}
|
|
8197
8392
|
main().catch(() => process.exit(0));
|
|
8198
8393
|
export {
|
|
8394
|
+
buildCapabilityMatrixContext,
|
|
8395
|
+
buildMatrixContext,
|
|
8396
|
+
buildMemoryContext,
|
|
8199
8397
|
buildProjectSelectionPrompt,
|
|
8200
8398
|
formatProjectBindingSegment,
|
|
8201
8399
|
formatSessionNumberSegment,
|