@moreih29/nexus-core 0.16.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/assets/capability-matrix.yml +7 -5
  2. package/assets/hooks/agent-bootstrap/handler.test.ts +18 -17
  3. package/assets/hooks/agent-bootstrap/handler.ts +20 -7
  4. package/assets/hooks/post-tool-telemetry/meta.yml +1 -2
  5. package/assets/hooks/session-init/handler.ts +8 -7
  6. package/dist/assets/hooks/agent-bootstrap/handler.d.ts.map +1 -1
  7. package/dist/assets/hooks/agent-bootstrap/handler.js +22 -8
  8. package/dist/assets/hooks/agent-bootstrap/handler.js.map +1 -1
  9. package/dist/assets/hooks/session-init/handler.d.ts.map +1 -1
  10. package/dist/assets/hooks/session-init/handler.js +5 -6
  11. package/dist/assets/hooks/session-init/handler.js.map +1 -1
  12. package/dist/claude/agents/architect.md +1 -1
  13. package/dist/claude/agents/designer.md +1 -1
  14. package/dist/claude/agents/engineer.md +1 -1
  15. package/dist/claude/agents/lead.md +1 -1
  16. package/dist/claude/agents/postdoc.md +1 -1
  17. package/dist/claude/agents/researcher.md +1 -1
  18. package/dist/claude/agents/reviewer.md +1 -1
  19. package/dist/claude/agents/strategist.md +1 -1
  20. package/dist/claude/agents/tester.md +1 -1
  21. package/dist/claude/agents/writer.md +1 -1
  22. package/dist/claude/dist/hooks/agent-bootstrap.js +128 -11
  23. package/dist/claude/dist/hooks/agent-finalize.js +1 -1
  24. package/dist/claude/dist/hooks/post-tool-telemetry.js +71 -0
  25. package/dist/claude/dist/hooks/prompt-router.js +1 -1
  26. package/dist/claude/dist/hooks/session-init.js +14 -1
  27. package/dist/claude/hooks/hooks.json +12 -0
  28. package/dist/codex/dist/hooks/agent-bootstrap.js +128 -11
  29. package/dist/codex/dist/hooks/agent-finalize.js +1 -1
  30. package/dist/codex/dist/hooks/prompt-router.js +1 -1
  31. package/dist/codex/dist/hooks/session-init.js +14 -1
  32. package/dist/hooks/agent-bootstrap.js +128 -11
  33. package/dist/hooks/agent-finalize.js +1 -1
  34. package/dist/hooks/post-tool-telemetry.js +71 -0
  35. package/dist/hooks/prompt-router.js +1 -1
  36. package/dist/hooks/session-init.js +14 -1
  37. package/dist/manifests/claude-hooks.json +12 -0
  38. package/dist/manifests/opencode-manifest.json +10 -0
  39. package/dist/manifests/portability-report.json +6 -18
  40. package/dist/scripts/build-agents.d.ts +6 -0
  41. package/dist/scripts/build-agents.d.ts.map +1 -1
  42. package/dist/scripts/build-agents.js +17 -0
  43. package/dist/scripts/build-agents.js.map +1 -1
  44. package/dist/scripts/build-hooks.d.ts.map +1 -1
  45. package/dist/scripts/build-hooks.js +7 -0
  46. package/dist/scripts/build-hooks.js.map +1 -1
  47. package/dist/scripts/smoke/smoke-consumer.js +153 -3
  48. package/dist/scripts/smoke/smoke-consumer.js.map +1 -1
  49. package/dist/src/shared/paths.d.ts +3 -1
  50. package/dist/src/shared/paths.d.ts.map +1 -1
  51. package/dist/src/shared/paths.js +38 -2
  52. package/dist/src/shared/paths.js.map +1 -1
  53. package/package.json +1 -1
@@ -162,8 +162,10 @@ capabilities:
162
162
  # Source: https://opencode.ai/docs/agents/ (model field is optional)
163
163
  # Verified: .nexus/memory/external-opencode-plugin.md §5
164
164
  #
165
- # claude model slugs (partial-match basis — Claude Code resolves to latest in series):
166
- # Source: https://code.claude.com/docs/en/sub-agents (model field)
165
+ # claude model slugs (alias formversion-tolerant; Claude Code resolves to the
166
+ # latest dated model in the series. Prefer aliases (opus/sonnet/haiku) over
167
+ # dated IDs (claude-opus-4-7 등) to avoid rot as new versions ship.):
168
+ # Source: https://code.claude.com/docs/en/sub-agents (model field accepts aliases)
167
169
  # Verified: .nexus/memory/external-claude-code-hooks-tools.md §6 (Agent tool: model param)
168
170
  #
169
171
  # codex model slugs:
@@ -178,7 +180,7 @@ model_tier:
178
180
  # high — used by: architect, designer, postdoc, strategist
179
181
  # Reasoning-intensive advisory roles that require deep analysis.
180
182
  high:
181
- claude: claude-opus-4
183
+ claude: opus # alias — Claude Code resolves to latest Opus
182
184
  codex: gpt-5.4 # UNVERIFIED slug — confirm against current Codex model list
183
185
  opencode: null # inherits user config
184
186
 
@@ -186,13 +188,13 @@ model_tier:
186
188
  # Execution and verification roles where throughput matters more than
187
189
  # top-tier reasoning.
188
190
  standard:
189
- claude: claude-sonnet-4
191
+ claude: sonnet # alias — Claude Code resolves to latest Sonnet
190
192
  codex: gpt-5.3-codex # UNVERIFIED slug — confirm against current Codex model list
191
193
  opencode: null # inherits user config
192
194
 
193
195
  # low — reserved tier; no agents currently assigned.
194
196
  # Intended for lightweight utility agents (fast, cheap, simple tasks).
195
197
  low:
196
- claude: claude-haiku-4
198
+ claude: haiku # alias — Claude Code resolves to latest Haiku
197
199
  codex: gpt-5.4-mini # UNVERIFIED slug — confirm against current Codex model list
198
200
  opencode: null # inherits user config
@@ -7,7 +7,7 @@
7
7
  * (3) resume_count > 0 → skip (not fresh)
8
8
  * (4) .nexus/rules/<role>.md absent → core index only
9
9
  * (5) core index > 2KB → truncated to recent-modified N entries
10
- * (6) tracker write side-effect absent (agent-tracker.json unchanged before/after)
10
+ * (6) tracker append: new entry created; same agent_id is idempotent (no duplicate)
11
11
  */
12
12
 
13
13
  import { describe, test, expect, beforeAll, afterAll } from "bun:test";
@@ -256,43 +256,44 @@ describe("scenario 5: 2KB truncation of core index", () => {
256
256
  });
257
257
 
258
258
  // ---------------------------------------------------------------------------
259
- // Scenario (6): no tracker write side-effects
259
+ // Scenario (6): tracker append
260
260
  // ---------------------------------------------------------------------------
261
261
 
262
- describe("scenario 6: handler produces no file write side-effects", () => {
262
+ describe("scenario 6: tracker append", () => {
263
263
  let cleanup: () => void;
264
264
 
265
265
  afterAll(() => cleanup?.());
266
266
 
267
- test("agent-tracker.json is not created or modified by handler", async () => {
267
+ test("agent-tracker.json is created with running entry for fresh agent", async () => {
268
268
  const { cwd, sessionId, cleanup: c } = makeFixture({ withTracker: undefined });
269
269
  cleanup = c;
270
270
 
271
271
  const trackerPath = join(cwd, ".nexus/state", sessionId, "agent-tracker.json");
272
272
 
273
- // Tracker must not exist before the call
274
273
  expect(existsSync(trackerPath)).toBe(false);
275
274
 
276
- await handler(makeInput(cwd, sessionId, "architect"));
275
+ await handler(makeInput(cwd, sessionId, "architect", "agent-001"));
277
276
 
278
- // Tracker must still not exist after the call
279
- expect(existsSync(trackerPath)).toBe(false);
277
+ expect(existsSync(trackerPath)).toBe(true);
278
+ const tracker = JSON.parse(readFileSync(trackerPath, "utf-8")) as Array<Record<string, unknown>>;
279
+ expect(tracker).toHaveLength(1);
280
+ expect(tracker[0]["agent_id"]).toBe("agent-001");
281
+ expect(tracker[0]["agent_type"]).toBe("architect");
282
+ expect(tracker[0]["status"]).toBe("running");
283
+ expect(typeof tracker[0]["started_at"]).toBe("string");
280
284
  });
281
285
 
282
- test("pre-existing agent-tracker.json is not modified by handler", async () => {
283
- const agentId = "agent-side-effect";
284
- const { cwd, sessionId, cleanup: c } = makeFixture({
285
- withTracker: { agentId, resumeCount: 0 },
286
- });
286
+ test("duplicate agent_id on second call does not create a second entry", async () => {
287
+ const { cwd, sessionId, cleanup: c } = makeFixture({ withTracker: undefined });
287
288
  cleanup = c;
288
289
 
289
290
  const trackerPath = join(cwd, ".nexus/state", sessionId, "agent-tracker.json");
290
- const before = readFileSync(trackerPath, "utf-8");
291
291
 
292
- await handler(makeInput(cwd, sessionId, "architect", agentId));
292
+ await handler(makeInput(cwd, sessionId, "architect", "agent-001"));
293
+ await handler(makeInput(cwd, sessionId, "architect", "agent-001"));
293
294
 
294
- const after = readFileSync(trackerPath, "utf-8");
295
- expect(after).toBe(before);
295
+ const tracker = JSON.parse(readFileSync(trackerPath, "utf-8")) as Array<Record<string, unknown>>;
296
+ expect(tracker.filter((e) => e["agent_id"] === "agent-001")).toHaveLength(1);
296
297
  });
297
298
  });
298
299
 
@@ -1,18 +1,18 @@
1
1
  import type { HookHandler } from "../../../src/hooks/types.js";
2
+ import { updateJsonFileLocked } from "../../../src/shared/json-store.js";
2
3
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
4
  import { join } from "node:path";
4
5
 
5
6
  const CORE_INDEX_SIZE_LIMIT = 2 * 1024; // 2KB
6
7
 
7
8
  function loadValidRoles(cwd: string): string[] {
9
+ const inlined = (globalThis as unknown as { __NEXUS_INLINE_AGENT_ROLES__?: string[] }).__NEXUS_INLINE_AGENT_ROLES__;
10
+ if (Array.isArray(inlined)) return inlined;
8
11
  const agentsDir = join(cwd, "assets/agents");
9
- const roles: string[] = [];
10
- if (existsSync(agentsDir)) {
11
- for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
12
- if (entry.isDirectory()) roles.push(entry.name);
13
- }
14
- }
15
- return roles;
12
+ if (!existsSync(agentsDir)) return [];
13
+ return readdirSync(agentsDir, { withFileTypes: true })
14
+ .filter((e) => e.isDirectory())
15
+ .map((e) => e.name);
16
16
  }
17
17
 
18
18
  function readFirstLine(path: string): string {
@@ -95,6 +95,19 @@ const handler: HookHandler = async (input) => {
95
95
  const validRoles = loadValidRoles(cwd);
96
96
  if (!validRoles.includes(agent_type)) return;
97
97
 
98
+ const trackerPath = join(cwd, ".nexus/state", session_id, "agent-tracker.json");
99
+ await updateJsonFileLocked(trackerPath, [], (tracker: Array<Record<string, unknown>>) => {
100
+ const list = Array.isArray(tracker) ? tracker : [];
101
+ if (list.find((e) => e["agent_id"] === agent_id)) return list;
102
+ list.push({
103
+ agent_id,
104
+ agent_type,
105
+ started_at: new Date().toISOString(),
106
+ status: "running",
107
+ });
108
+ return list;
109
+ });
110
+
98
111
  const parts: string[] = [];
99
112
 
100
113
  const coreIndex = buildCoreIndex(cwd);
@@ -1,11 +1,10 @@
1
1
  name: post-tool-telemetry
2
2
  description: Track memory access and file-edit operations for strength/forgetting signals and audit
3
3
  events: [PostToolUse]
4
- matcher: "Read|Edit|Write|MultiEdit|ApplyPatch|NotebookEdit|Bash"
4
+ matcher: "Read|Edit|Write|MultiEdit|ApplyPatch|NotebookEdit"
5
5
  timeout: 5
6
6
  fallback: warn
7
7
  priority: 10
8
8
  requires_capabilities:
9
9
  - event.post_tool_use.read
10
10
  - event.post_tool_use.edit
11
- - event.post_tool_use.bash_parsed
@@ -1,11 +1,11 @@
1
1
  import type { HookHandler } from "../../../src/hooks/types.js";
2
2
  import { mkdirSync, writeFileSync } from "node:fs";
3
3
  import { join, basename } from "node:path";
4
+ import { getParentPid } from "../../../src/shared/paths.js";
4
5
 
5
6
  const handler: HookHandler = async (input) => {
6
7
  if (input.hook_event_name !== "SessionStart") return;
7
8
 
8
- // Sanitize session_id to prevent path traversal
9
9
  const safeSid = basename(input.session_id);
10
10
  if (!safeSid || safeSid.startsWith(".") || safeSid.includes("/")) {
11
11
  process.stderr.write(`[session-init] invalid session_id: ${input.session_id}\n`);
@@ -14,17 +14,18 @@ const handler: HookHandler = async (input) => {
14
14
 
15
15
  const sessionDir = join(input.cwd, ".nexus/state", safeSid);
16
16
 
17
- // Ensure directory exists (idempotent)
18
17
  mkdirSync(sessionDir, { recursive: true });
19
18
 
20
- // Initialize per-session state files — overwrite unconditionally (resume is intentional)
21
19
  writeFileSync(join(sessionDir, "agent-tracker.json"), "[]");
22
20
  writeFileSync(join(sessionDir, "tool-log.jsonl"), "");
23
21
 
24
- // plan.json and tasks.json are MCP responsibility — not touched here
25
- // memory-access.jsonl is project-level — not touched here
26
-
27
- // No additional_context returned (decided: no context injection at session start)
22
+ const ppid = getParentPid();
23
+ const byPpidDir = join(input.cwd, ".nexus/state/runtime/by-ppid");
24
+ mkdirSync(byPpidDir, { recursive: true });
25
+ writeFileSync(
26
+ join(byPpidDir, `${ppid}.json`),
27
+ JSON.stringify({ session_id: input.session_id, updated_at: new Date().toISOString(), cwd: input.cwd }),
28
+ );
28
29
  };
29
30
 
30
31
  export default handler;
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../assets/hooks/agent-bootstrap/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAoF/D,QAAA,MAAM,OAAO,EAAE,WAgCd,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../assets/hooks/agent-bootstrap/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAoF/D,QAAA,MAAM,OAAO,EAAE,WA6Cd,CAAC;AAEF,eAAe,OAAO,CAAC"}
@@ -1,16 +1,17 @@
1
+ import { updateJsonFileLocked } from "../../../src/shared/json-store.js";
1
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
3
  import { join } from "node:path";
3
4
  const CORE_INDEX_SIZE_LIMIT = 2 * 1024; // 2KB
4
5
  function loadValidRoles(cwd) {
6
+ const inlined = globalThis.__NEXUS_INLINE_AGENT_ROLES__;
7
+ if (Array.isArray(inlined))
8
+ return inlined;
5
9
  const agentsDir = join(cwd, "assets/agents");
6
- const roles = [];
7
- if (existsSync(agentsDir)) {
8
- for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
9
- if (entry.isDirectory())
10
- roles.push(entry.name);
11
- }
12
- }
13
- return roles;
10
+ if (!existsSync(agentsDir))
11
+ return [];
12
+ return readdirSync(agentsDir, { withFileTypes: true })
13
+ .filter((e) => e.isDirectory())
14
+ .map((e) => e.name);
14
15
  }
15
16
  function readFirstLine(path) {
16
17
  try {
@@ -80,6 +81,19 @@ const handler = async (input) => {
80
81
  const validRoles = loadValidRoles(cwd);
81
82
  if (!validRoles.includes(agent_type))
82
83
  return;
84
+ const trackerPath = join(cwd, ".nexus/state", session_id, "agent-tracker.json");
85
+ await updateJsonFileLocked(trackerPath, [], (tracker) => {
86
+ const list = Array.isArray(tracker) ? tracker : [];
87
+ if (list.find((e) => e["agent_id"] === agent_id))
88
+ return list;
89
+ list.push({
90
+ agent_id,
91
+ agent_type,
92
+ started_at: new Date().toISOString(),
93
+ status: "running",
94
+ });
95
+ return list;
96
+ });
83
97
  const parts = [];
84
98
  const coreIndex = buildCoreIndex(cwd);
85
99
  if (coreIndex) {
@@ -1 +1 @@
1
- {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../../assets/hooks/agent-bootstrap/handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../../assets/hooks/agent-bootstrap/handler.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../assets/hooks/session-init/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAI/D,QAAA,MAAM,OAAO,EAAE,WAuBd,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../assets/hooks/session-init/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAK/D,QAAA,MAAM,OAAO,EAAE,WAuBd,CAAC;AAEF,eAAe,OAAO,CAAC"}
@@ -1,23 +1,22 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { join, basename } from "node:path";
3
+ import { getParentPid } from "../../../src/shared/paths.js";
3
4
  const handler = async (input) => {
4
5
  if (input.hook_event_name !== "SessionStart")
5
6
  return;
6
- // Sanitize session_id to prevent path traversal
7
7
  const safeSid = basename(input.session_id);
8
8
  if (!safeSid || safeSid.startsWith(".") || safeSid.includes("/")) {
9
9
  process.stderr.write(`[session-init] invalid session_id: ${input.session_id}\n`);
10
10
  return;
11
11
  }
12
12
  const sessionDir = join(input.cwd, ".nexus/state", safeSid);
13
- // Ensure directory exists (idempotent)
14
13
  mkdirSync(sessionDir, { recursive: true });
15
- // Initialize per-session state files — overwrite unconditionally (resume is intentional)
16
14
  writeFileSync(join(sessionDir, "agent-tracker.json"), "[]");
17
15
  writeFileSync(join(sessionDir, "tool-log.jsonl"), "");
18
- // plan.json and tasks.json are MCP responsibility — not touched here
19
- // memory-access.jsonl is project-level — not touched here
20
- // No additional_context returned (decided: no context injection at session start)
16
+ const ppid = getParentPid();
17
+ const byPpidDir = join(input.cwd, ".nexus/state/runtime/by-ppid");
18
+ mkdirSync(byPpidDir, { recursive: true });
19
+ writeFileSync(join(byPpidDir, `${ppid}.json`), JSON.stringify({ session_id: input.session_id, updated_at: new Date().toISOString(), cwd: input.cwd }));
21
20
  };
22
21
  export default handler;
23
22
  //# sourceMappingURL=handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../../assets/hooks/session-init/handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,OAAO,GAAgB,KAAK,EAAE,KAAK,EAAE,EAAE;IAC3C,IAAI,KAAK,CAAC,eAAe,KAAK,cAAc;QAAE,OAAO;IAErD,gDAAgD;IAChD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;QACjF,OAAO;IACT,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;IAE5D,uCAAuC;IACvC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,yFAAyF;IACzF,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,oBAAoB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5D,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEtD,qEAAqE;IACrE,0DAA0D;IAE1D,kFAAkF;AACpF,CAAC,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../../assets/hooks/session-init/handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,MAAM,OAAO,GAAgB,KAAK,EAAE,KAAK,EAAE,EAAE;IAC3C,IAAI,KAAK,CAAC,eAAe,KAAK,cAAc;QAAE,OAAO;IAErD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;QACjF,OAAO;IACT,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;IAE5D,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,oBAAoB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5D,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEtD,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,8BAA8B,CAAC,CAAC;IAClE,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,aAAa,CACX,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,EAC/B,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CACvG,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,OAAO,CAAC"}
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Technical design — evaluates How, reviews architecture, advises on implementation approach"
3
- model: claude-opus-4
3
+ model: opus
4
4
  disallowedTools:
5
5
  - Edit
6
6
  - Write
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "UX/UI design — evaluates user experience, interaction patterns, and how users will experience the product"
3
- model: claude-opus-4
3
+ model: opus
4
4
  disallowedTools:
5
5
  - Edit
6
6
  - Write
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Implementation — writes code, debugs issues, follows specifications from Lead and architect"
3
- model: claude-sonnet-4
3
+ model: sonnet
4
4
  disallowedTools:
5
5
  - mcp__plugin_claude-nexus_nx__nx_task_add
6
6
  ---
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Primary orchestrator — converses directly with users, composes 9 subagents across HOW/DO/CHECK categories, and owns scope decisions and task lifecycle"
3
- model: claude-opus-4
3
+ model: opus
4
4
  ---
5
5
  ## Identity
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Research methodology and synthesis — designs investigation approach, evaluates evidence quality, writes synthesis documents"
3
- model: claude-opus-4
3
+ model: opus
4
4
  disallowedTools:
5
5
  - Edit
6
6
  - Write
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Independent investigation — conducts web searches, gathers evidence, and reports findings with citations"
3
- model: claude-sonnet-4
3
+ model: sonnet
4
4
  disallowedTools:
5
5
  - Edit
6
6
  - Write
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Content verification — validates accuracy, checks facts, confirms grammar and format of non-code deliverables"
3
- model: claude-sonnet-4
3
+ model: sonnet
4
4
  disallowedTools:
5
5
  - Edit
6
6
  - Write
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Business strategy — evaluates market positioning, competitive landscape, and business viability of decisions"
3
- model: claude-opus-4
3
+ model: opus
4
4
  disallowedTools:
5
5
  - Edit
6
6
  - Write
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Testing and verification — tests, verifies, validates stability and security of implementations"
3
- model: claude-sonnet-4
3
+ model: sonnet
4
4
  disallowedTools:
5
5
  - Edit
6
6
  - Write
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Technical writing — transforms research findings, code, and analysis into clear documents and presentations for the intended audience"
3
- model: claude-sonnet-4
3
+ model: sonnet
4
4
  disallowedTools:
5
5
  - mcp__plugin_claude-nexus_nx__nx_task_add
6
6
  ---
@@ -1,21 +1,124 @@
1
+ // src/shared/json-store.js
2
+ import fs from "node:fs/promises";
3
+ import { constants as fsConstants, appendFileSync, mkdirSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { randomUUID } from "node:crypto";
6
+ var inProcessQueues = new Map;
7
+ async function runWithInProcessLock(filePath, action) {
8
+ const previous = inProcessQueues.get(filePath) ?? Promise.resolve();
9
+ let release = () => {};
10
+ const gate = new Promise((resolve) => {
11
+ release = resolve;
12
+ });
13
+ const entry = previous.then(() => gate);
14
+ inProcessQueues.set(filePath, entry);
15
+ await previous;
16
+ try {
17
+ return await action();
18
+ } finally {
19
+ release();
20
+ entry.finally(() => {
21
+ if (inProcessQueues.get(filePath) === entry) {
22
+ inProcessQueues.delete(filePath);
23
+ }
24
+ });
25
+ }
26
+ }
27
+ var LOCK_RETRY_INTERVAL_MS = 100;
28
+ var LOCK_MAX_RETRIES = 50;
29
+ var LOCK_STALE_MS = 30000;
30
+ function lockPath(filePath) {
31
+ return `${filePath}.lock`;
32
+ }
33
+ async function acquireFsLock(filePath) {
34
+ const lp = lockPath(filePath);
35
+ for (let attempt = 0;attempt <= LOCK_MAX_RETRIES; attempt++) {
36
+ try {
37
+ const fd = await fs.open(lp, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL);
38
+ await fd.close();
39
+ return;
40
+ } catch (err) {
41
+ const e = err;
42
+ if (e.code !== "EEXIST")
43
+ throw err;
44
+ try {
45
+ const stat = await fs.stat(lp);
46
+ const ageMs = Date.now() - stat.mtimeMs;
47
+ if (ageMs > LOCK_STALE_MS) {
48
+ await fs.unlink(lp).catch(() => {
49
+ return;
50
+ });
51
+ continue;
52
+ }
53
+ } catch {
54
+ continue;
55
+ }
56
+ if (attempt === LOCK_MAX_RETRIES) {
57
+ throw new Error(`Failed to acquire lock for "${filePath}" after ${LOCK_MAX_RETRIES} retries`);
58
+ }
59
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));
60
+ }
61
+ }
62
+ }
63
+ async function releaseFsLock(filePath) {
64
+ await fs.unlink(lockPath(filePath)).catch(() => {
65
+ return;
66
+ });
67
+ }
68
+ async function readJsonFile(filePath, defaultValue) {
69
+ let raw;
70
+ try {
71
+ raw = await fs.readFile(filePath, "utf8");
72
+ } catch (err) {
73
+ const e = err;
74
+ if (e.code === "ENOENT")
75
+ return defaultValue;
76
+ throw err;
77
+ }
78
+ try {
79
+ return JSON.parse(raw);
80
+ } catch {
81
+ return defaultValue;
82
+ }
83
+ }
84
+ async function writeJsonFile(filePath, data) {
85
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
86
+ const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}.${randomUUID()}`;
87
+ await fs.writeFile(tmpPath, JSON.stringify(data, null, 2) + `
88
+ `, "utf8");
89
+ await fs.rename(tmpPath, filePath);
90
+ }
91
+ async function updateJsonFileLocked(filePath, defaultValue, updater) {
92
+ return runWithInProcessLock(filePath, async () => {
93
+ await acquireFsLock(filePath);
94
+ try {
95
+ const current = await readJsonFile(filePath, defaultValue);
96
+ const next = await updater(current);
97
+ await writeJsonFile(filePath, next);
98
+ return next;
99
+ } finally {
100
+ await releaseFsLock(filePath);
101
+ }
102
+ });
103
+ }
104
+ var APPEND_SIZE_WARN_THRESHOLD = 4 * 1024;
105
+
1
106
  // assets/hooks/agent-bootstrap/handler.ts
2
107
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
108
  import { join } from "node:path";
4
109
  var CORE_INDEX_SIZE_LIMIT = 2 * 1024;
5
110
  function loadValidRoles(cwd) {
111
+ const inlined = globalThis.__NEXUS_INLINE_AGENT_ROLES__;
112
+ if (Array.isArray(inlined))
113
+ return inlined;
6
114
  const agentsDir = join(cwd, "assets/agents");
7
- const roles = [];
8
- if (existsSync(agentsDir)) {
9
- for (const entry of readdirSync(agentsDir, { withFileTypes: true })) {
10
- if (entry.isDirectory())
11
- roles.push(entry.name);
12
- }
13
- }
14
- return roles;
115
+ if (!existsSync(agentsDir))
116
+ return [];
117
+ return readdirSync(agentsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
15
118
  }
16
- function readFirstLine(path) {
119
+ function readFirstLine(path2) {
17
120
  try {
18
- const content = readFileSync(path, "utf-8");
121
+ const content = readFileSync(path2, "utf-8");
19
122
  const firstNonEmpty = content.split(`
20
123
  `).find((l) => l.trim().length > 0) ?? "";
21
124
  return firstNonEmpty.replace(/^#+\s*/, "").slice(0, 80);
@@ -76,6 +179,19 @@ var handler = async (input) => {
76
179
  const validRoles = loadValidRoles(cwd);
77
180
  if (!validRoles.includes(agent_type))
78
181
  return;
182
+ const trackerPath = join(cwd, ".nexus/state", session_id, "agent-tracker.json");
183
+ await updateJsonFileLocked(trackerPath, [], (tracker) => {
184
+ const list = Array.isArray(tracker) ? tracker : [];
185
+ if (list.find((e) => e["agent_id"] === agent_id))
186
+ return list;
187
+ list.push({
188
+ agent_id,
189
+ agent_type,
190
+ started_at: new Date().toISOString(),
191
+ status: "running"
192
+ });
193
+ return list;
194
+ });
79
195
  const parts = [];
80
196
  const coreIndex = buildCoreIndex(cwd);
81
197
  if (coreIndex) {
@@ -101,8 +217,9 @@ ${ruleContent}
101
217
  };
102
218
  var handler_default = handler;
103
219
 
104
- // ../../../../../tmp/nexus-hook-entry-agent-bootstrap-1776674516656/agent-bootstrap-entry.ts
220
+ // ../../../../../tmp/nexus-hook-entry-agent-bootstrap-1776690665703/agent-bootstrap-entry.ts
105
221
  import { readFileSync as readFileSync2 } from "node:fs";
222
+ globalThis.__NEXUS_INLINE_AGENT_ROLES__ = ["architect", "designer", "engineer", "reviewer", "strategist", "researcher", "postdoc", "lead", "tester", "writer"];
106
223
  async function main() {
107
224
  let raw = "";
108
225
  try {
@@ -160,7 +160,7 @@ Subagent "${agent_type}" finished. Tasks still pending with this role: ${ids}. R
160
160
  };
161
161
  var handler_default = handler;
162
162
 
163
- // ../../../../../tmp/nexus-hook-entry-agent-finalize-1776674516650/agent-finalize-entry.ts
163
+ // ../../../../../tmp/nexus-hook-entry-agent-finalize-1776690665695/agent-finalize-entry.ts
164
164
  import { readFileSync as readFileSync2 } from "node:fs";
165
165
  async function main() {
166
166
  let raw = "";