@wrongstack/core 0.300.0 → 0.301.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 (144) hide show
  1. package/dist/chronicle/index.d.ts +2 -0
  2. package/dist/chronicle/index.js +297 -10
  3. package/dist/chronicle/project-server.js +253 -15
  4. package/dist/chronicle/sqlite-compaction.d.ts +20 -0
  5. package/dist/chronicle/sqlite-journal.d.ts +57 -0
  6. package/dist/coordination/agents/capability-manifest.d.ts +7 -0
  7. package/dist/coordination/agents/index.d.ts +3 -2
  8. package/dist/coordination/agents/index.js +818 -570
  9. package/dist/coordination/agents/project-agent-identity.d.ts +6 -6
  10. package/dist/coordination/agents/role-skills.d.ts +1 -1
  11. package/dist/coordination/agents/types.d.ts +6 -0
  12. package/dist/coordination/director.d.ts +2 -2
  13. package/dist/coordination/index.js +6778 -6438
  14. package/dist/coordination/mail-tools.d.ts +3 -3
  15. package/dist/coordination/mailbox-project-server.js +3 -4
  16. package/dist/coordination/mailbox-types.d.ts +6 -0
  17. package/dist/core/agent-response.d.ts +5 -1
  18. package/dist/core/agent-tools.d.ts +3 -0
  19. package/dist/core/context.d.ts +20 -0
  20. package/dist/core/index.d.ts +8 -7
  21. package/dist/core/index.js +893 -284
  22. package/dist/core/instruction-template.d.ts +6 -3
  23. package/dist/core/next-steps-slot.d.ts +88 -0
  24. package/dist/core/system-prompt-builder.d.ts +5 -3
  25. package/dist/core/system-prompt-memory-skills.d.ts +1 -0
  26. package/dist/core/system-prompt-skill-bodies.d.ts +3 -3
  27. package/dist/defaults/index.js +9796 -8897
  28. package/dist/design/index.js +11 -3
  29. package/dist/execution/auto-compaction-middleware.d.ts +17 -0
  30. package/dist/execution/autonomy-brain.d.ts +8 -2
  31. package/dist/execution/brain-runtime.d.ts +3 -1
  32. package/dist/execution/compaction-core.d.ts +41 -2
  33. package/dist/execution/council-brain.d.ts +37 -2
  34. package/dist/execution/council-orchestrator.d.ts +2 -2
  35. package/dist/execution/index.d.ts +11 -10
  36. package/dist/execution/index.js +5178 -4077
  37. package/dist/execution/tool-executor.d.ts +4 -1
  38. package/dist/execution/topic-shift-advisor.d.ts +53 -0
  39. package/dist/extension/index.js +8 -2
  40. package/dist/extension/registry.d.ts +8 -1
  41. package/dist/goal/index.js +11 -3
  42. package/dist/hooks/index.js +42 -9
  43. package/dist/hooks/runner.d.ts +12 -1
  44. package/dist/hq/index.js +41 -17
  45. package/dist/hq/publisher.d.ts +21 -3
  46. package/dist/index.d.ts +21 -20
  47. package/dist/index.js +10887 -8053
  48. package/dist/infrastructure/index.js +108 -21
  49. package/dist/kernel/events/session-events.d.ts +13 -0
  50. package/dist/kernel/events/tool-events.d.ts +3 -3
  51. package/dist/models/index.d.ts +1 -0
  52. package/dist/models/index.js +17 -0
  53. package/dist/models/provider-credentials.d.ts +54 -0
  54. package/dist/plugin/index.d.ts +1 -0
  55. package/dist/plugin/index.js +2024 -502
  56. package/dist/plugins/auto-review-plugin.d.ts +3 -0
  57. package/dist/plugins/cloud-config-sync-plugin.d.ts +22 -0
  58. package/dist/plugins/review-claim-registry.d.ts +23 -8
  59. package/dist/plugins/review-types.d.ts +2 -0
  60. package/dist/registry/index.js +109 -74
  61. package/dist/registry/tool-registry.d.ts +15 -0
  62. package/dist/security/capabilities.d.ts +2 -0
  63. package/dist/security/index.d.ts +1 -1
  64. package/dist/security/index.js +9 -2
  65. package/dist/security/readonly-permission-policy.d.ts +20 -0
  66. package/dist/session-registry-atomic-file.d.ts +32 -5
  67. package/dist/skills/frontmatter.d.ts +6 -0
  68. package/dist/skills/index.js +22 -5
  69. package/dist/storage/cloud-config-sync/sanitize.d.ts +54 -0
  70. package/dist/storage/cloud-config-sync.d.ts +87 -0
  71. package/dist/storage/index.d.ts +1 -0
  72. package/dist/storage/index.js +854 -66
  73. package/dist/tools/index.d.ts +1 -1
  74. package/dist/tools/index.js +511 -234
  75. package/dist/tools/one-shot-llm-tool.d.ts +1 -0
  76. package/dist/tools/plugin-manager.d.ts +27 -0
  77. package/dist/types/config/mcp-features.d.ts +18 -11
  78. package/dist/types/config/root.d.ts +8 -1
  79. package/dist/types/config/runtime.d.ts +20 -6
  80. package/dist/types/config/skills-fleet-brain.d.ts +12 -4
  81. package/dist/types/config/tools.d.ts +16 -0
  82. package/dist/types/context-window.d.ts +1 -0
  83. package/dist/types/hooks.d.ts +14 -0
  84. package/dist/types/index.d.ts +2 -2
  85. package/dist/types/index.js +1 -0
  86. package/dist/types/multi-agent.d.ts +2 -0
  87. package/dist/types/one-shot-llm.d.ts +16 -0
  88. package/dist/types/provider.d.ts +16 -0
  89. package/dist/types/runtime-capability-manifest.d.ts +168 -0
  90. package/dist/types/skill.d.ts +5 -0
  91. package/dist/types/system-prompt.d.ts +3 -1
  92. package/dist/types/tool-executor.d.ts +8 -1
  93. package/dist/utils/context-breakdown.d.ts +8 -2
  94. package/dist/utils/index.d.ts +1 -1
  95. package/dist/utils/index.js +141 -31
  96. package/dist/utils/regex-guard.d.ts +16 -2
  97. package/dist/utils/sage-output-block.d.ts +7 -10
  98. package/dist/utils/wstack-paths.d.ts +11 -3
  99. package/instructions/agents/browser.md +1 -4
  100. package/instructions/agents/e2e.md +17 -15
  101. package/instructions/agents/ios.md +3 -3
  102. package/instructions/agents/search.md +1 -1
  103. package/instructions/autonomy/goal-preamble.md +4 -4
  104. package/instructions/coordination/director-preamble.md +2 -3
  105. package/instructions/coordination/subagent-baseline.md +3 -1
  106. package/instructions/leader-after-task.md +12 -0
  107. package/instructions/llm/chimera-review.md +1 -1
  108. package/instructions/modes/audit-lite.md +1 -1
  109. package/instructions/sections/tool/common-patterns.md +18 -2
  110. package/instructions/sections/tool/mailbox-compact.md +1 -1
  111. package/instructions/sections/tool/mailbox-full.md +1 -1
  112. package/instructions/system-lite.md +10 -0
  113. package/instructions/system-pro.md +23 -14
  114. package/instructions/system.md +22 -13
  115. package/package.json +3 -3
  116. package/skills/api-design/SKILL.md +3 -0
  117. package/skills/audit-log/SKILL.md +2 -0
  118. package/skills/auto-review/SKILL.md +6 -1
  119. package/skills/bug-hunter/SKILL.md +4 -1
  120. package/skills/chimera/SKILL.md +3 -0
  121. package/skills/data-governance/SKILL.md +3 -0
  122. package/skills/design-system/SKILL.md +5 -2
  123. package/skills/docker-deploy/SKILL.md +2 -0
  124. package/skills/git-flow/SKILL.md +2 -0
  125. package/skills/mailbox-bridge/SKILL.md +3 -0
  126. package/skills/mnemosyne/SKILL.md +2 -0
  127. package/skills/multi-agent/SKILL.md +4 -1
  128. package/skills/node-modern/SKILL.md +4 -1
  129. package/skills/observability/SKILL.md +3 -0
  130. package/skills/output-standards/SKILL.md +2 -0
  131. package/skills/plugin-author/SKILL.md +4 -1
  132. package/skills/prompt-engineering/SKILL.md +3 -1
  133. package/skills/react-modern/SKILL.md +6 -3
  134. package/skills/refactor-planner/SKILL.md +2 -0
  135. package/skills/research-web/SKILL.md +3 -0
  136. package/skills/sdd/SKILL.md +3 -1
  137. package/skills/security-scanner/SKILL.md +3 -0
  138. package/skills/skill-creator/SKILL.md +2 -0
  139. package/skills/tech-stack/SKILL.md +3 -0
  140. package/skills/testing/SKILL.md +4 -1
  141. package/skills/typescript-strict/SKILL.md +4 -1
  142. package/skills/wrongstack-kanban/SKILL.md +6 -3
  143. package/skills/wrongstack-mailbox/SKILL.md +4 -1
  144. package/skills/wrongstack-mailbox-mcp/SKILL.md +10 -8
@@ -1,56 +1,3 @@
1
- // src/coordination/agents/types.ts
2
- var HOUR = 60 * 60 * 1e3;
3
- var LIGHT_BUDGET = {
4
- timeoutMs: 3 * HOUR,
5
- maxIterations: 3e3,
6
- maxToolCalls: 8e3
7
- };
8
- var MEDIUM_BUDGET = {
9
- timeoutMs: 5 * HOUR,
10
- maxIterations: 5e3,
11
- maxToolCalls: 14e3
12
- };
13
- var HEAVY_BUDGET = {
14
- timeoutMs: 10 * HOUR,
15
- maxIterations: 8e3,
16
- maxToolCalls: 2e4
17
- };
18
- var TOOLS = {
19
- /** Pure read/inspect — safe for analysis and review agents. */
20
- read: ["read", "grep", "glob", "search", "tree", "mailbox"],
21
- /** Read + structured inspection (logs, diffs, json, dependency audit). */
22
- inspect: ["read", "grep", "glob", "search", "tree", "json", "diff", "logs", "audit", "mailbox"],
23
- /** Read + edit (no shell). For agents that write code/docs but don't run it. */
24
- write: ["read", "grep", "glob", "search", "tree", "write", "edit", "replace", "patch", "mailbox"],
25
- /** Full build loop: edit + run (lint/format/typecheck/test/bash). */
26
- build: [
27
- "read",
28
- "grep",
29
- "glob",
30
- "search",
31
- "tree",
32
- "write",
33
- "edit",
34
- "replace",
35
- "patch",
36
- "bash",
37
- "exec",
38
- "lint",
39
- "format",
40
- "typecheck",
41
- "test",
42
- "mailbox"
43
- ],
44
- /** Version control. */
45
- vcs: ["read", "grep", "glob", "git", "diff"],
46
- /** Dependency management + CVE audit. */
47
- deps: ["read", "grep", "glob", "install", "outdated", "audit", "json", "mailbox"],
48
- /** Documentation authoring. */
49
- docs: ["read", "grep", "glob", "search", "tree", "write", "edit", "document", "mailbox"],
50
- /** Web research. */
51
- research: ["read", "grep", "glob", "search", "fetch", "mailbox"]
52
- };
53
-
54
1
  // src/coordination/agents/agent-prompts.ts
55
2
  import { readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
56
3
  import * as path8 from "node:path";
@@ -73,6 +20,9 @@ function canonicalProjectRoot(absRoot) {
73
20
  const commonDirFile = path.join(gitDir, "commondir");
74
21
  if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;
75
22
  const commonDir = path.resolve(gitDir, fs.readFileSync(commonDirFile, "utf8").trim());
23
+ const worktreesDir = path.dirname(gitDir);
24
+ if (path.basename(worktreesDir).toLowerCase() !== "worktrees") return checkoutRoot;
25
+ if (path.resolve(worktreesDir, "..") !== commonDir) return checkoutRoot;
76
26
  if (path.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
77
27
  return path.dirname(commonDir);
78
28
  } catch {
@@ -95,7 +45,7 @@ function safeProfileName(name) {
95
45
  const safe = (name ?? "").replace(/[/\\:]/g, "_").replace(/\.\./g, "_").trim();
96
46
  return safe || "default";
97
47
  }
98
- function activeProfileName(globalRoot) {
48
+ function bootstrapProfileName(globalRoot) {
99
49
  try {
100
50
  const parsed = JSON.parse(fs.readFileSync(path.join(globalRoot, "config.json"), "utf8"));
101
51
  return safeProfileName(
@@ -113,7 +63,7 @@ function wstackGlobalRoot() {
113
63
  function resolveWstackPaths(opts) {
114
64
  const globalRoot = opts.globalRoot ?? (opts.userHome ? path.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
115
65
  const homeDir = opts.userHome ?? os.homedir();
116
- const profileName = safeProfileName(opts.profileName ?? activeProfileName(globalRoot));
66
+ const profileName = safeProfileName(opts.profileName ?? bootstrapProfileName(globalRoot));
117
67
  const profileDir = path.join(globalRoot, "profiles", profileName);
118
68
  const hash = projectHash(opts.projectRoot);
119
69
  const slug = projectSlug(opts.projectRoot);
@@ -183,7 +133,12 @@ function resolveWstackPaths(opts) {
183
133
  projectRequirementIntakes: path.join(projectDir, "requirement-intakes"),
184
134
  syncConfig: path.join(profileDir, "sync.json"),
185
135
  configHistoryDir: path.join(globalRoot, "config-history"),
186
- projectStatus: (projectHash2) => path.join(globalRoot, "projects", projectHash2, "status.json")
136
+ projectStatus: (statusProjectSlug) => {
137
+ if (!/^[a-z0-9](?:[a-z0-9-]{0,39})-[a-f0-9]{6}$/.test(statusProjectSlug)) {
138
+ throw new Error(`Invalid project slug: ${statusProjectSlug}`);
139
+ }
140
+ return path.join(globalRoot, "projects", statusProjectSlug, "status.json");
141
+ }
187
142
  };
188
143
  }
189
144
 
@@ -228,6 +183,8 @@ var ToolCapabilities = {
228
183
  NET_OUTBOUND: "net.outbound",
229
184
  /** Can mutate in-memory session todos only. */
230
185
  SESSION_TODO: "session.todo",
186
+ /** Can park after-task `<nextsteps>` suggestions for the current turn. */
187
+ SESSION_NEXTSTEPS: "session.nextsteps",
231
188
  /** Can mutate in-memory session mode only. */
232
189
  SESSION_MODE: "session.mode",
233
190
  /** Can inspect registered tool metadata. */
@@ -298,6 +255,294 @@ function clampSubagentCapabilities(requested, ceiling = WIDE_SUBAGENT_CAPABILITI
298
255
  };
299
256
  }
300
257
 
258
+ // src/types/runtime-capability-manifest.ts
259
+ var PLAYWRIGHT_ALIASES = {
260
+ playwright_navigate: "browser_navigate",
261
+ playwright_screenshot: "browser_screenshot",
262
+ playwright_click: "browser_click",
263
+ playwright_type: "browser_type",
264
+ playwright_evaluate: "browser_evaluate",
265
+ playwright_select_option: "browser_select",
266
+ playwright_hover: "browser_hover",
267
+ playwright_fill_form: "browser_type",
268
+ playwright_wait_for: "browser_wait",
269
+ playwright_press_key: "browser_press",
270
+ playwright_drag: "browser_drag"
271
+ };
272
+ var RUNTIME_CAPABILITY_MANIFEST = [
273
+ {
274
+ id: "filesystem.read",
275
+ pack: "core",
276
+ exposure: "direct",
277
+ tools: ["read", "grep", "glob", "tree"]
278
+ },
279
+ {
280
+ id: "code.inspect",
281
+ pack: "core",
282
+ exposure: "direct",
283
+ tools: [
284
+ "codebase-stats",
285
+ "codebase-search",
286
+ "codebase-incoming-calls",
287
+ "codebase-outgoing-calls",
288
+ "codebase-index",
289
+ "dead-code-scan",
290
+ "diff",
291
+ "json",
292
+ "logs"
293
+ ]
294
+ },
295
+ {
296
+ id: "filesystem.write",
297
+ pack: "development",
298
+ exposure: "direct",
299
+ tools: ["write", "edit", "replace", "patch"]
300
+ },
301
+ {
302
+ id: "execution.shell",
303
+ pack: "development",
304
+ exposure: "direct",
305
+ tools: ["bash", "exec", "language", "language_info", "language_package"]
306
+ },
307
+ {
308
+ id: "verification.run",
309
+ pack: "development",
310
+ exposure: "direct",
311
+ tools: ["lint", "format", "typecheck", "test", "e2e_plan"]
312
+ },
313
+ {
314
+ id: "version-control.manage",
315
+ pack: "development",
316
+ exposure: "direct",
317
+ tools: ["git"]
318
+ },
319
+ {
320
+ id: "dependencies.manage",
321
+ pack: "development",
322
+ exposure: "on-demand",
323
+ tools: ["install", "outdated", "audit"]
324
+ },
325
+ {
326
+ id: "documentation.author",
327
+ pack: "development",
328
+ exposure: "on-demand",
329
+ tools: ["document", "design", "scaffold"]
330
+ },
331
+ {
332
+ id: "web.research",
333
+ pack: "core",
334
+ exposure: "direct",
335
+ tools: ["search", "fetch"]
336
+ },
337
+ {
338
+ id: "work.plan",
339
+ pack: "core",
340
+ exposure: "direct",
341
+ tools: ["todo", "plan", "task", "kanban", "nextsteps"]
342
+ },
343
+ {
344
+ id: "coordination.mailbox",
345
+ pack: "fleet",
346
+ exposure: "direct",
347
+ tools: ["mailbox", "mail_send", "mail_inbox", "fleet_status"]
348
+ },
349
+ {
350
+ id: "fleet.delegate",
351
+ pack: "fleet",
352
+ exposure: "on-demand",
353
+ tools: [
354
+ "delegate",
355
+ "spawn_subagent",
356
+ "assign_task",
357
+ "kanban_queue",
358
+ "await_tasks",
359
+ "ask_subagent",
360
+ "ask_result",
361
+ "roll_up",
362
+ "quality_gate",
363
+ "terminate_subagent",
364
+ "terminate_all",
365
+ "work_complete",
366
+ "collab_debug",
367
+ "fleet_emit",
368
+ "fleet"
369
+ ]
370
+ },
371
+ {
372
+ id: "reasoning.oneshot",
373
+ pack: "core",
374
+ exposure: "on-demand",
375
+ tools: ["llm", "council"]
376
+ },
377
+ {
378
+ id: "memory.manage",
379
+ pack: "memory",
380
+ exposure: "on-demand",
381
+ tools: ["remember", "search_memory", "find_related_memories", "forget"]
382
+ },
383
+ {
384
+ id: "memory.curate",
385
+ pack: "memory",
386
+ exposure: "on-demand",
387
+ tools: [
388
+ "memory_candidates",
389
+ "memory_update",
390
+ "memory_delete",
391
+ "memory_recover",
392
+ "memory_backfill_recoverable",
393
+ "memory_for_file",
394
+ "memory_for_path",
395
+ "memory_search",
396
+ "memory_graph",
397
+ "memory_gather_batch",
398
+ "memory_verify",
399
+ "memory_hygiene"
400
+ ]
401
+ },
402
+ {
403
+ id: "browser.interact",
404
+ pack: "browser",
405
+ exposure: "on-demand",
406
+ tools: [
407
+ "browser_open",
408
+ "browser_status",
409
+ "browser_list",
410
+ "browser_navigate",
411
+ "browser_snapshot",
412
+ "browser_screenshot",
413
+ "browser_click",
414
+ "browser_type",
415
+ "browser_select",
416
+ "browser_press",
417
+ "browser_hover",
418
+ "browser_drag",
419
+ "browser_wait",
420
+ "browser_evaluate",
421
+ "browser_upload",
422
+ "browser_close"
423
+ ],
424
+ aliases: PLAYWRIGHT_ALIASES
425
+ },
426
+ {
427
+ id: "mcp.dynamic",
428
+ pack: "mcp",
429
+ exposure: "on-demand",
430
+ tools: ["mcp_use"]
431
+ },
432
+ {
433
+ id: "automation.manage",
434
+ pack: "admin",
435
+ exposure: "on-demand",
436
+ tools: ["cron_schedule", "cron_list", "cron_cancel", "watch_start", "watch_list", "watch_stop"]
437
+ },
438
+ {
439
+ id: "context.pin",
440
+ pack: "admin",
441
+ exposure: "on-demand",
442
+ tools: ["pin_add", "pin_list", "pin_remove"]
443
+ },
444
+ {
445
+ id: "quality.analyze",
446
+ pack: "development",
447
+ exposure: "on-demand",
448
+ tools: ["dead_code_scan", "detect_duplicate_code", "secret_scanner_test", "error_lens_history"]
449
+ },
450
+ {
451
+ id: "release.manage",
452
+ pack: "development",
453
+ exposure: "on-demand",
454
+ tools: ["git_autocommit", "semver_current", "semver_bump", "semver_changelog"]
455
+ },
456
+ {
457
+ id: "messaging.telegram",
458
+ pack: "fleet",
459
+ exposure: "on-demand",
460
+ tools: ["telegram_send", "telegram_read", "telegram_approve"]
461
+ },
462
+ {
463
+ id: "tools.discover",
464
+ pack: "admin",
465
+ exposure: "direct",
466
+ tools: ["tool_search", "tool_use", "tool_help", "batch_tool_use"]
467
+ },
468
+ {
469
+ id: "runtime.admin",
470
+ pack: "admin",
471
+ exposure: "internal",
472
+ tools: ["set_working_dir", "context_manager", "mode", "skill", "mcp_control"]
473
+ }
474
+ ];
475
+ var CAPABILITY_BY_ID = new Map(
476
+ RUNTIME_CAPABILITY_MANIFEST.map((definition) => [definition.id, definition])
477
+ );
478
+ var CAPABILITY_BY_TOOL = /* @__PURE__ */ new Map();
479
+ var TOOL_ALIASES = /* @__PURE__ */ new Map();
480
+ for (const definition of RUNTIME_CAPABILITY_MANIFEST) {
481
+ for (const tool of definition.tools) {
482
+ CAPABILITY_BY_TOOL.set(tool, definition.id);
483
+ }
484
+ for (const [alias, tool] of Object.entries(definition.aliases ?? {})) {
485
+ TOOL_ALIASES.set(alias, tool);
486
+ }
487
+ }
488
+ function toolsForRuntimeCapabilities(capabilityIds) {
489
+ return [
490
+ ...new Set(
491
+ capabilityIds.flatMap((id) => {
492
+ const definition = CAPABILITY_BY_ID.get(id);
493
+ if (!definition) throw new Error(`Unknown runtime capability: "${id}"`);
494
+ return definition.tools;
495
+ })
496
+ )
497
+ ];
498
+ }
499
+ function normalizeRuntimeToolName(name) {
500
+ return TOOL_ALIASES.get(name) ?? name;
501
+ }
502
+ function runtimeCapabilityForTool(name) {
503
+ return CAPABILITY_BY_TOOL.get(normalizeRuntimeToolName(name));
504
+ }
505
+ function isKnownRuntimeCapability(id) {
506
+ return CAPABILITY_BY_ID.has(id);
507
+ }
508
+ function inferRuntimeCapabilities(toolNames) {
509
+ const ids = /* @__PURE__ */ new Set();
510
+ for (const rawName of toolNames) {
511
+ const id = runtimeCapabilityForTool(rawName);
512
+ if (id) ids.add(id);
513
+ }
514
+ return [...ids];
515
+ }
516
+ function missingRuntimeCapabilities(required, toolNames) {
517
+ if (!required || required.length === 0) return [];
518
+ const available = new Set(inferRuntimeCapabilities(toolNames));
519
+ return required.filter((id) => !CAPABILITY_BY_ID.has(id) || !available.has(id));
520
+ }
521
+ function missingRequiredRuntimeTools(required, toolNames) {
522
+ if (!required || required.length === 0) return [];
523
+ const available = new Set(toolNames.map(normalizeRuntimeToolName));
524
+ return required.map(normalizeRuntimeToolName).filter((name) => !CAPABILITY_BY_TOOL.has(name) || !available.has(name));
525
+ }
526
+ function runtimeToolReferencesFromText(text) {
527
+ const references = /* @__PURE__ */ new Set();
528
+ for (const match of text.matchAll(/`([a-z][a-z0-9_-]+)`/gi)) {
529
+ const name = normalizeRuntimeToolName(match[1] ?? "");
530
+ if (CAPABILITY_BY_TOOL.has(name)) references.add(name);
531
+ }
532
+ for (const pattern of [
533
+ /\b(?:call|use|invoke|run)\s+`([a-z][a-z0-9_-]+)`/gi,
534
+ /`([a-z][a-z0-9_-]+)`\s+tool\b/gi
535
+ ]) {
536
+ for (const match of text.matchAll(pattern)) {
537
+ references.add(normalizeRuntimeToolName(match[1] ?? ""));
538
+ }
539
+ }
540
+ return [...references];
541
+ }
542
+ function isDeprecatedRuntimeToolAlias(name) {
543
+ return TOOL_ALIASES.has(name) || name.startsWith("mcp__");
544
+ }
545
+
301
546
  // src/coordination/agents/project-agent-config-validation.ts
302
547
  import * as path2 from "node:path";
303
548
  var PROJECT_AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
@@ -437,73 +682,28 @@ function validateProjectAgentAvailability(value) {
437
682
  }
438
683
  }
439
684
 
440
- // src/coordination/agents/project-agent-knowledge-manifests.ts
441
- var BUILT_IN_KNOWLEDGE_MANIFESTS = {
442
- android: {
443
- role: "android",
444
- // Only machine-readable JSON registries belong in liveQueries. Android
445
- // release/API data has no JSON registry endpoint, so those checks stay as
446
- // checklist prose below.
447
- liveQueries: {
448
- kotlin: {
449
- registry: "https://registry.npmjs.org/kotlin/latest",
450
- key: "version",
451
- description: "Kotlin latest stable"
452
- }
453
- },
454
- checklist: [
455
- "Current Android Gradle Plugin version (8.x+)",
456
- "Current Kotlin version (2.x+)",
457
- "Current compileSdk / targetSdk (\u2265 34, check Play Store policy)",
458
- "Current Jetpack Compose stable version",
459
- "All third-party dependencies are at latest compatible minor"
460
- ],
461
- verifyThreshold: 0.5
462
- },
463
- frontend: {
464
- role: "frontend",
465
- liveQueries: {
466
- react: {
467
- registry: "https://registry.npmjs.org/react/latest",
468
- key: "version",
469
- description: "React latest stable"
470
- },
471
- nextjs: {
472
- registry: "https://registry.npmjs.org/next/latest",
473
- key: "version",
474
- description: "Next.js latest stable"
475
- }
476
- },
477
- checklist: [
478
- "Current React version (pinned in package.json)",
479
- "Current Next.js / Vite / framework version",
480
- "Node.js runtime version (project .nvmrc or engines)",
481
- "All devDependencies are at latest compatible versions",
482
- "Browser compatibility targets from browserslist"
483
- ],
484
- verifyThreshold: 0.5
485
- },
486
- executor: {
487
- role: "executor",
488
- liveQueries: {
489
- node: {
490
- registry: "https://registry.npmjs.org/node/latest",
491
- key: "version",
492
- description: "Node.js latest release"
493
- }
494
- },
495
- checklist: [
496
- "Node.js version from .nvmrc or package.engines",
497
- "TypeScript version from package.json",
498
- "Package manager (pnpm \u2265 9) from packageManager",
499
- "Current toolchain: esbuild, vitest, etc."
500
- ],
501
- verifyThreshold: 0.6
502
- }
503
- };
685
+ // src/coordination/agents/project-agent-consolidation.ts
686
+ import {
687
+ existsSync,
688
+ mkdirSync as mkdirSync2,
689
+ readFileSync as readFileSync2,
690
+ rmSync as rmSync2
691
+ } from "node:fs";
692
+ import * as path4 from "node:path";
504
693
 
505
- // src/coordination/agents/project-agent-learning-policy.ts
506
- import { readFileSync as readFileSync2 } from "node:fs";
694
+ // src/coordination/agents/project-agent-learning-entries.ts
695
+ function tokenOverlap(a, b) {
696
+ const setA = new Set(a.split(/\s+/));
697
+ const setB = new Set(b.split(/\s+/));
698
+ const intersect = new Set([...setA].filter((t) => setB.has(t)));
699
+ const union = /* @__PURE__ */ new Set([...setA, ...setB]);
700
+ return union.size === 0 ? 0 : intersect.size / union.size;
701
+ }
702
+ function splitLearnedEntries(body) {
703
+ return body.split(/\n---\n+/).map((entry) => entry.trim()).map(
704
+ (entry) => entry.replace(/^# Learned wisdom for .+$/im, "").replace(/<!--[\s\S]*?-->/g, "").trim()
705
+ ).filter(Boolean);
706
+ }
507
707
 
508
708
  // src/coordination/agents/project-agent-paths.ts
509
709
  import { randomUUID } from "node:crypto";
@@ -547,54 +747,199 @@ function projectAgentProfilePath(role, projectRoot) {
547
747
  return path3.join(roleDir(role, projectRoot), "profile.json");
548
748
  }
549
749
 
550
- // src/coordination/agents/project-agent-learning-policy.ts
551
- var DEFAULT_LEARNING_POLICY = {
552
- enabled: true,
553
- lifetimeCaptureCount: 0
554
- };
555
- function loadProjectAgentLearningPolicy(role, projectRoot) {
750
+ // src/coordination/agents/project-agent-consolidation.ts
751
+ function learnedPath(role, projectRoot) {
752
+ return path4.join(roleDir(role, projectRoot), "learned.md");
753
+ }
754
+ function loadProjectAgentLearnedText(role, projectRoot) {
556
755
  try {
557
- const parsed = JSON.parse(
558
- readFileSync2(learningPolicyPath(role, projectRoot), "utf8")
559
- );
560
- return {
561
- enabled: parsed.enabled !== false,
562
- lifetimeCaptureCount: typeof parsed.lifetimeCaptureCount === "number" && Number.isInteger(parsed.lifetimeCaptureCount) && parsed.lifetimeCaptureCount >= 0 ? parsed.lifetimeCaptureCount : 0,
563
- ...typeof parsed.lastCaptureAt === "string" ? { lastCaptureAt: parsed.lastCaptureAt } : {},
564
- ...parsed.lastCaptureSource === "automatic" || parsed.lastCaptureSource === "manual" || parsed.lastCaptureSource === "taught" ? { lastCaptureSource: parsed.lastCaptureSource } : {}
565
- };
756
+ return readFileSync2(learnedPath(role, projectRoot), "utf8").trim();
566
757
  } catch {
567
- return { ...DEFAULT_LEARNING_POLICY };
758
+ return "";
568
759
  }
569
760
  }
570
- function updateProjectAgentLearningPolicy(role, patch, projectRoot) {
571
- const current = loadProjectAgentLearningPolicy(role, projectRoot);
572
- const updated = {
573
- ...current,
574
- ...patch.enabled !== void 0 ? { enabled: patch.enabled } : {}
575
- };
576
- writeTextAtomically(
577
- learningPolicyPath(role, projectRoot),
578
- `${JSON.stringify(updated, null, 2)}
579
- `
580
- );
581
- return updated;
582
- }
583
-
584
- // src/coordination/agents/project-agent-learning-entries.ts
585
- function tokenOverlap(a, b) {
586
- const setA = new Set(a.split(/\s+/));
587
- const setB = new Set(b.split(/\s+/));
588
- const intersect = new Set([...setA].filter((t) => setB.has(t)));
589
- const union = /* @__PURE__ */ new Set([...setA, ...setB]);
590
- return union.size === 0 ? 0 : intersect.size / union.size;
761
+ function listProjectAgentLearnedTextEntries(role, projectRoot) {
762
+ return splitLearnedEntries(loadProjectAgentLearnedText(role, projectRoot));
591
763
  }
592
- function splitLearnedEntries(body) {
593
- return body.split(/\n---\n+/).map((entry) => entry.trim()).map(
594
- (entry) => entry.replace(/^# Learned wisdom for .+$/im, "").replace(/<!--[\s\S]*?-->/g, "").trim()
595
- ).filter(Boolean);
764
+ function consolidationPath(role, projectRoot) {
765
+ return path4.join(roleDir(role, projectRoot), "consolidated.md");
766
+ }
767
+ function consolidationMetaPath(role, projectRoot) {
768
+ return path4.join(roleDir(role, projectRoot), "consolidation.json");
769
+ }
770
+ function loadProjectAgentConsolidated(role, projectRoot) {
771
+ try {
772
+ return readFileSync2(consolidationPath(role, projectRoot), "utf8").trim();
773
+ } catch {
774
+ return "";
775
+ }
776
+ }
777
+ function loadConsolidationMetadata(role, projectRoot) {
778
+ try {
779
+ const raw = readFileSync2(consolidationMetaPath(role, projectRoot), "utf8");
780
+ const parsed = JSON.parse(raw);
781
+ if (typeof parsed === "object" && parsed !== null && typeof parsed.consolidatedAt === "string" && typeof parsed.sourceEntryCount === "number") {
782
+ return parsed;
783
+ }
784
+ return void 0;
785
+ } catch {
786
+ return void 0;
787
+ }
788
+ }
789
+ function isConsolidated(role, projectRoot) {
790
+ return existsSync(consolidationMetaPath(role, projectRoot));
791
+ }
792
+ function saveProjectAgentConsolidated(role, content, projectRoot, metadata) {
793
+ if (!content.trim()) {
794
+ return consolidationPath(assertProjectAgentRole(role), projectRoot);
795
+ }
796
+ const normalizedRole = assertProjectAgentRole(role);
797
+ const dir = roleDir(normalizedRole, projectRoot);
798
+ mkdirSync2(dir, { recursive: true });
799
+ const fp = consolidationPath(normalizedRole, projectRoot);
800
+ writeTextAtomically(fp, content);
801
+ const rawEntries = listProjectAgentLearnedTextEntries(normalizedRole, projectRoot);
802
+ const rawBytes = Buffer.byteLength(loadProjectAgentLearnedText(normalizedRole, projectRoot), "utf8");
803
+ const meta = {
804
+ consolidatedAt: (/* @__PURE__ */ new Date()).toISOString(),
805
+ sourceEntryCount: rawEntries.length,
806
+ sourceBytes: rawBytes,
807
+ consolidatedBytes: Buffer.byteLength(content, "utf8"),
808
+ trigger: metadata?.trigger ?? "manual",
809
+ ...metadata?.model ? { model: metadata.model } : {}
810
+ };
811
+ writeTextAtomically(
812
+ consolidationMetaPath(normalizedRole, projectRoot),
813
+ `${JSON.stringify(meta, null, 2)}
814
+ `
815
+ );
816
+ return fp;
817
+ }
818
+ function clearProjectAgentConsolidated(role, projectRoot) {
819
+ const normalizedRole = assertProjectAgentRole(role);
820
+ for (const file of [
821
+ consolidationPath(normalizedRole, projectRoot),
822
+ consolidationMetaPath(normalizedRole, projectRoot)
823
+ ]) {
824
+ try {
825
+ rmSync2(file, { force: true });
826
+ } catch {
827
+ }
828
+ }
829
+ }
830
+ function buildConsolidationInstruction(role, projectRoot) {
831
+ const normalizedRole = assertProjectAgentRole(role);
832
+ const rawEntries = listProjectAgentLearnedTextEntries(normalizedRole, projectRoot);
833
+ const existingConsolidation = loadProjectAgentConsolidated(normalizedRole, projectRoot);
834
+ const rawBody = rawEntries.map((entry, i) => `### Entry ${i + 1}
835
+
836
+ ${entry}`).join("\n\n");
837
+ const sections = [
838
+ `You are reviewing and consolidating the captured learning entries for the "${normalizedRole}" agent role in this project.`,
839
+ "",
840
+ "Your task is to synthesize the raw entries below into a single, narrowly-scoped document that represents what this agent has learned \u2014 specifically for its skills and role responsibilities. This document is the **instruction manual for future invocations** of this agent in this project. It is not a journal, not an exhaustive transcription, and not a memory store.",
841
+ "",
842
+ "## Requirements",
843
+ "",
844
+ "1. **Be selective.** Some raw entries are noise \u2014 narrative descriptions of single-session events, ephemeral references (commit SHAs, timestamps, line numbers, PR refs), or thin fragments that cannot be generalized. **Drop them.** The output must be smaller than the input; a consolidation that preserves every word is not a consolidation.",
845
+ '2. **Extract the directive.** When a raw entry mixes narrative framing with a buried lesson ("When I worked on X, I realized Y"), extract the lesson as a directive and discard the framing.',
846
+ "3. **Generic, not specific.** Rewrite entries so they apply across sessions. Replace session-specific anchors (commit SHAs, dates, line numbers) with general principles or concrete tools/commands that will still be valid next month.",
847
+ "4. **Self-contained bullets.** Each bullet must make sense standalone \u2014 understandable to a future agent that has no context about the session that produced it.",
848
+ "5. **Narrow scope.** Keep only knowledge that is genuinely durable and role-relevant. When in doubt, leave it out.",
849
+ "6. **Structured format.** Organize the content under clear markdown headings (##) by topic. Use bullet points for individual facts. Group by category (Conventions, Patterns, Warnings, Project Facts) when it helps scannability.",
850
+ "7. **Preserve actionable anchors.** Keep exact file paths, command names, package names, and configuration values that make a directive concrete and runnable.",
851
+ "8. **No filler.** Do not include meta-commentary about the consolidation process. The document should read as if it were always a single authoritative reference.",
852
+ "",
853
+ rawEntries.length > 0 ? `## Raw learned entries (${rawEntries.length} total)
854
+
855
+ ${rawBody}` : "## Raw learned entries\n\n(No raw entries yet \u2014 return an empty document.)"
856
+ ];
857
+ if (existingConsolidation) {
858
+ sections.push(
859
+ "",
860
+ `## Existing consolidated document (for reference \u2014 improve upon it)
861
+
862
+ ${existingConsolidation}`
863
+ );
864
+ }
865
+ sections.push(
866
+ "",
867
+ "## Output",
868
+ "",
869
+ "Return ONLY the consolidated markdown document. Do not wrap it in code fences or add commentary."
870
+ );
871
+ return {
872
+ instruction: sections.join("\n"),
873
+ rawEntries,
874
+ hasExistingConsolidation: Boolean(existingConsolidation)
875
+ };
596
876
  }
597
877
 
878
+ // src/coordination/agents/project-agent-knowledge-manifests.ts
879
+ var BUILT_IN_KNOWLEDGE_MANIFESTS = {
880
+ android: {
881
+ role: "android",
882
+ // Only machine-readable JSON registries belong in liveQueries. Android
883
+ // release/API data has no JSON registry endpoint, so those checks stay as
884
+ // checklist prose below.
885
+ liveQueries: {
886
+ kotlin: {
887
+ registry: "https://registry.npmjs.org/kotlin/latest",
888
+ key: "version",
889
+ description: "Kotlin latest stable"
890
+ }
891
+ },
892
+ checklist: [
893
+ "Current Android Gradle Plugin version (8.x+)",
894
+ "Current Kotlin version (2.x+)",
895
+ "Current compileSdk / targetSdk (\u2265 34, check Play Store policy)",
896
+ "Current Jetpack Compose stable version",
897
+ "All third-party dependencies are at latest compatible minor"
898
+ ],
899
+ verifyThreshold: 0.5
900
+ },
901
+ frontend: {
902
+ role: "frontend",
903
+ liveQueries: {
904
+ react: {
905
+ registry: "https://registry.npmjs.org/react/latest",
906
+ key: "version",
907
+ description: "React latest stable"
908
+ },
909
+ nextjs: {
910
+ registry: "https://registry.npmjs.org/next/latest",
911
+ key: "version",
912
+ description: "Next.js latest stable"
913
+ }
914
+ },
915
+ checklist: [
916
+ "Current React version (pinned in package.json)",
917
+ "Current Next.js / Vite / framework version",
918
+ "Node.js runtime version (project .nvmrc or engines)",
919
+ "All devDependencies are at latest compatible versions",
920
+ "Browser compatibility targets from browserslist"
921
+ ],
922
+ verifyThreshold: 0.5
923
+ },
924
+ executor: {
925
+ role: "executor",
926
+ liveQueries: {
927
+ node: {
928
+ registry: "https://registry.npmjs.org/node/latest",
929
+ key: "version",
930
+ description: "Node.js latest release"
931
+ }
932
+ },
933
+ checklist: [
934
+ "Node.js version from .nvmrc or package.engines",
935
+ "TypeScript version from package.json",
936
+ "Package manager (pnpm \u2265 9) from packageManager",
937
+ "Current toolchain: esbuild, vitest, etc."
938
+ ],
939
+ verifyThreshold: 0.6
940
+ }
941
+ };
942
+
598
943
  // src/coordination/agents/project-agent-learning-normalize.ts
599
944
  var LEARNED_SOFT_LIMIT = 8192;
600
945
  var LEARNED_HARD_LIMIT = 16384;
@@ -712,6 +1057,41 @@ function normalizeForComparison(text) {
712
1057
  return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim().split(/\s+/).sort().join(" ");
713
1058
  }
714
1059
 
1060
+ // src/coordination/agents/project-agent-learning-policy.ts
1061
+ import { readFileSync as readFileSync3 } from "node:fs";
1062
+ var DEFAULT_LEARNING_POLICY = {
1063
+ enabled: true,
1064
+ lifetimeCaptureCount: 0
1065
+ };
1066
+ function loadProjectAgentLearningPolicy(role, projectRoot) {
1067
+ try {
1068
+ const parsed = JSON.parse(
1069
+ readFileSync3(learningPolicyPath(role, projectRoot), "utf8")
1070
+ );
1071
+ return {
1072
+ enabled: parsed.enabled !== false,
1073
+ lifetimeCaptureCount: typeof parsed.lifetimeCaptureCount === "number" && Number.isInteger(parsed.lifetimeCaptureCount) && parsed.lifetimeCaptureCount >= 0 ? parsed.lifetimeCaptureCount : 0,
1074
+ ...typeof parsed.lastCaptureAt === "string" ? { lastCaptureAt: parsed.lastCaptureAt } : {},
1075
+ ...parsed.lastCaptureSource === "automatic" || parsed.lastCaptureSource === "manual" || parsed.lastCaptureSource === "taught" ? { lastCaptureSource: parsed.lastCaptureSource } : {}
1076
+ };
1077
+ } catch {
1078
+ return { ...DEFAULT_LEARNING_POLICY };
1079
+ }
1080
+ }
1081
+ function updateProjectAgentLearningPolicy(role, patch, projectRoot) {
1082
+ const current = loadProjectAgentLearningPolicy(role, projectRoot);
1083
+ const updated = {
1084
+ ...current,
1085
+ ...patch.enabled !== void 0 ? { enabled: patch.enabled } : {}
1086
+ };
1087
+ writeTextAtomically(
1088
+ learningPolicyPath(role, projectRoot),
1089
+ `${JSON.stringify(updated, null, 2)}
1090
+ `
1091
+ );
1092
+ return updated;
1093
+ }
1094
+
715
1095
  // src/coordination/agents/project-agent-learning-structured.ts
716
1096
  function parseLearnedEntryStamp(entry) {
717
1097
  const structuredMatch = entry.match(
@@ -839,311 +1219,86 @@ function mergeStructuredEntries(existing, fresh) {
839
1219
  };
840
1220
  const merged = existing.filter((entry) => tokenOverlap(entry.key, freshEntry.key) < 0.55);
841
1221
  merged.push(freshEntry);
842
- const order = {
843
- warning: 0,
844
- convention: 1,
845
- pattern: 2,
846
- fact: 3
847
- };
848
- merged.sort((a, b) => {
849
- const cmp = order[a.category] - order[b.category];
850
- if (cmp !== 0) return cmp;
851
- return a.what.localeCompare(b.what);
852
- });
853
- return merged;
854
- }
855
- var SECTION_TITLE = {
856
- warning: "## What to avoid",
857
- convention: "## What to do",
858
- pattern: "## Patterns to follow",
859
- fact: "## Project facts"
860
- };
861
- function renderLearnedInstructions(role, entries, capturedAt) {
862
- const headerLines = [
863
- `# Learned instructions for \`${role}\``,
864
- "",
865
- "> Project-specific learning data for the `" + role + "` agent. Each entry is a directive \u2014 read it as an instruction, not a journal entry. Entries are re-derived on every capture, so this file is always a current, structured snapshot of what this agent has learned.",
866
- ""
867
- ];
868
- if (entries.length === 0) {
869
- return headerLines.join("\n") + [
870
- "_No learned entries yet._",
871
- "",
872
- "---",
873
- `*Last capture: ${capturedAt} \xB7 0 entries*`,
874
- ""
875
- ].join("\n");
876
- }
877
- const buckets = {
878
- warning: [],
879
- convention: [],
880
- pattern: [],
881
- fact: []
882
- };
883
- for (const entry of entries) buckets[entry.category].push(entry);
884
- const sections = [];
885
- for (const category of ["warning", "convention", "pattern", "fact"]) {
886
- const list = buckets[category];
887
- if (list.length === 0) continue;
888
- sections.push(SECTION_TITLE[category]);
889
- sections.push("");
890
- for (const entry of list) {
891
- const stamp = `<!-- learned-stamp: category=${entry.category}; capturedAt=${entry.capturedAt} -->`;
892
- sections.push(stamp);
893
- sections.push(`- **${entry.what}**`);
894
- sections.push(` - *Why:* ${entry.why}`);
895
- if (entry.how) {
896
- for (const line of entry.how.split("\n"))
897
- sections.push(` - *How:* ${line.replace(/^- /, "")}`);
898
- }
899
- sections.push("");
900
- }
901
- }
902
- const footer = ["---", `*Last capture: ${capturedAt} \xB7 ${entries.length} entries*`, ""];
903
- return [...headerLines, ...sections, ...footer].join("\n");
904
- }
905
-
906
- // src/coordination/agents/project-agent-consolidation.ts
907
- import {
908
- existsSync,
909
- mkdirSync as mkdirSync2,
910
- readFileSync as readFileSync3,
911
- rmSync as rmSync2
912
- } from "node:fs";
913
- import * as path4 from "node:path";
914
- function learnedPath(role, projectRoot) {
915
- return path4.join(roleDir(role, projectRoot), "learned.md");
916
- }
917
- function loadProjectAgentLearnedText(role, projectRoot) {
918
- try {
919
- return readFileSync3(learnedPath(role, projectRoot), "utf8").trim();
920
- } catch {
921
- return "";
922
- }
923
- }
924
- function listProjectAgentLearnedTextEntries(role, projectRoot) {
925
- return splitLearnedEntries(loadProjectAgentLearnedText(role, projectRoot));
926
- }
927
- function consolidationPath(role, projectRoot) {
928
- return path4.join(roleDir(role, projectRoot), "consolidated.md");
929
- }
930
- function consolidationMetaPath(role, projectRoot) {
931
- return path4.join(roleDir(role, projectRoot), "consolidation.json");
932
- }
933
- function loadProjectAgentConsolidated(role, projectRoot) {
934
- try {
935
- return readFileSync3(consolidationPath(role, projectRoot), "utf8").trim();
936
- } catch {
937
- return "";
938
- }
939
- }
940
- function loadConsolidationMetadata(role, projectRoot) {
941
- try {
942
- const raw = readFileSync3(consolidationMetaPath(role, projectRoot), "utf8");
943
- const parsed = JSON.parse(raw);
944
- if (typeof parsed === "object" && parsed !== null && typeof parsed.consolidatedAt === "string" && typeof parsed.sourceEntryCount === "number") {
945
- return parsed;
946
- }
947
- return void 0;
948
- } catch {
949
- return void 0;
950
- }
951
- }
952
- function isConsolidated(role, projectRoot) {
953
- return existsSync(consolidationMetaPath(role, projectRoot));
954
- }
955
- function saveProjectAgentConsolidated(role, content, projectRoot, metadata) {
956
- if (!content.trim()) {
957
- return consolidationPath(assertProjectAgentRole(role), projectRoot);
958
- }
959
- const normalizedRole = assertProjectAgentRole(role);
960
- const dir = roleDir(normalizedRole, projectRoot);
961
- mkdirSync2(dir, { recursive: true });
962
- const fp = consolidationPath(normalizedRole, projectRoot);
963
- writeTextAtomically(fp, content);
964
- const rawEntries = listProjectAgentLearnedTextEntries(normalizedRole, projectRoot);
965
- const rawBytes = Buffer.byteLength(loadProjectAgentLearnedText(normalizedRole, projectRoot), "utf8");
966
- const meta = {
967
- consolidatedAt: (/* @__PURE__ */ new Date()).toISOString(),
968
- sourceEntryCount: rawEntries.length,
969
- sourceBytes: rawBytes,
970
- consolidatedBytes: Buffer.byteLength(content, "utf8"),
971
- trigger: metadata?.trigger ?? "manual",
972
- ...metadata?.model ? { model: metadata.model } : {}
973
- };
974
- writeTextAtomically(
975
- consolidationMetaPath(normalizedRole, projectRoot),
976
- `${JSON.stringify(meta, null, 2)}
977
- `
978
- );
979
- return fp;
980
- }
981
- function clearProjectAgentConsolidated(role, projectRoot) {
982
- const normalizedRole = assertProjectAgentRole(role);
983
- for (const file of [
984
- consolidationPath(normalizedRole, projectRoot),
985
- consolidationMetaPath(normalizedRole, projectRoot)
986
- ]) {
987
- try {
988
- rmSync2(file, { force: true });
989
- } catch {
990
- }
991
- }
992
- }
993
- function buildConsolidationInstruction(role, projectRoot) {
994
- const normalizedRole = assertProjectAgentRole(role);
995
- const rawEntries = listProjectAgentLearnedTextEntries(normalizedRole, projectRoot);
996
- const existingConsolidation = loadProjectAgentConsolidated(normalizedRole, projectRoot);
997
- const rawBody = rawEntries.map((entry, i) => `### Entry ${i + 1}
998
-
999
- ${entry}`).join("\n\n");
1000
- const sections = [
1001
- `You are reviewing and consolidating the captured learning entries for the "${normalizedRole}" agent role in this project.`,
1002
- "",
1003
- "Your task is to synthesize the raw entries below into a single, narrowly-scoped document that represents what this agent has learned \u2014 specifically for its skills and role responsibilities. This document is the **instruction manual for future invocations** of this agent in this project. It is not a journal, not an exhaustive transcription, and not a memory store.",
1004
- "",
1005
- "## Requirements",
1006
- "",
1007
- "1. **Be selective.** Some raw entries are noise \u2014 narrative descriptions of single-session events, ephemeral references (commit SHAs, timestamps, line numbers, PR refs), or thin fragments that cannot be generalized. **Drop them.** The output must be smaller than the input; a consolidation that preserves every word is not a consolidation.",
1008
- '2. **Extract the directive.** When a raw entry mixes narrative framing with a buried lesson ("When I worked on X, I realized Y"), extract the lesson as a directive and discard the framing.',
1009
- "3. **Generic, not specific.** Rewrite entries so they apply across sessions. Replace session-specific anchors (commit SHAs, dates, line numbers) with general principles or concrete tools/commands that will still be valid next month.",
1010
- "4. **Self-contained bullets.** Each bullet must make sense standalone \u2014 understandable to a future agent that has no context about the session that produced it.",
1011
- "5. **Narrow scope.** Keep only knowledge that is genuinely durable and role-relevant. When in doubt, leave it out.",
1012
- "6. **Structured format.** Organize the content under clear markdown headings (##) by topic. Use bullet points for individual facts. Group by category (Conventions, Patterns, Warnings, Project Facts) when it helps scannability.",
1013
- "7. **Preserve actionable anchors.** Keep exact file paths, command names, package names, and configuration values that make a directive concrete and runnable.",
1014
- "8. **No filler.** Do not include meta-commentary about the consolidation process. The document should read as if it were always a single authoritative reference.",
1015
- "",
1016
- rawEntries.length > 0 ? `## Raw learned entries (${rawEntries.length} total)
1017
-
1018
- ${rawBody}` : "## Raw learned entries\n\n(No raw entries yet \u2014 return an empty document.)"
1019
- ];
1020
- if (existingConsolidation) {
1021
- sections.push(
1022
- "",
1023
- `## Existing consolidated document (for reference \u2014 improve upon it)
1024
-
1025
- ${existingConsolidation}`
1026
- );
1027
- }
1028
- sections.push(
1029
- "",
1030
- "## Output",
1031
- "",
1032
- "Return ONLY the consolidated markdown document. Do not wrap it in code fences or add commentary."
1033
- );
1034
- return {
1035
- instruction: sections.join("\n"),
1036
- rawEntries,
1037
- hasExistingConsolidation: Boolean(existingConsolidation)
1038
- };
1039
- }
1040
-
1041
- // src/coordination/agents/project-agent-profile.ts
1042
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync4 } from "node:fs";
1043
- import * as path5 from "node:path";
1044
- function slugifyProjectAgentRole(name) {
1045
- const slug = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "").slice(0, 96);
1046
- return assertProjectAgentRole(slug);
1047
- }
1048
- function loadProjectAgentProfile(role, projectRoot) {
1049
- try {
1050
- const parsed = JSON.parse(
1051
- readFileSync4(projectAgentProfilePath(role, projectRoot), "utf8")
1052
- );
1053
- const normalizedRole = assertProjectAgentRole(parsed.role ?? role);
1054
- const baseRole = assertProjectAgentRole(parsed.baseRole ?? "generic");
1055
- if (normalizedRole !== assertProjectAgentRole(role)) return void 0;
1056
- if (typeof parsed.name !== "string" || !parsed.name.trim()) return void 0;
1057
- if (typeof parsed.purpose !== "string" || !parsed.purpose.trim()) return void 0;
1058
- if (!Array.isArray(parsed.taskTypes) || parsed.taskTypes.some((item) => typeof item !== "string")) {
1059
- return void 0;
1060
- }
1061
- const createdAt = typeof parsed.createdAt === "string" ? parsed.createdAt : (/* @__PURE__ */ new Date(0)).toISOString();
1062
- const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : createdAt;
1063
- return {
1064
- role: normalizedRole,
1065
- name: parsed.name.trim(),
1066
- baseRole,
1067
- purpose: parsed.purpose.trim(),
1068
- taskTypes: parsed.taskTypes.map((item) => item.trim()).filter(Boolean),
1069
- createdAt,
1070
- updatedAt
1071
- };
1072
- } catch {
1073
- return void 0;
1074
- }
1075
- }
1076
- function createProjectAgent(input, projectRoot) {
1077
- const name = input.name.trim();
1078
- const purpose = input.purpose.trim();
1079
- if (!name || name.length > 120) throw new Error("Project agent name must be 1-120 characters.");
1080
- if (purpose.length < 10 || purpose.length > 4e3) {
1081
- throw new Error("Project agent purpose must be 10-4000 characters.");
1082
- }
1083
- const role = assertProjectAgentRole(
1084
- (input.role?.trim() || slugifyProjectAgentRole(name)).toLowerCase()
1085
- );
1086
- const baseRole = assertProjectAgentRole((input.baseRole?.trim() || "generic").toLowerCase());
1087
- const taskTypes = [...new Set(input.taskTypes.map((item) => item.trim()).filter(Boolean))];
1088
- if (taskTypes.length === 0 || taskTypes.length > 32 || taskTypes.some((item) => item.length > 160)) {
1089
- throw new Error("Project agent requires 1-32 task descriptions, each at most 160 characters.");
1090
- }
1091
- const dir = roleDir(role, projectRoot);
1092
- if (existsSync2(dir) && readdirSync(dir).length > 0) {
1093
- throw new Error(`Project agent role "${role}" already exists.`);
1094
- }
1095
- const now = (/* @__PURE__ */ new Date()).toISOString();
1096
- const profile = {
1097
- role,
1098
- name,
1099
- baseRole,
1100
- purpose,
1101
- taskTypes,
1102
- createdAt: now,
1103
- updatedAt: now
1104
- };
1105
- const identity = [
1106
- `# ${name}`,
1107
- "",
1108
- `You are the project-specific "${name}" agent (role: ${role}).`,
1109
- "",
1110
- "## Purpose",
1111
- "",
1112
- purpose,
1113
- "",
1114
- "## Primary task types",
1115
- "",
1116
- ...taskTypes.map((item) => `- ${item}`),
1222
+ const order = {
1223
+ warning: 0,
1224
+ convention: 1,
1225
+ pattern: 2,
1226
+ fact: 3
1227
+ };
1228
+ merged.sort((a, b) => {
1229
+ const cmp = order[a.category] - order[b.category];
1230
+ if (cmp !== 0) return cmp;
1231
+ return a.what.localeCompare(b.what);
1232
+ });
1233
+ return merged;
1234
+ }
1235
+ var SECTION_TITLE = {
1236
+ warning: "## What to avoid",
1237
+ convention: "## What to do",
1238
+ pattern: "## Patterns to follow",
1239
+ fact: "## Project facts"
1240
+ };
1241
+ function renderLearnedInstructions(role, entries, capturedAt) {
1242
+ const headerLines = [
1243
+ `# Learned instructions for \`${role}\``,
1117
1244
  "",
1118
- "Learn durable project conventions from completed work, keep conclusions evidence-based, and stay within this assigned purpose.",
1245
+ "> Project-specific learning data for the `" + role + "` agent. Each entry is a directive \u2014 read it as an instruction, not a journal entry. Entries are re-derived on every capture, so this file is always a current, structured snapshot of what this agent has learned.",
1119
1246
  ""
1120
- ].join("\n");
1121
- mkdirSync3(dir, { recursive: true });
1122
- writeTextAtomically(path5.join(dir, "identity.md"), identity);
1123
- writeTextAtomically(
1124
- projectAgentProfilePath(role, projectRoot),
1125
- `${JSON.stringify(profile, null, 2)}
1126
- `
1127
- );
1128
- return profile;
1247
+ ];
1248
+ if (entries.length === 0) {
1249
+ return headerLines.join("\n") + [
1250
+ "_No learned entries yet._",
1251
+ "",
1252
+ "---",
1253
+ `*Last capture: ${capturedAt} \xB7 0 entries*`,
1254
+ ""
1255
+ ].join("\n");
1256
+ }
1257
+ const buckets = {
1258
+ warning: [],
1259
+ convention: [],
1260
+ pattern: [],
1261
+ fact: []
1262
+ };
1263
+ for (const entry of entries) buckets[entry.category].push(entry);
1264
+ const sections = [];
1265
+ for (const category of ["warning", "convention", "pattern", "fact"]) {
1266
+ const list = buckets[category];
1267
+ if (list.length === 0) continue;
1268
+ sections.push(SECTION_TITLE[category]);
1269
+ sections.push("");
1270
+ for (const entry of list) {
1271
+ const stamp = `<!-- learned-stamp: category=${entry.category}; capturedAt=${entry.capturedAt} -->`;
1272
+ sections.push(stamp);
1273
+ sections.push(`- **${entry.what}**`);
1274
+ sections.push(` - *Why:* ${entry.why}`);
1275
+ if (entry.how) {
1276
+ for (const line of entry.how.split("\n"))
1277
+ sections.push(` - *How:* ${line.replace(/^- /, "")}`);
1278
+ }
1279
+ sections.push("");
1280
+ }
1281
+ }
1282
+ const footer = ["---", `*Last capture: ${capturedAt} \xB7 ${entries.length} entries*`, ""];
1283
+ return [...headerLines, ...sections, ...footer].join("\n");
1129
1284
  }
1130
1285
 
1131
1286
  // src/coordination/agents/project-agent-files.ts
1132
1287
  import {
1133
- existsSync as existsSync3,
1134
- mkdirSync as mkdirSync4,
1135
- readdirSync as readdirSync2,
1136
- readFileSync as readFileSync5,
1288
+ existsSync as existsSync2,
1289
+ mkdirSync as mkdirSync3,
1290
+ readdirSync,
1291
+ readFileSync as readFileSync4,
1137
1292
  rmSync as rmSync3
1138
1293
  } from "node:fs";
1139
- import * as path6 from "node:path";
1294
+ import * as path5 from "node:path";
1140
1295
  function updateProjectAgentLearned(role, content, projectRoot, mode = "append") {
1141
1296
  const dir = roleDir(role, projectRoot);
1142
- mkdirSync4(dir, { recursive: true });
1143
- const filePath = path6.join(dir, "learned.md");
1297
+ mkdirSync3(dir, { recursive: true });
1298
+ const filePath = path5.join(dir, "learned.md");
1144
1299
  const existing = (() => {
1145
1300
  try {
1146
- return readFileSync5(filePath, "utf8");
1301
+ return readFileSync4(filePath, "utf8");
1147
1302
  } catch {
1148
1303
  return "";
1149
1304
  }
@@ -1158,24 +1313,24 @@ ${content.trimStart()}` : content;
1158
1313
  }
1159
1314
  function updateProjectAgentIdentity(role, content, projectRoot) {
1160
1315
  const dir = roleDir(role, projectRoot);
1161
- mkdirSync4(dir, { recursive: true });
1162
- const filePath = path6.join(dir, "identity.md");
1316
+ mkdirSync3(dir, { recursive: true });
1317
+ const filePath = path5.join(dir, "identity.md");
1163
1318
  writeTextAtomically(filePath, content);
1164
1319
  return filePath;
1165
1320
  }
1166
1321
  function updateProjectAgentConfig(role, config, projectRoot) {
1167
1322
  const validated = validateProjectAgentConfig(config);
1168
1323
  const dir = roleDir(role, projectRoot);
1169
- mkdirSync4(dir, { recursive: true });
1170
- const filePath = path6.join(dir, "config.json");
1324
+ mkdirSync3(dir, { recursive: true });
1325
+ const filePath = path5.join(dir, "config.json");
1171
1326
  writeTextAtomically(filePath, `${JSON.stringify(validated, null, 2)}
1172
1327
  `);
1173
1328
  return filePath;
1174
1329
  }
1175
1330
  function updateProjectAgentKnowledge(role, manifest, projectRoot) {
1176
1331
  const dir = roleDir(role, projectRoot);
1177
- mkdirSync4(dir, { recursive: true });
1178
- const filePath = path6.join(dir, "knowledge.json");
1332
+ mkdirSync3(dir, { recursive: true });
1333
+ const filePath = path5.join(dir, "knowledge.json");
1179
1334
  writeTextAtomically(filePath, `${JSON.stringify(manifest, null, 2)}
1180
1335
  `);
1181
1336
  return filePath;
@@ -1184,14 +1339,14 @@ function resetProjectAgentIdentity(role, projectRoot) {
1184
1339
  const removed = [];
1185
1340
  if (!role || role === "*") {
1186
1341
  const dir2 = agentsDir(projectRoot);
1187
- if (existsSync3(dir2)) {
1342
+ if (existsSync2(dir2)) {
1188
1343
  rmSync3(dir2, { recursive: true, force: true });
1189
1344
  removed.push(dir2);
1190
1345
  }
1191
1346
  return removed;
1192
1347
  }
1193
1348
  const dir = roleDir(role, projectRoot);
1194
- if (existsSync3(dir)) {
1349
+ if (existsSync2(dir)) {
1195
1350
  rmSync3(dir, { recursive: true, force: true });
1196
1351
  removed.push(dir);
1197
1352
  }
@@ -1199,20 +1354,20 @@ function resetProjectAgentIdentity(role, projectRoot) {
1199
1354
  }
1200
1355
  function refreshProjectAgentIdentity(role, projectRoot) {
1201
1356
  const dir = roleDir(role, projectRoot);
1202
- mkdirSync4(dir, { recursive: true });
1357
+ mkdirSync3(dir, { recursive: true });
1203
1358
  for (const file of ["learned.md", "identity.md", "consolidated.md", "consolidation.json"]) {
1204
- const fp = path6.join(dir, file);
1359
+ const fp = path5.join(dir, file);
1205
1360
  try {
1206
1361
  rmSync3(fp, { force: true });
1207
1362
  } catch {
1208
1363
  }
1209
1364
  }
1210
1365
  writeTextAtomically(
1211
- path6.join(dir, "learned.md"),
1366
+ path5.join(dir, "learned.md"),
1212
1367
  renderLearnedInstructions(role, [], (/* @__PURE__ */ new Date()).toISOString())
1213
1368
  );
1214
1369
  writeTextAtomically(
1215
- path6.join(dir, "identity.md"),
1370
+ path5.join(dir, "identity.md"),
1216
1371
  `# Project identity for ${role}
1217
1372
 
1218
1373
  <!-- Describe how this agent should behave in the context of this project. -->
@@ -1223,9 +1378,9 @@ function refreshProjectAgentIdentity(role, projectRoot) {
1223
1378
  function listProjectAgentRoles(projectRoot) {
1224
1379
  const dir = agentsDir(projectRoot);
1225
1380
  try {
1226
- return readdirSync2(dir).filter((name) => {
1381
+ return readdirSync(dir).filter((name) => {
1227
1382
  if (!isProjectAgentRoleName(name)) return false;
1228
- const sub = path6.join(dir, name);
1383
+ const sub = path5.join(dir, name);
1229
1384
  try {
1230
1385
  return [
1231
1386
  "learned.md",
@@ -1236,7 +1391,7 @@ function listProjectAgentRoles(projectRoot) {
1236
1391
  "profile.json",
1237
1392
  "consolidated.md",
1238
1393
  "consolidation.json"
1239
- ].some((file) => existsSync3(path6.join(sub, file)));
1394
+ ].some((file) => existsSync2(path5.join(sub, file)));
1240
1395
  } catch {
1241
1396
  return false;
1242
1397
  }
@@ -1246,6 +1401,96 @@ function listProjectAgentRoles(projectRoot) {
1246
1401
  }
1247
1402
  }
1248
1403
 
1404
+ // src/coordination/agents/project-agent-profile.ts
1405
+ import { existsSync as existsSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "node:fs";
1406
+ import * as path6 from "node:path";
1407
+ function slugifyProjectAgentRole(name) {
1408
+ const slug = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "").slice(0, 96);
1409
+ return assertProjectAgentRole(slug);
1410
+ }
1411
+ function loadProjectAgentProfile(role, projectRoot) {
1412
+ try {
1413
+ const parsed = JSON.parse(
1414
+ readFileSync5(projectAgentProfilePath(role, projectRoot), "utf8")
1415
+ );
1416
+ const normalizedRole = assertProjectAgentRole(parsed.role ?? role);
1417
+ const baseRole = assertProjectAgentRole(parsed.baseRole ?? "generic");
1418
+ if (normalizedRole !== assertProjectAgentRole(role)) return void 0;
1419
+ if (typeof parsed.name !== "string" || !parsed.name.trim()) return void 0;
1420
+ if (typeof parsed.purpose !== "string" || !parsed.purpose.trim()) return void 0;
1421
+ if (!Array.isArray(parsed.taskTypes) || parsed.taskTypes.some((item) => typeof item !== "string")) {
1422
+ return void 0;
1423
+ }
1424
+ const createdAt = typeof parsed.createdAt === "string" ? parsed.createdAt : (/* @__PURE__ */ new Date(0)).toISOString();
1425
+ const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : createdAt;
1426
+ return {
1427
+ role: normalizedRole,
1428
+ name: parsed.name.trim(),
1429
+ baseRole,
1430
+ purpose: parsed.purpose.trim(),
1431
+ taskTypes: parsed.taskTypes.map((item) => item.trim()).filter(Boolean),
1432
+ createdAt,
1433
+ updatedAt
1434
+ };
1435
+ } catch {
1436
+ return void 0;
1437
+ }
1438
+ }
1439
+ function createProjectAgent(input, projectRoot) {
1440
+ const name = input.name.trim();
1441
+ const purpose = input.purpose.trim();
1442
+ if (!name || name.length > 120) throw new Error("Project agent name must be 1-120 characters.");
1443
+ if (purpose.length < 10 || purpose.length > 4e3) {
1444
+ throw new Error("Project agent purpose must be 10-4000 characters.");
1445
+ }
1446
+ const role = assertProjectAgentRole(
1447
+ (input.role?.trim() || slugifyProjectAgentRole(name)).toLowerCase()
1448
+ );
1449
+ const baseRole = assertProjectAgentRole((input.baseRole?.trim() || "generic").toLowerCase());
1450
+ const taskTypes = [...new Set(input.taskTypes.map((item) => item.trim()).filter(Boolean))];
1451
+ if (taskTypes.length === 0 || taskTypes.length > 32 || taskTypes.some((item) => item.length > 160)) {
1452
+ throw new Error("Project agent requires 1-32 task descriptions, each at most 160 characters.");
1453
+ }
1454
+ const dir = roleDir(role, projectRoot);
1455
+ if (existsSync3(dir) && readdirSync2(dir).length > 0) {
1456
+ throw new Error(`Project agent role "${role}" already exists.`);
1457
+ }
1458
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1459
+ const profile = {
1460
+ role,
1461
+ name,
1462
+ baseRole,
1463
+ purpose,
1464
+ taskTypes,
1465
+ createdAt: now,
1466
+ updatedAt: now
1467
+ };
1468
+ const identity = [
1469
+ `# ${name}`,
1470
+ "",
1471
+ `You are the project-specific "${name}" agent (role: ${role}).`,
1472
+ "",
1473
+ "## Purpose",
1474
+ "",
1475
+ purpose,
1476
+ "",
1477
+ "## Primary task types",
1478
+ "",
1479
+ ...taskTypes.map((item) => `- ${item}`),
1480
+ "",
1481
+ "Learn durable project conventions from completed work, keep conclusions evidence-based, and stay within this assigned purpose.",
1482
+ ""
1483
+ ].join("\n");
1484
+ mkdirSync4(dir, { recursive: true });
1485
+ writeTextAtomically(path6.join(dir, "identity.md"), identity);
1486
+ writeTextAtomically(
1487
+ projectAgentProfilePath(role, projectRoot),
1488
+ `${JSON.stringify(profile, null, 2)}
1489
+ `
1490
+ );
1491
+ return profile;
1492
+ }
1493
+
1249
1494
  // src/coordination/agents/project-agent-identity.ts
1250
1495
  function createProjectAgentRoster(baseRoster, projectRoot) {
1251
1496
  const target = { ...baseRoster };
@@ -1341,6 +1586,7 @@ function applyProjectAgentConfig(base, projectConfig, options = {}) {
1341
1586
  const result = { ...base };
1342
1587
  if (projectConfig.tools !== void 0) {
1343
1588
  result.tools = options.protectSystemRole ? base.tools === void 0 ? void 0 : [.../* @__PURE__ */ new Set([...base.tools, ...projectConfig.tools])] : [...projectConfig.tools];
1589
+ result.capabilities = inferRuntimeCapabilities(result.tools ?? []);
1344
1590
  }
1345
1591
  if (projectConfig.skillNames !== void 0) result.skillNames = [...projectConfig.skillNames];
1346
1592
  if (projectConfig.provider !== void 0) result.provider = projectConfig.provider;
@@ -1941,6 +2187,64 @@ function isDirectory(candidate) {
1941
2187
  }
1942
2188
  }
1943
2189
 
2190
+ // src/coordination/agents/types.ts
2191
+ var HOUR = 60 * 60 * 1e3;
2192
+ var LIGHT_BUDGET = {
2193
+ timeoutMs: 3 * HOUR,
2194
+ maxIterations: 3e3,
2195
+ maxToolCalls: 8e3
2196
+ };
2197
+ var MEDIUM_BUDGET = {
2198
+ timeoutMs: 5 * HOUR,
2199
+ maxIterations: 5e3,
2200
+ maxToolCalls: 14e3
2201
+ };
2202
+ var HEAVY_BUDGET = {
2203
+ timeoutMs: 10 * HOUR,
2204
+ maxIterations: 8e3,
2205
+ maxToolCalls: 2e4
2206
+ };
2207
+ var TOOLS = {
2208
+ /** Pure read/inspect — safe for analysis and review agents. */
2209
+ read: ["read", "grep", "glob", "search", "tree", "mailbox"],
2210
+ /** Read + structured inspection (logs, diffs, json, dependency audit). */
2211
+ inspect: ["read", "grep", "glob", "search", "tree", "json", "diff", "logs", "audit", "mailbox"],
2212
+ /** Read + edit (no shell). For agents that write code/docs but don't run it. */
2213
+ write: ["read", "grep", "glob", "search", "tree", "write", "edit", "replace", "patch", "mailbox"],
2214
+ /** Full build loop: edit + run (lint/format/typecheck/test/bash). */
2215
+ build: [
2216
+ "read",
2217
+ "grep",
2218
+ "glob",
2219
+ "search",
2220
+ "tree",
2221
+ "write",
2222
+ "edit",
2223
+ "replace",
2224
+ "patch",
2225
+ "bash",
2226
+ "exec",
2227
+ "lint",
2228
+ "format",
2229
+ "typecheck",
2230
+ "test",
2231
+ "mailbox"
2232
+ ],
2233
+ /** Version control. */
2234
+ vcs: ["read", "grep", "glob", "git", "diff"],
2235
+ /** Dependency management + CVE audit. */
2236
+ deps: ["read", "grep", "glob", "install", "outdated", "audit", "json", "mailbox"],
2237
+ /** Documentation authoring. */
2238
+ docs: ["read", "grep", "glob", "search", "tree", "write", "edit", "document", "mailbox"],
2239
+ /** Web research. */
2240
+ research: ["read", "grep", "glob", "search", "fetch", "mailbox"]
2241
+ };
2242
+ var SPECIALIST_TOOLS = {
2243
+ browser: toolsForRuntimeCapabilities(["browser.interact"]),
2244
+ memory: toolsForRuntimeCapabilities(["memory.manage"]),
2245
+ mcp: toolsForRuntimeCapabilities(["mcp.dynamic"])
2246
+ };
2247
+
1944
2248
  // src/coordination/agents/phase1-discovery.ts
1945
2249
  var DISCOVERY_AGENTS = [
1946
2250
  {
@@ -1976,7 +2280,7 @@ var DISCOVERY_AGENTS = [
1976
2280
  id: "search",
1977
2281
  name: "Search",
1978
2282
  role: "search",
1979
- tools: [...TOOLS.read],
2283
+ tools: [...TOOLS.read, "codebase-search"],
1980
2284
  prompt: agentPrompt("search")
1981
2285
  },
1982
2286
  budget: MEDIUM_BUDGET,
@@ -2438,14 +2742,7 @@ var WAVE1_AGENTS = [
2438
2742
  capability: {
2439
2743
  phase: "domain",
2440
2744
  summary: "Android platform specialist: Kotlin, Jetpack Compose, Android SDK, lifecycle and Play Store delivery.",
2441
- keywords: [
2442
- ...ANDROID_META.signals,
2443
- "android",
2444
- "kotlin",
2445
- "compose",
2446
- "gradle",
2447
- "playstore"
2448
- ]
2745
+ keywords: [...ANDROID_META.signals, "android", "kotlin", "compose", "gradle", "playstore"]
2449
2746
  }
2450
2747
  },
2451
2748
  {
@@ -2460,14 +2757,7 @@ var WAVE1_AGENTS = [
2460
2757
  capability: {
2461
2758
  phase: "domain",
2462
2759
  summary: "Desktop application specialist: Electron or Tauri, native packaging, IPC and OS integration.",
2463
- keywords: [
2464
- ...DESKTOP_META.signals,
2465
- "desktop",
2466
- "electron",
2467
- "tauri",
2468
- "ipc",
2469
- "tray"
2470
- ]
2760
+ keywords: [...DESKTOP_META.signals, "desktop", "electron", "tauri", "ipc", "tray"]
2471
2761
  }
2472
2762
  },
2473
2763
  {
@@ -2543,7 +2833,7 @@ var WAVE1_AGENTS = [
2543
2833
  id: "platform-engineer",
2544
2834
  name: "Platform Engineer",
2545
2835
  role: "platform-engineer",
2546
- tools: [...TOOLS.build, "install", "git", "node-modern"],
2836
+ tools: [...TOOLS.build, "install", "git"],
2547
2837
  prompt: agentPrompt("platform-engineer")
2548
2838
  },
2549
2839
  budget: MEDIUM_BUDGET,
@@ -2787,8 +3077,7 @@ var WAVE2_AGENTS = [
2787
3077
  id: "resilience-engineer",
2788
3078
  name: "Resilience Engineer",
2789
3079
  role: "resilience-engineer",
2790
- tools: [...TOOLS.build, "logs", "observability"],
2791
- // uniqueness guaranteed by 'observability' vs chaos-engineer's 'test'
3080
+ tools: [...TOOLS.build, "logs"],
2792
3081
  prompt: agentPrompt("resilience-engineer")
2793
3082
  },
2794
3083
  budget: MEDIUM_BUDGET,
@@ -2811,8 +3100,7 @@ var WAVE2_AGENTS = [
2811
3100
  id: "chaos-engineer",
2812
3101
  name: "Chaos Engineer",
2813
3102
  role: "chaos-engineer",
2814
- tools: [...TOOLS.build, "logs", "observability"],
2815
- // uniqueness guaranteed by 'observability' vs chaos-engineer's 'test'
3103
+ tools: [...TOOLS.build, "logs"],
2816
3104
  prompt: agentPrompt("chaos-engineer")
2817
3105
  },
2818
3106
  budget: MEDIUM_BUDGET,
@@ -2953,21 +3241,7 @@ var VERIFY_AGENTS = [
2953
3241
  id: "e2e",
2954
3242
  name: "E2E",
2955
3243
  role: "e2e",
2956
- tools: [
2957
- ...TOOLS.build,
2958
- "fetch",
2959
- "playwright_navigate",
2960
- "playwright_screenshot",
2961
- "playwright_click",
2962
- "playwright_type",
2963
- "playwright_evaluate",
2964
- "playwright_select_option",
2965
- "playwright_hover",
2966
- "playwright_fill_form",
2967
- "playwright_wait_for",
2968
- "playwright_press_key",
2969
- "playwright_drag"
2970
- ],
3244
+ tools: [...TOOLS.build, "fetch", ...SPECIALIST_TOOLS.browser],
2971
3245
  prompt: agentPrompt("e2e")
2972
3246
  },
2973
3247
  budget: HEAVY_BUDGET,
@@ -3002,21 +3276,7 @@ var VERIFY_AGENTS = [
3002
3276
  id: "browser",
3003
3277
  name: "Browser",
3004
3278
  role: "browser",
3005
- tools: [
3006
- ...TOOLS.read,
3007
- "fetch",
3008
- "playwright_navigate",
3009
- "playwright_screenshot",
3010
- "playwright_click",
3011
- "playwright_type",
3012
- "playwright_evaluate",
3013
- "playwright_select_option",
3014
- "playwright_hover",
3015
- "playwright_fill_form",
3016
- "playwright_wait_for",
3017
- "playwright_press_key",
3018
- "playwright_drag"
3019
- ],
3279
+ tools: [...TOOLS.read, "fetch", ...SPECIALIST_TOOLS.browser],
3020
3280
  prompt: agentPrompt("browser")
3021
3281
  },
3022
3282
  budget: MEDIUM_BUDGET,
@@ -3717,27 +3977,7 @@ var DELIVERY_AGENTS = [
3717
3977
  id: "devops",
3718
3978
  name: "DevOps",
3719
3979
  role: "devops",
3720
- tools: [
3721
- ...TOOLS.build,
3722
- "mcp__ssh__ssh_list_servers",
3723
- "mcp__ssh__ssh_connection_status",
3724
- "mcp__ssh__ssh_execute",
3725
- "mcp__ssh__ssh_execute_sudo",
3726
- "mcp__ssh__ssh_upload",
3727
- "mcp__ssh__ssh_download",
3728
- "mcp__ssh__ssh_sync",
3729
- "mcp__ssh__ssh_deploy",
3730
- "mcp__ssh__ssh_health_check",
3731
- "mcp__ssh__ssh_service_status",
3732
- "mcp__ssh__ssh_process_manager",
3733
- "mcp__ssh__ssh_tunnel",
3734
- "mcp__ssh__ssh_backup_create",
3735
- "mcp__ssh__ssh_backup_list",
3736
- "mcp__ssh__ssh_backup_restore",
3737
- "mcp__ssh__ssh_db_list",
3738
- "mcp__ssh__ssh_db_query",
3739
- "mcp__ssh__ssh_profile"
3740
- ],
3980
+ tools: [...TOOLS.build, ...SPECIALIST_TOOLS.mcp],
3741
3981
  prompt: agentPrompt("devops")
3742
3982
  },
3743
3983
  budget: MEDIUM_BUDGET,
@@ -3938,7 +4178,7 @@ var WAVE3_AGENTS = [
3938
4178
  id: "messaging",
3939
4179
  name: "Messaging",
3940
4180
  role: "messaging",
3941
- tools: [...TOOLS.build, "logs", "observability"],
4181
+ tools: [...TOOLS.build, "logs"],
3942
4182
  prompt: agentPrompt("messaging")
3943
4183
  },
3944
4184
  budget: HEAVY_BUDGET,
@@ -4379,7 +4619,7 @@ var WAVE4_AGENTS = [
4379
4619
  id: "memory-curator",
4380
4620
  name: "Memory Curator",
4381
4621
  role: "memory-curator",
4382
- tools: [...TOOLS.read, "memory"],
4622
+ tools: [...TOOLS.read, ...SPECIALIST_TOOLS.memory],
4383
4623
  prompt: agentPrompt("memory-curator")
4384
4624
  },
4385
4625
  budget: LIGHT_BUDGET,
@@ -4401,7 +4641,7 @@ var WAVE4_AGENTS = [
4401
4641
  id: "fleet-coordinator",
4402
4642
  name: "Fleet Coordinator",
4403
4643
  role: "fleet-coordinator",
4404
- tools: [...TOOLS.read, "plan", "multi-agent"],
4644
+ tools: [...TOOLS.read, "plan"],
4405
4645
  prompt: agentPrompt("fleet-coordinator")
4406
4646
  },
4407
4647
  budget: MEDIUM_BUDGET,
@@ -4527,8 +4767,20 @@ var ROLE_SKILL_SETS = {
4527
4767
  cost: skillSet("audit-log", "tech-stack", "observability", "research-web"),
4528
4768
  "tech-stack": skillSet("tech-stack", "research-web", "security-scanner", "node-modern"),
4529
4769
  // ── Wave 1: Platform and systems engineering ──────────────────────────
4530
- android: skillSet("react-modern", "typescript-strict", "testing", "security-scanner", "tech-stack"),
4531
- desktop: skillSet("node-modern", "typescript-strict", "testing", "security-scanner", "tech-stack"),
4770
+ android: skillSet(
4771
+ "react-modern",
4772
+ "typescript-strict",
4773
+ "testing",
4774
+ "security-scanner",
4775
+ "tech-stack"
4776
+ ),
4777
+ desktop: skillSet(
4778
+ "node-modern",
4779
+ "typescript-strict",
4780
+ "testing",
4781
+ "security-scanner",
4782
+ "tech-stack"
4783
+ ),
4532
4784
  realtime: skillSet(
4533
4785
  "api-design",
4534
4786
  "observability",
@@ -4632,26 +4884,9 @@ var ROLE_SKILL_SETS = {
4632
4884
  "security-scanner",
4633
4885
  "tech-stack"
4634
4886
  ),
4635
- "benchmark-engineer": skillSet(
4636
- "observability",
4637
- "testing",
4638
- "output-standards",
4639
- "tech-stack"
4640
- ),
4641
- "memory-curator": skillSet(
4642
- "mnemosyne",
4643
- "audit-log",
4644
- "testing",
4645
- "output-standards",
4646
- "tech-stack"
4647
- ),
4648
- "fleet-coordinator": skillSet(
4649
- "multi-agent",
4650
- "sdd",
4651
- "output-standards",
4652
- "testing",
4653
- "tech-stack"
4654
- ),
4887
+ "benchmark-engineer": skillSet("observability", "testing", "output-standards", "tech-stack"),
4888
+ "memory-curator": skillSet("mnemosyne", "audit-log", "testing", "output-standards", "tech-stack"),
4889
+ "fleet-coordinator": skillSet("multi-agent", "sdd", "output-standards", "testing", "tech-stack"),
4655
4890
  // ── Wave 2: Security, resilience, governance and agent platform ────────
4656
4891
  "threat-modeler": skillSet(
4657
4892
  "security-scanner",
@@ -4703,6 +4938,7 @@ var SHADOW_AGENT_SKILLS = skillSet(
4703
4938
  "observability",
4704
4939
  "security-scanner"
4705
4940
  );
4941
+ var MAX_EAGER_ROSTER_SKILLS = 3;
4706
4942
  function assignSkillsToAgents(definitions) {
4707
4943
  return definitions.map((definition) => {
4708
4944
  const role = definition.config.role;
@@ -4716,7 +4952,8 @@ function assignSkillsToAgents(definitions) {
4716
4952
  ...definition,
4717
4953
  config: {
4718
4954
  ...definition.config,
4719
- skillNames: [...skills]
4955
+ capabilities: inferRuntimeCapabilities(definition.config.tools ?? []),
4956
+ skillNames: skills.slice(0, MAX_EAGER_ROSTER_SKILLS)
4720
4957
  }
4721
4958
  };
4722
4959
  });
@@ -4789,7 +5026,9 @@ export {
4789
5026
  PLANNING_AGENTS,
4790
5027
  REVIEW_AGENTS,
4791
5028
  ROLE_SKILL_SETS,
5029
+ RUNTIME_CAPABILITY_MANIFEST,
4792
5030
  SHADOW_AGENT_SKILLS,
5031
+ SPECIALIST_TOOLS,
4793
5032
  TOOLS,
4794
5033
  VERIFY_AGENTS,
4795
5034
  WAVE1_AGENTS,
@@ -4822,7 +5061,10 @@ export {
4822
5061
  getAgentDefinition,
4823
5062
  getProjectAgentLearnStats,
4824
5063
  hintLearnedNeedsSummarization,
5064
+ inferRuntimeCapabilities,
4825
5065
  isConsolidated,
5066
+ isDeprecatedRuntimeToolAlias,
5067
+ isKnownRuntimeCapability,
4826
5068
  listProjectAgentLearnedEntries,
4827
5069
  listProjectAgentRoles,
4828
5070
  loadConsolidationMetadata,
@@ -4834,14 +5076,20 @@ export {
4834
5076
  loadProjectAgentProfile,
4835
5077
  loadRoleKnowledgeManifest,
4836
5078
  mergeStructuredEntries,
5079
+ missingRequiredRuntimeTools,
5080
+ missingRuntimeCapabilities,
4837
5081
  normalizeLearnedEntry,
5082
+ normalizeRuntimeToolName,
4838
5083
  parseLearnedEntryStamp,
4839
5084
  parseStructuredLearnedEntries,
4840
5085
  refreshProjectAgentIdentity,
4841
5086
  renderLearnedInstructions,
4842
5087
  resetProjectAgentIdentity,
5088
+ runtimeCapabilityForTool,
5089
+ runtimeToolReferencesFromText,
4843
5090
  saveProjectAgentConsolidated,
4844
5091
  slugifyProjectAgentRole,
5092
+ toolsForRuntimeCapabilities,
4845
5093
  updateProjectAgentConfig,
4846
5094
  updateProjectAgentIdentity,
4847
5095
  updateProjectAgentKnowledge,