opencode-swarm 7.14.0 → 7.16.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 (34) hide show
  1. package/README.md +4 -3
  2. package/dist/cli/index.js +2114 -553
  3. package/dist/commands/close.d.ts +14 -1
  4. package/dist/commands/registry.d.ts +3 -3
  5. package/dist/commands/turbo.d.ts +4 -4
  6. package/dist/config/constants.d.ts +12 -1
  7. package/dist/config/index.d.ts +2 -2
  8. package/dist/config/schema.d.ts +116 -0
  9. package/dist/index.js +5288 -2128
  10. package/dist/parallel/file-locks.d.ts +50 -2
  11. package/dist/services/skill-improver.d.ts +1 -0
  12. package/dist/services/status-service.d.ts +29 -0
  13. package/dist/state.d.ts +17 -0
  14. package/dist/tools/index.d.ts +6 -0
  15. package/dist/tools/lean-turbo-acquire-locks.d.ts +36 -0
  16. package/dist/tools/lean-turbo-plan-lanes.d.ts +35 -0
  17. package/dist/tools/lean-turbo-review.d.ts +34 -0
  18. package/dist/tools/lean-turbo-run-phase.d.ts +44 -0
  19. package/dist/tools/lean-turbo-runner-status.d.ts +36 -0
  20. package/dist/tools/lean-turbo-status.d.ts +44 -0
  21. package/dist/tools/tool-names.d.ts +1 -1
  22. package/dist/tools/update-task-status.d.ts +7 -4
  23. package/dist/turbo/lean/conflicts.d.ts +100 -0
  24. package/dist/turbo/lean/evidence.d.ts +91 -0
  25. package/dist/turbo/lean/index.d.ts +27 -0
  26. package/dist/turbo/lean/integration.d.ts +137 -0
  27. package/dist/turbo/lean/phase-ready.d.ts +105 -0
  28. package/dist/turbo/lean/planner.d.ts +115 -0
  29. package/dist/turbo/lean/reviewer.d.ts +124 -0
  30. package/dist/turbo/lean/risk.d.ts +47 -0
  31. package/dist/turbo/lean/runner.d.ts +322 -0
  32. package/dist/turbo/lean/state.d.ts +61 -0
  33. package/dist/turbo/lean/task-completion.d.ts +53 -0
  34. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -34,7 +34,7 @@ var package_default;
34
34
  var init_package = __esm(() => {
35
35
  package_default = {
36
36
  name: "opencode-swarm",
37
- version: "7.14.0",
37
+ version: "7.16.0",
38
38
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
39
39
  main: "dist/index.js",
40
40
  types: "dist/index.d.ts",
@@ -16061,7 +16061,13 @@ var init_tool_names = __esm(() => {
16061
16061
  "skill_inspect",
16062
16062
  "skill_improve",
16063
16063
  "spec_write",
16064
- "knowledge_ack"
16064
+ "knowledge_ack",
16065
+ "lean_turbo_plan_lanes",
16066
+ "lean_turbo_acquire_locks",
16067
+ "lean_turbo_runner_status",
16068
+ "lean_turbo_review",
16069
+ "lean_turbo_run_phase",
16070
+ "lean_turbo_status"
16065
16071
  ];
16066
16072
  TOOL_NAME_SET = new Set(TOOL_NAMES);
16067
16073
  });
@@ -16100,7 +16106,7 @@ function freezeSet(items) {
16100
16106
  });
16101
16107
  return proxy;
16102
16108
  }
16103
- var QA_AGENTS, PIPELINE_AGENTS, ORCHESTRATOR_NAME = "architect", ALL_SUBAGENT_NAMES, ALL_AGENT_NAMES, OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, AGENT_TOOL_MAP, DEFAULT_AGENT_CONFIGS;
16109
+ var QA_AGENTS, PIPELINE_AGENTS, ORCHESTRATOR_NAME = "architect", ALL_SUBAGENT_NAMES, ALL_AGENT_NAMES, OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, AGENT_TOOL_MAP, TOOL_DESCRIPTIONS, DEFAULT_AGENT_CONFIGS;
16104
16110
  var init_constants = __esm(() => {
16105
16111
  init_tool_names();
16106
16112
  QA_AGENTS = ["reviewer", "critic", "critic_oversight"];
@@ -16307,7 +16313,13 @@ var init_constants = __esm(() => {
16307
16313
  "skill_apply",
16308
16314
  "skill_inspect",
16309
16315
  "skill_improve",
16310
- "knowledge_ack"
16316
+ "knowledge_ack",
16317
+ "lean_turbo_plan_lanes",
16318
+ "lean_turbo_acquire_locks",
16319
+ "lean_turbo_runner_status",
16320
+ "lean_turbo_review",
16321
+ "lean_turbo_run_phase",
16322
+ "lean_turbo_status"
16311
16323
  ],
16312
16324
  explorer: [
16313
16325
  "complexity_hotspots",
@@ -16504,6 +16516,81 @@ var init_constants = __esm(() => {
16504
16516
  "spec_write"
16505
16517
  ]
16506
16518
  };
16519
+ TOOL_DESCRIPTIONS = {
16520
+ symbols: "code symbol search",
16521
+ checkpoint: "state snapshots",
16522
+ diff: "structured git diff with contract change detection",
16523
+ diff_summary: "filter classified AST changes by category, risk level, or file for reviewer drill-down",
16524
+ imports: "dependency audit",
16525
+ lint: "code quality",
16526
+ placeholder_scan: "todo and FIXME comment detection",
16527
+ secretscan: "secret detection",
16528
+ sast_scan: "static analysis security scan",
16529
+ syntax_check: "syntax validation",
16530
+ test_runner: "auto-detect and run tests",
16531
+ test_impact: "identify test files impacted by changed source files via import analysis",
16532
+ mutation_test: "executes pre-generated mutation patches against tests, evaluates kill rate against quality gate thresholds",
16533
+ generate_mutants: "generate LLM-based mutation testing patches for source files; returns MutationPatch[] for direct consumption by the mutation_test tool",
16534
+ write_mutation_evidence: "write mutation gate evidence for a completed phase; normalizes PASS/WARN/FAIL/SKIP verdicts and writes .swarm/evidence/{phase}/mutation-gate.json",
16535
+ pkg_audit: "dependency vulnerability scan \u2014 npm/pip/cargo",
16536
+ complexity_hotspots: "git churn \xD7 complexity risk map",
16537
+ schema_drift: "OpenAPI spec vs route drift",
16538
+ todo_extract: "structured TODO/FIXME extraction",
16539
+ evidence_check: "verify task evidence completeness",
16540
+ sbom_generate: "SBOM generation for dependency inventory",
16541
+ build_check: "build verification",
16542
+ quality_budget: "code quality budget check",
16543
+ pre_check_batch: "parallel verification: lint:check + secretscan + sast_scan + quality_budget",
16544
+ update_task_status: "mark tasks complete, track phase progress",
16545
+ write_retro: "document phase retrospectives via phase_complete workflow, capture lessons learned",
16546
+ write_drift_evidence: "write drift verification evidence for a completed phase",
16547
+ write_hallucination_evidence: "write hallucination verification evidence for a completed phase",
16548
+ write_final_council_evidence: "write final council evidence for project completion",
16549
+ declare_scope: "declare file scope for next coder delegation",
16550
+ phase_complete: "mark a phase as complete and track dispatched agents",
16551
+ save_plan: "save a structured implementation plan",
16552
+ doc_scan: "scan project documentation files and build an index manifest",
16553
+ doc_extract: "extract actionable constraints from project documentation",
16554
+ curator_analyze: "run curator phase analysis and optionally apply knowledge recommendations",
16555
+ knowledge_add: "store a new lesson in the knowledge base",
16556
+ knowledge_recall: "search the knowledge base for relevant past decisions",
16557
+ knowledge_remove: "delete an outdated swarm knowledge entry by ID (swarm tier only)",
16558
+ knowledge_query: "query swarm or hive knowledge with optional filters",
16559
+ co_change_analyzer: "detect hidden couplings by analyzing git history",
16560
+ check_gate_status: "check the gate status of a specific task",
16561
+ completion_verify: "verify completed tasks have required evidence",
16562
+ submit_council_verdicts: "submit pre-collected council member verdicts for synthesis (architect MUST dispatch critic/reviewer/sme/test_engineer/explorer as Agent tasks first; this tool synthesizes only, it does not contact members)",
16563
+ submit_phase_council_verdicts: "submit pre-collected phase-level council member verdicts for holistic phase synthesis (architect MUST dispatch all 5 council members with phase-scoped context first; this tool synthesizes only, it does not contact members)",
16564
+ declare_council_criteria: "pre-declare acceptance criteria for a task before the coder starts work; criteria are read back during council evaluation",
16565
+ detect_domains: "detect which SME domains are relevant for a given text",
16566
+ extract_code_blocks: "extract code blocks from text content and save them to files",
16567
+ gitingest: "fetch a GitHub repository full content via gitingest.com",
16568
+ retrieve_summary: "retrieve the full content of a stored tool output summary",
16569
+ search: "Workspace-scoped ripgrep-style text search with structured JSON output. Supports literal and regex modes, glob filtering, and result limits. NOTE: This is text search, not structural AST search \u2014 use symbols and imports tools for structural queries.",
16570
+ web_search: "External web search (Tavily or Brave) for architect-driven council research. Returns titled results with snippets and URLs. Config-gated on council.general.enabled; requires a search API key. Used by the architect in MODE: COUNCIL to gather a RESEARCH CONTEXT before dispatching council agents.",
16571
+ convene_general_council: "Synthesize responses from a multi-model General Council. Accepts parallel member responses (Round 1, optionally Round 2), detects disagreements, and returns consensus points, persisting disagreements, and a structured synthesis. Architect-only. Config-gated on council.general.enabled.",
16572
+ batch_symbols: "Batched symbol extraction across multiple files. Returns per-file symbol summaries with isolated error handling.",
16573
+ suggest_patch: "Reviewer-safe structured patch suggestion tool. Produces context-anchored patch artifacts without file modification. Returns structured diagnostics on context mismatch.",
16574
+ lint_spec: "validate .swarm/spec.md format and required fields",
16575
+ get_approved_plan: "retrieve the last critic-approved immutable plan snapshot for baseline drift comparison",
16576
+ repo_map: "query the repo code graph: importers, dependencies, blast radius, and localization context for structural awareness before refactoring",
16577
+ get_qa_gate_profile: "retrieve the QA gate profile for the current plan: gates (reviewer, test_engineer, sme_enabled, critic_pre_plan, sast_enabled, council_mode, hallucination_guard, mutation_test, council_general_review, drift_check, final_council), lock state, and profile hash. Read-only.",
16578
+ set_qa_gates: "configure the QA gate profile for the current plan. Architect-only. Ratchet-tighter only \u2014 rejected once the profile is locked after critic approval. Supports: reviewer, test_engineer, sme_enabled, critic_pre_plan, sast_enabled, council_mode, hallucination_guard, mutation_test, council_general_review, drift_check, final_council.",
16579
+ req_coverage: "query requirement coverage status for tracked functional requirements",
16580
+ skill_generate: "compile knowledge entries into a structured SKILL.md draft",
16581
+ skill_list: "list generated skill files and their status",
16582
+ skill_apply: "activate a draft skill proposal",
16583
+ skill_inspect: "inspect the content and source entries of a skill file",
16584
+ skill_improve: "run the skill_improver agent to review and refine skills",
16585
+ spec_write: "author or update .swarm/spec.md for the current project",
16586
+ knowledge_ack: "record an explicit KNOWLEDGE_APPLIED/IGNORED/VIOLATED acknowledgment",
16587
+ lean_turbo_plan_lanes: "partition phase tasks into parallel lanes based on file-scope conflicts for Lean Turbo execution",
16588
+ lean_turbo_acquire_locks: "acquire file locks for all files in a lane (all-or-nothing) before lane execution",
16589
+ lean_turbo_runner_status: "read Lean Turbo run state from .swarm/turbo-state.json",
16590
+ lean_turbo_review: "dispatch a read-only reviewer agent to evaluate a completed Lean Turbo phase",
16591
+ lean_turbo_run_phase: "Execute a phase using Lean Turbo parallel lane execution. " + "Plans lanes, acquires file locks, and dispatches coder agents concurrently. " + "Use when Lean Turbo is active and you want to execute all tasks in a phase in parallel lanes.",
16592
+ lean_turbo_status: "returns Lean Turbo configuration and active status for the current session"
16593
+ };
16507
16594
  for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
16508
16595
  const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
16509
16596
  if (invalidTools.length > 0) {
@@ -16607,7 +16694,7 @@ function getCanonicalAgentRole(agentName, generatedAgentNames) {
16607
16694
  function stripKnownSwarmPrefix(agentName) {
16608
16695
  return getCanonicalAgentRole(agentName);
16609
16696
  }
16610
- var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, KnowledgeApplicationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, PluginConfigSchema;
16697
+ var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, KnowledgeApplicationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, PluginConfigSchema;
16611
16698
  var init_schema = __esm(() => {
16612
16699
  init_zod();
16613
16700
  init_constants();
@@ -17194,6 +17281,31 @@ var init_schema = __esm(() => {
17194
17281
  max_coders: exports_external.number().int().min(1).max(16).default(3),
17195
17282
  max_reviewers: exports_external.number().int().min(1).max(16).default(2)
17196
17283
  });
17284
+ LeanTurboConfigSchema = exports_external.object({
17285
+ max_parallel_coders: exports_external.number().int().min(1).max(6).default(4),
17286
+ require_declared_scope: exports_external.boolean().default(true),
17287
+ conflict_policy: exports_external.enum(["serialize", "degrade"]).default("serialize"),
17288
+ degrade_on_risk: exports_external.boolean().default(true),
17289
+ phase_reviewer: exports_external.boolean().default(true),
17290
+ phase_critic: exports_external.boolean().default(true),
17291
+ integrated_diff_required: exports_external.boolean().default(true),
17292
+ allow_docs_only_without_reviewer: exports_external.boolean().default(false),
17293
+ worktree_isolation: exports_external.boolean().default(false).refine((val) => val === false, {
17294
+ message: "worktree_isolation: true is not yet implemented. Use false (the default) for in-repo parallel execution. Full worktree isolation will be available in a future release."
17295
+ })
17296
+ });
17297
+ StandardTurboConfigSchema = exports_external.object({
17298
+ strategy: exports_external.literal("standard"),
17299
+ lean: LeanTurboConfigSchema.optional()
17300
+ });
17301
+ LeanTurboStrategyConfigSchema = exports_external.object({
17302
+ strategy: exports_external.literal("lean"),
17303
+ lean: LeanTurboConfigSchema
17304
+ });
17305
+ TurboConfigSchema = exports_external.discriminatedUnion("strategy", [
17306
+ StandardTurboConfigSchema,
17307
+ LeanTurboStrategyConfigSchema
17308
+ ]);
17197
17309
  PluginConfigSchema = exports_external.object({
17198
17310
  agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
17199
17311
  default_agent: exports_external.string().optional().transform((v) => {
@@ -17253,6 +17365,7 @@ var init_schema = __esm(() => {
17253
17365
  prm: PrmConfigSchema.optional(),
17254
17366
  council: CouncilConfigSchema.optional(),
17255
17367
  parallelization: ParallelizationConfigSchema.optional(),
17368
+ turbo: TurboConfigSchema.optional(),
17256
17369
  turbo_mode: exports_external.boolean().default(false).optional(),
17257
17370
  quiet: exports_external.boolean().default(true).optional(),
17258
17371
  version_check: exports_external.boolean().default(true).optional(),
@@ -19372,8 +19485,17 @@ function getLockFilePath(directory, filePath) {
19372
19485
  if (!pathOk) {
19373
19486
  throw new Error("Invalid file path: path traversal not allowed");
19374
19487
  }
19375
- const hash2 = Buffer.from(normalized).toString("base64").replace(/[/+=]/g, "_");
19376
- return path6.join(directory, LOCKS_DIR, `${hash2}.lock`);
19488
+ const pathForHash = process.platform === "win32" ? normalized.toLowerCase() : normalized;
19489
+ const hash2 = Buffer.from(pathForHash).toString("base64").replace(/[/+=]/g, "_");
19490
+ const lockPath = path6.join(directory, LOCKS_DIR, `${hash2}.lock`);
19491
+ if (process.platform === "win32") {
19492
+ const oldHash = Buffer.from(normalized).toString("base64").replace(/[/+=]/g, "_");
19493
+ const oldLockPath = path6.join(directory, LOCKS_DIR, `${oldHash}.lock`);
19494
+ if (fs3.existsSync(oldLockPath)) {
19495
+ return oldLockPath;
19496
+ }
19497
+ }
19498
+ return lockPath;
19377
19499
  }
19378
19500
  async function tryAcquireLock(directory, filePath, agent, taskId) {
19379
19501
  const lockPath = getLockFilePath(directory, filePath);
@@ -19408,10 +19530,14 @@ async function tryAcquireLock(directory, filePath, agent, taskId) {
19408
19530
  };
19409
19531
  return { acquired: true, lock };
19410
19532
  }
19411
- var import_proper_lockfile, LOCKS_DIR = ".swarm/locks", LOCK_TIMEOUT_MS;
19533
+ var import_proper_lockfile, LOCKS_DIR = ".swarm/locks", LOCK_TIMEOUT_MS, _internals4;
19412
19534
  var init_file_locks = __esm(() => {
19413
19535
  import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
19414
19536
  LOCK_TIMEOUT_MS = 5 * 60 * 1000;
19537
+ _internals4 = {
19538
+ tryAcquireLock,
19539
+ writeFile: fs3.promises.writeFile
19540
+ };
19415
19541
  });
19416
19542
 
19417
19543
  // src/evidence/lock.ts
@@ -19634,7 +19760,7 @@ async function loadEvidence(directory, taskId) {
19634
19760
  return { status: "invalid_schema", errors: ["Invalid JSON"] };
19635
19761
  }
19636
19762
  if (isFlatRetrospective(parsed)) {
19637
- const wrappedBundle = _internals4.wrapFlatRetrospective(parsed, sanitizedTaskId);
19763
+ const wrappedBundle = _internals5.wrapFlatRetrospective(parsed, sanitizedTaskId);
19638
19764
  try {
19639
19765
  const validated = EvidenceBundleSchema.parse(wrappedBundle);
19640
19766
  try {
@@ -19730,14 +19856,14 @@ async function checkRequirementCoverage(phase, directory) {
19730
19856
  }
19731
19857
  }
19732
19858
  async function archiveEvidence(directory, maxAgeDays, maxBundles) {
19733
- const taskIds = await _internals4.listEvidenceTaskIds(directory);
19859
+ const taskIds = await _internals5.listEvidenceTaskIds(directory);
19734
19860
  const cutoffDate = new Date;
19735
19861
  cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
19736
19862
  const cutoffIso = cutoffDate.toISOString();
19737
19863
  const archived = [];
19738
19864
  const remainingBundles = [];
19739
19865
  for (const taskId of taskIds) {
19740
- const result = await _internals4.loadEvidence(directory, taskId);
19866
+ const result = await _internals5.loadEvidence(directory, taskId);
19741
19867
  if (result.status !== "found") {
19742
19868
  continue;
19743
19869
  }
@@ -19765,7 +19891,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles) {
19765
19891
  }
19766
19892
  return archived;
19767
19893
  }
19768
- var VALID_EVIDENCE_TYPES, sanitizeTaskId2, LEGACY_TASK_COMPLEXITY_MAP, _internals4;
19894
+ var VALID_EVIDENCE_TYPES, sanitizeTaskId2, LEGACY_TASK_COMPLEXITY_MAP, _internals5;
19769
19895
  var init_manager2 = __esm(() => {
19770
19896
  init_zod();
19771
19897
  init_evidence_schema();
@@ -19795,7 +19921,7 @@ var init_manager2 = __esm(() => {
19795
19921
  medium: "moderate",
19796
19922
  high: "complex"
19797
19923
  };
19798
- _internals4 = {
19924
+ _internals5 = {
19799
19925
  wrapFlatRetrospective,
19800
19926
  loadEvidence,
19801
19927
  listEvidenceTaskIds
@@ -20006,7 +20132,7 @@ function getProfile(directory, planId) {
20006
20132
  return row ? rowToProfile(row) : null;
20007
20133
  }
20008
20134
  function getOrCreateProfile(directory, planId, projectType) {
20009
- const existing = _internals5.getProfile(directory, planId);
20135
+ const existing = _internals6.getProfile(directory, planId);
20010
20136
  if (existing)
20011
20137
  return existing;
20012
20138
  const db = getProjectDb(directory);
@@ -20022,14 +20148,14 @@ function getOrCreateProfile(directory, planId, projectType) {
20022
20148
  throw err;
20023
20149
  }
20024
20150
  }
20025
- const after = _internals5.getProfile(directory, planId);
20151
+ const after = _internals6.getProfile(directory, planId);
20026
20152
  if (!after) {
20027
20153
  throw new Error(`Failed to create or load QA gate profile for plan_id=${planId}`);
20028
20154
  }
20029
20155
  return after;
20030
20156
  }
20031
20157
  function setGates(directory, planId, gates) {
20032
- const current = _internals5.getProfile(directory, planId);
20158
+ const current = _internals6.getProfile(directory, planId);
20033
20159
  if (!current) {
20034
20160
  throw new Error(`No QA gate profile found for plan_id=${planId} \u2014 call getOrCreateProfile first`);
20035
20161
  }
@@ -20053,7 +20179,7 @@ function setGates(directory, planId, gates) {
20053
20179
  JSON.stringify(merged),
20054
20180
  planId
20055
20181
  ]);
20056
- const updated = _internals5.getProfile(directory, planId);
20182
+ const updated = _internals6.getProfile(directory, planId);
20057
20183
  if (!updated) {
20058
20184
  throw new Error(`Failed to re-read QA gate profile after update for plan_id=${planId}`);
20059
20185
  }
@@ -20075,10 +20201,10 @@ function getEffectiveGates(profile, sessionOverrides) {
20075
20201
  }
20076
20202
  return merged;
20077
20203
  }
20078
- var _internals5, DEFAULT_QA_GATES;
20204
+ var _internals6, DEFAULT_QA_GATES;
20079
20205
  var init_qa_gate_profile = __esm(() => {
20080
20206
  init_project_db();
20081
- _internals5 = {
20207
+ _internals6 = {
20082
20208
  getProfile,
20083
20209
  getOrCreateProfile,
20084
20210
  setGates,
@@ -20813,6 +20939,30 @@ function hasActiveTurboMode(sessionID) {
20813
20939
  }
20814
20940
  return false;
20815
20941
  }
20942
+ function hasActiveFullAuto(sessionID) {
20943
+ if (sessionID) {
20944
+ const session = swarmState.agentSessions.get(sessionID);
20945
+ return session?.fullAutoMode === true;
20946
+ }
20947
+ for (const [_sessionId, session] of swarmState.agentSessions) {
20948
+ if (session.fullAutoMode === true) {
20949
+ return true;
20950
+ }
20951
+ }
20952
+ return false;
20953
+ }
20954
+ function hasActiveLeanTurbo(sessionID) {
20955
+ if (sessionID) {
20956
+ const session = swarmState.agentSessions.get(sessionID);
20957
+ return session?.turboMode === true && session?.turboStrategy === "lean" && session?.leanTurboActive === true;
20958
+ }
20959
+ for (const [_sessionId, session] of swarmState.agentSessions) {
20960
+ if (session.turboMode === true && session.turboStrategy === "lean" && session.leanTurboActive === true) {
20961
+ return true;
20962
+ }
20963
+ }
20964
+ return false;
20965
+ }
20816
20966
  var _rehydrationCache = null, _councilDisagreementWarned, _toolAggregates, defaultRunContext, _runContexts, swarmState;
20817
20967
  var init_state = __esm(() => {
20818
20968
  init_constants();
@@ -21440,8 +21590,8 @@ function getElementAtPath2(obj, path9) {
21440
21590
  }
21441
21591
  function promiseAllObject2(promisesObj) {
21442
21592
  const keys = Object.keys(promisesObj);
21443
- const promises = keys.map((key) => promisesObj[key]);
21444
- return Promise.all(promises).then((results) => {
21593
+ const promises2 = keys.map((key) => promisesObj[key]);
21594
+ return Promise.all(promises2).then((results) => {
21445
21595
  const resolvedObj = {};
21446
21596
  for (let i = 0;i < keys.length; i++) {
21447
21597
  resolvedObj[keys[i]] = results[i];
@@ -34401,7 +34551,7 @@ function resetToRemoteBranch(cwd, options) {
34401
34551
  const prunedBranches = [];
34402
34552
  try {
34403
34553
  const currentBranch = getCurrentBranch(cwd);
34404
- const defaultRemoteBranch = _internals6.detectDefaultRemoteBranch(cwd);
34554
+ const defaultRemoteBranch = _internals7.detectDefaultRemoteBranch(cwd);
34405
34555
  if (!defaultRemoteBranch) {
34406
34556
  return {
34407
34557
  success: false,
@@ -34583,7 +34733,7 @@ function resetToRemoteBranch(cwd, options) {
34583
34733
  function resetToMainAfterMerge(cwd, options) {
34584
34734
  const warnings = [];
34585
34735
  try {
34586
- const defaultBranch = _internals6.detectDefaultRemoteBranch(cwd);
34736
+ const defaultBranch = _internals7.detectDefaultRemoteBranch(cwd);
34587
34737
  if (!defaultBranch) {
34588
34738
  return {
34589
34739
  success: false,
@@ -34610,7 +34760,7 @@ function resetToMainAfterMerge(cwd, options) {
34610
34760
  }
34611
34761
  if (currentBranch === defaultBranch) {
34612
34762
  try {
34613
- const logOutput = _internals6.gitExec(["log", `${targetBranch}..HEAD`, "--oneline"], cwd);
34763
+ const logOutput = _internals7.gitExec(["log", `${targetBranch}..HEAD`, "--oneline"], cwd);
34614
34764
  if (logOutput.trim().length > 0) {
34615
34765
  return {
34616
34766
  success: false,
@@ -34625,11 +34775,11 @@ function resetToMainAfterMerge(cwd, options) {
34625
34775
  } catch {}
34626
34776
  } else {
34627
34777
  try {
34628
- _internals6.gitExec(["rev-parse", "--abbrev-ref", `${currentBranch}@{upstream}`], cwd);
34778
+ _internals7.gitExec(["rev-parse", "--abbrev-ref", `${currentBranch}@{upstream}`], cwd);
34629
34779
  } catch {
34630
34780
  try {
34631
- const localSha = _internals6.gitExec(["rev-parse", "HEAD"], cwd).trim();
34632
- const remoteSha = _internals6.gitExec(["rev-parse", targetBranch], cwd).trim();
34781
+ const localSha = _internals7.gitExec(["rev-parse", "HEAD"], cwd).trim();
34782
+ const remoteSha = _internals7.gitExec(["rev-parse", targetBranch], cwd).trim();
34633
34783
  if (localSha !== remoteSha) {
34634
34784
  return {
34635
34785
  success: false,
@@ -34655,7 +34805,7 @@ function resetToMainAfterMerge(cwd, options) {
34655
34805
  }
34656
34806
  }
34657
34807
  try {
34658
- _internals6.gitExec(["fetch", "--prune", "origin"], cwd);
34808
+ _internals7.gitExec(["fetch", "--prune", "origin"], cwd);
34659
34809
  } catch (err) {
34660
34810
  return {
34661
34811
  success: false,
@@ -34671,7 +34821,7 @@ function resetToMainAfterMerge(cwd, options) {
34671
34821
  let switchedBranch = false;
34672
34822
  if (currentBranch !== defaultBranch) {
34673
34823
  try {
34674
- _internals6.gitExec(["checkout", defaultBranch], cwd);
34824
+ _internals7.gitExec(["checkout", defaultBranch], cwd);
34675
34825
  switchedBranch = true;
34676
34826
  } catch (err) {
34677
34827
  return {
@@ -34686,7 +34836,7 @@ function resetToMainAfterMerge(cwd, options) {
34686
34836
  }
34687
34837
  }
34688
34838
  try {
34689
- _internals6.gitExec(["reset", "--hard", targetBranch], cwd);
34839
+ _internals7.gitExec(["reset", "--hard", targetBranch], cwd);
34690
34840
  } catch (err) {
34691
34841
  return {
34692
34842
  success: false,
@@ -34707,7 +34857,7 @@ function resetToMainAfterMerge(cwd, options) {
34707
34857
  while (Date.now() < endTime) {}
34708
34858
  }
34709
34859
  try {
34710
- _internals6.gitExec(["checkout", "--", "."], cwd);
34860
+ _internals7.gitExec(["checkout", "--", "."], cwd);
34711
34861
  discardSucceeded = true;
34712
34862
  break;
34713
34863
  } catch {}
@@ -34718,18 +34868,18 @@ function resetToMainAfterMerge(cwd, options) {
34718
34868
  changesDiscarded = discardSucceeded;
34719
34869
  }
34720
34870
  try {
34721
- _internals6.gitExec(["clean", "-fd"], cwd);
34871
+ _internals7.gitExec(["clean", "-fd"], cwd);
34722
34872
  } catch {
34723
34873
  warnings.push("Could not clean untracked files");
34724
34874
  }
34725
34875
  let branchDeleted = false;
34726
34876
  if (switchedBranch && previousBranch !== defaultBranch) {
34727
34877
  try {
34728
- const mergedOutput = _internals6.gitExec(["branch", "--merged", defaultBranch], cwd);
34878
+ const mergedOutput = _internals7.gitExec(["branch", "--merged", defaultBranch], cwd);
34729
34879
  const isMerged = mergedOutput.split(`
34730
34880
  `).some((line) => line.trim() === previousBranch || line.trim() === `* ${previousBranch}`);
34731
34881
  if (isMerged) {
34732
- _internals6.gitExec(["branch", "-d", previousBranch], cwd);
34882
+ _internals7.gitExec(["branch", "-d", previousBranch], cwd);
34733
34883
  branchDeleted = true;
34734
34884
  } else {
34735
34885
  warnings.push(`Branch ${previousBranch} is not merged into ${defaultBranch} \u2014 keeping it`);
@@ -34740,7 +34890,7 @@ function resetToMainAfterMerge(cwd, options) {
34740
34890
  }
34741
34891
  if (options?.pruneBranches) {
34742
34892
  try {
34743
- const mergedOutput = _internals6.gitExec(["branch", "--merged", defaultBranch], cwd);
34893
+ const mergedOutput = _internals7.gitExec(["branch", "--merged", defaultBranch], cwd);
34744
34894
  const mergedLines = mergedOutput.split(`
34745
34895
  `);
34746
34896
  for (const line of mergedLines) {
@@ -34749,7 +34899,7 @@ function resetToMainAfterMerge(cwd, options) {
34749
34899
  continue;
34750
34900
  }
34751
34901
  try {
34752
- _internals6.gitExec(["branch", "-d", trimmedLine], cwd);
34902
+ _internals7.gitExec(["branch", "-d", trimmedLine], cwd);
34753
34903
  } catch {
34754
34904
  warnings.push(`Could not prune branch: ${trimmedLine}`);
34755
34905
  }
@@ -34779,10 +34929,10 @@ function resetToMainAfterMerge(cwd, options) {
34779
34929
  };
34780
34930
  }
34781
34931
  }
34782
- var GIT_TIMEOUT_MS2 = 30000, _internals6;
34932
+ var GIT_TIMEOUT_MS2 = 30000, _internals7;
34783
34933
  var init_branch = __esm(() => {
34784
34934
  init_logger();
34785
- _internals6 = {
34935
+ _internals7 = {
34786
34936
  gitExec: gitExec2,
34787
34937
  detectDefaultRemoteBranch,
34788
34938
  getDefaultBaseBranch,
@@ -35215,6 +35365,20 @@ function validateLesson(candidate, existingLessons, meta3) {
35215
35365
  severity: null
35216
35366
  };
35217
35367
  }
35368
+ function validateSkillPath(p) {
35369
+ if (typeof p !== "string")
35370
+ return false;
35371
+ if (p.length === 0 || p.length > 256)
35372
+ return false;
35373
+ if (p.includes("\x00"))
35374
+ return false;
35375
+ if (path11.isAbsolute(p))
35376
+ return false;
35377
+ if (p.includes(".."))
35378
+ return false;
35379
+ const norm = p.replace(/\\/g, "/");
35380
+ return ALLOWED_SKILL_PATH_PREFIXES.some((prefix) => norm.startsWith(prefix));
35381
+ }
35218
35382
  async function quarantineEntry(directory, entryId, reason, reportedBy) {
35219
35383
  if (!directory || directory.includes("..")) {
35220
35384
  warn("[knowledge-validator] quarantineEntry: directory traversal attempt blocked");
@@ -35325,7 +35489,7 @@ async function restoreEntry(directory, entryId) {
35325
35489
  }
35326
35490
  }
35327
35491
  }
35328
- var import_proper_lockfile4, DANGEROUS_COMMAND_PATTERNS, SECURITY_DEGRADING_PATTERNS, INVISIBLE_FORMAT_CHARS, INJECTION_PATTERNS, VALID_CATEGORIES, TECH_REFERENCE_WORDS, ACTION_VERB_WORDS, NEGATION_PAIRS, VALID_DIRECTIVE_PRIORITIES;
35492
+ var import_proper_lockfile4, DANGEROUS_COMMAND_PATTERNS, SECURITY_DEGRADING_PATTERNS, INVISIBLE_FORMAT_CHARS, INJECTION_PATTERNS, VALID_CATEGORIES, TECH_REFERENCE_WORDS, ACTION_VERB_WORDS, NEGATION_PAIRS, ALLOWED_SKILL_PATH_PREFIXES, VALID_DIRECTIVE_PRIORITIES;
35329
35493
  var init_knowledge_validator = __esm(() => {
35330
35494
  init_logger();
35331
35495
  init_knowledge_store();
@@ -35431,6 +35595,10 @@ var init_knowledge_validator = __esm(() => {
35431
35595
  ["use", "don't use"],
35432
35596
  ["recommended", "not recommended"]
35433
35597
  ];
35598
+ ALLOWED_SKILL_PATH_PREFIXES = [
35599
+ ".opencode/skills/generated/",
35600
+ ".swarm/skills/proposals/"
35601
+ ];
35434
35602
  VALID_DIRECTIVE_PRIORITIES = new Set([
35435
35603
  "low",
35436
35604
  "medium",
@@ -35967,7 +36135,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
35967
36135
  existingEntries.push(entry);
35968
36136
  }
35969
36137
  await enforceKnowledgeCap(knowledgePath, config3.swarm_max_entries);
35970
- await _internals7.runAutoPromotion(directory, config3);
36138
+ await _internals8.runAutoPromotion(directory, config3);
35971
36139
  return { stored, skipped, rejected };
35972
36140
  }
35973
36141
  async function runAutoPromotion(directory, config3) {
@@ -36048,7 +36216,7 @@ function createKnowledgeCuratorHook(directory, config3) {
36048
36216
  });
36049
36217
  const projectName2 = evidenceData.project_name ?? "unknown";
36050
36218
  const phaseNumber2 = typeof evidenceData.phase_number === "number" ? evidenceData.phase_number : 1;
36051
- await _internals7.curateAndStoreSwarm(lessons, projectName2, { phase_number: phaseNumber2 }, directory, config3);
36219
+ await _internals8.curateAndStoreSwarm(lessons, projectName2, { phase_number: phaseNumber2 }, directory, config3);
36052
36220
  await updateRetrievalOutcome(directory, `Phase ${phaseNumber2}`, true);
36053
36221
  return;
36054
36222
  }
@@ -36071,19 +36239,19 @@ function createKnowledgeCuratorHook(directory, config3) {
36071
36239
  const projectName = projectNameMatch ? projectNameMatch[1].trim() : "unknown";
36072
36240
  const phaseMatch = /^Phase:\s*(\d+)/m.exec(planContent);
36073
36241
  const phaseNumber = phaseMatch ? parseInt(phaseMatch[1], 10) : 1;
36074
- await _internals7.curateAndStoreSwarm(normalLessons, projectName, { phase_number: phaseNumber }, directory, config3);
36242
+ await _internals8.curateAndStoreSwarm(normalLessons, projectName, { phase_number: phaseNumber }, directory, config3);
36075
36243
  await updateRetrievalOutcome(directory, `Phase ${phaseNumber}`, true);
36076
36244
  };
36077
36245
  return safeHook(handler);
36078
36246
  }
36079
- var seenRetroSections, _internals7;
36247
+ var seenRetroSections, _internals8;
36080
36248
  var init_knowledge_curator = __esm(() => {
36081
36249
  init_knowledge_reader();
36082
36250
  init_knowledge_store();
36083
36251
  init_knowledge_validator();
36084
36252
  init_utils2();
36085
36253
  seenRetroSections = new Map;
36086
- _internals7 = {
36254
+ _internals8 = {
36087
36255
  isWriteToEvidenceFile,
36088
36256
  curateAndStoreSwarm,
36089
36257
  runAutoPromotion,
@@ -36091,6 +36259,904 @@ var init_knowledge_curator = __esm(() => {
36091
36259
  };
36092
36260
  });
36093
36261
 
36262
+ // src/hooks/skill-improver-llm-factory.ts
36263
+ function resolveSkillImproverAgentName(sessionId) {
36264
+ const suffix = "skill_improver";
36265
+ const registeredNames = swarmState.skillImproverAgentNames;
36266
+ if (registeredNames.length === 1)
36267
+ return registeredNames[0];
36268
+ if (registeredNames.length === 0)
36269
+ return suffix;
36270
+ const prefixMap = new Map;
36271
+ for (const name of registeredNames) {
36272
+ const prefix = name.endsWith(suffix) ? name.slice(0, name.length - suffix.length) : "";
36273
+ prefixMap.set(prefix, name);
36274
+ }
36275
+ const matchForAgent = (agentName) => {
36276
+ let bestPrefix = "";
36277
+ let bestName = "";
36278
+ for (const [prefix, name] of prefixMap) {
36279
+ if (prefix && agentName.startsWith(prefix)) {
36280
+ if (prefix.length > bestPrefix.length) {
36281
+ bestPrefix = prefix;
36282
+ bestName = name;
36283
+ }
36284
+ }
36285
+ }
36286
+ return bestName;
36287
+ };
36288
+ if (sessionId) {
36289
+ const callingAgent = swarmState.activeAgent.get(sessionId);
36290
+ if (callingAgent) {
36291
+ const match = matchForAgent(callingAgent);
36292
+ if (match)
36293
+ return match;
36294
+ const defaultAgent = prefixMap.get("");
36295
+ if (defaultAgent)
36296
+ return defaultAgent;
36297
+ }
36298
+ }
36299
+ for (const activeAgentName of swarmState.activeAgent.values()) {
36300
+ const match = matchForAgent(activeAgentName);
36301
+ if (match)
36302
+ return match;
36303
+ }
36304
+ return prefixMap.get("") ?? registeredNames[0];
36305
+ }
36306
+ function createSkillImproverLLMDelegate(directory, sessionId) {
36307
+ const client = swarmState.opencodeClient;
36308
+ if (!client)
36309
+ return;
36310
+ return async (systemPrompt, userInput, signal) => {
36311
+ let ephemeralSessionId;
36312
+ const cleanup = () => {
36313
+ if (ephemeralSessionId) {
36314
+ const id = ephemeralSessionId;
36315
+ ephemeralSessionId = undefined;
36316
+ client.session.delete({ path: { id } }).catch(() => {});
36317
+ }
36318
+ };
36319
+ if (signal?.aborted) {
36320
+ cleanup();
36321
+ throw new Error("SKILL_IMPROVER_LLM_TIMEOUT");
36322
+ }
36323
+ signal?.addEventListener("abort", cleanup, { once: true });
36324
+ try {
36325
+ const createResult = await client.session.create({
36326
+ query: { directory }
36327
+ });
36328
+ if (!createResult.data) {
36329
+ throw new Error(`Failed to create skill_improver session: ${JSON.stringify(createResult.error)}`);
36330
+ }
36331
+ ephemeralSessionId = createResult.data.id;
36332
+ if (signal?.aborted)
36333
+ throw new Error("SKILL_IMPROVER_LLM_TIMEOUT");
36334
+ const agentName = resolveSkillImproverAgentName(sessionId);
36335
+ let promptResult;
36336
+ try {
36337
+ const prelude = systemPrompt ? `${systemPrompt}
36338
+
36339
+ ---
36340
+
36341
+ ${userInput}` : userInput;
36342
+ promptResult = await client.session.prompt({
36343
+ path: { id: ephemeralSessionId },
36344
+ body: {
36345
+ agent: agentName,
36346
+ tools: { write: false, edit: false, patch: false },
36347
+ parts: [{ type: "text", text: prelude }]
36348
+ }
36349
+ });
36350
+ } catch (err) {
36351
+ if (signal?.aborted)
36352
+ throw new Error("SKILL_IMPROVER_LLM_TIMEOUT");
36353
+ throw err;
36354
+ }
36355
+ if (!promptResult.data) {
36356
+ throw new Error(`skill_improver LLM prompt failed: ${JSON.stringify(promptResult.error)}`);
36357
+ }
36358
+ const textParts = promptResult.data.parts.filter((p) => p.type === "text");
36359
+ return textParts.map((p) => p.text).join(`
36360
+ `);
36361
+ } finally {
36362
+ signal?.removeEventListener("abort", cleanup);
36363
+ cleanup();
36364
+ }
36365
+ };
36366
+ }
36367
+ var init_skill_improver_llm_factory = __esm(() => {
36368
+ init_state();
36369
+ });
36370
+
36371
+ // src/services/skill-generator.ts
36372
+ import { existsSync as existsSync9 } from "fs";
36373
+ import { mkdir as mkdir5, readFile as readFile5, rename as rename3, writeFile as writeFile6 } from "fs/promises";
36374
+ import * as path14 from "path";
36375
+ function sanitizeSlug(input) {
36376
+ const lc = input.toLowerCase().trim();
36377
+ const mapped = lc.replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-");
36378
+ const trimmed = mapped.replace(/^-+|-+$/g, "");
36379
+ return trimmed.slice(0, 64);
36380
+ }
36381
+ function isValidSlug(slug) {
36382
+ return SLUG_PATTERN.test(slug);
36383
+ }
36384
+ function proposalPath(directory, slug) {
36385
+ return path14.join(directory, ".swarm", "skills", "proposals", `${slug}.md`);
36386
+ }
36387
+ function activePath(directory, slug) {
36388
+ return path14.join(directory, ".opencode", "skills", "generated", slug, "SKILL.md");
36389
+ }
36390
+ function activeRepoRelativePath(slug) {
36391
+ return `.opencode/skills/generated/${slug}/SKILL.md`;
36392
+ }
36393
+ async function selectCandidateEntries(directory, opts) {
36394
+ const swarm = await readKnowledge(resolveSwarmKnowledgePath(directory));
36395
+ const hivePath = resolveHiveKnowledgePath();
36396
+ const hive = existsSync9(hivePath) ? await readKnowledge(hivePath) : [];
36397
+ const all = [...swarm, ...hive];
36398
+ return all.filter((e) => {
36399
+ if (e.status === "archived")
36400
+ return false;
36401
+ if (e.confidence < opts.minConfidence)
36402
+ return false;
36403
+ const confirmations = (e.confirmed_by ?? []).length;
36404
+ if (confirmations < opts.minConfirmations)
36405
+ return false;
36406
+ if (e.generated_skill_slug)
36407
+ return false;
36408
+ return true;
36409
+ });
36410
+ }
36411
+ function clusterKey(e) {
36412
+ const t = (e.triggers ?? []).map((s) => s.toLowerCase()).sort().join("|");
36413
+ if (t)
36414
+ return `trigger:${t}`;
36415
+ const tools = (e.applies_to_tools ?? []).map((s) => s.toLowerCase()).sort();
36416
+ const agents = (e.applies_to_agents ?? []).map((s) => s.toLowerCase()).sort();
36417
+ if (tools.length > 0 || agents.length > 0) {
36418
+ return `tool-agent:${tools.join("+")}::${agents.join("+")}`;
36419
+ }
36420
+ const tagSig = e.tags.slice(0, 3).map((s) => s.toLowerCase()).sort().join(",");
36421
+ return `cat:${e.category}:${tagSig}`;
36422
+ }
36423
+ function clusterEntries(entries) {
36424
+ const groups = new Map;
36425
+ for (const e of entries) {
36426
+ const k = clusterKey(e);
36427
+ const arr = groups.get(k) ?? [];
36428
+ arr.push(e);
36429
+ groups.set(k, arr);
36430
+ }
36431
+ const clusters = [];
36432
+ for (const [key, arr] of groups) {
36433
+ const triggers = uniqueStrings(arr.flatMap((e) => e.triggers ?? []));
36434
+ const required3 = uniqueStrings(arr.flatMap((e) => e.required_actions ?? []));
36435
+ const forbidden = uniqueStrings(arr.flatMap((e) => e.forbidden_actions ?? []));
36436
+ const agents = uniqueStrings(arr.flatMap((e) => e.applies_to_agents ?? []));
36437
+ const checks5 = uniqueStrings(arr.flatMap((e) => e.verification_checks ?? []));
36438
+ const avgConf = arr.reduce((s, e) => s + e.confidence, 0) / Math.max(1, arr.length);
36439
+ const slugSeed = triggers[0] ?? required3[0] ?? arr[0]?.tags?.[0] ?? arr[0]?.category ?? "lesson";
36440
+ const slug = sanitizeSlug(slugSeed);
36441
+ const title = triggers[0] ?? required3[0] ?? `Lessons: ${arr[0]?.category ?? "general"} (${arr.length})`;
36442
+ clusters.push({
36443
+ slug: isValidSlug(slug) ? slug : sanitizeSlug(`cluster-${key.slice(0, 12)}`),
36444
+ title,
36445
+ entries: arr,
36446
+ triggers,
36447
+ required_actions: required3,
36448
+ forbidden_actions: forbidden,
36449
+ target_agents: agents,
36450
+ verification_checks: checks5,
36451
+ avgConfidence: avgConf
36452
+ });
36453
+ }
36454
+ clusters.sort((a, b) => b.entries.length - a.entries.length || b.avgConfidence - a.avgConfidence || a.slug.localeCompare(b.slug));
36455
+ return clusters;
36456
+ }
36457
+ function uniqueStrings(arr) {
36458
+ return [...new Set(arr.filter((s) => typeof s === "string" && s.length > 0))];
36459
+ }
36460
+ function renderSkillMarkdown(cluster, mode = "active") {
36461
+ const description = cluster.title.length > 200 ? `${cluster.title.slice(0, 197)}\u2026` : cluster.title;
36462
+ const ids = cluster.entries.map((e) => ` - ${e.id}`).join(`
36463
+ `);
36464
+ const lines = [];
36465
+ lines.push("---");
36466
+ lines.push(`name: ${cluster.slug}`);
36467
+ lines.push(`description: ${escapeYaml(description)}`);
36468
+ lines.push("generated_from_knowledge:");
36469
+ lines.push(ids);
36470
+ lines.push(`confidence: ${cluster.avgConfidence.toFixed(2)}`);
36471
+ lines.push(`status: ${mode === "active" ? "active" : "draft"}`);
36472
+ lines.push("---");
36473
+ lines.push("");
36474
+ lines.push("<!-- generated by opencode-swarm skill-generator. Do not edit by hand; edits will be preserved on regeneration only with controlled update mode. -->");
36475
+ lines.push("");
36476
+ lines.push(`# ${escapeMarkdown(cluster.title)}`);
36477
+ lines.push("");
36478
+ lines.push("## Trigger");
36479
+ lines.push("");
36480
+ for (const t of cluster.triggers.length > 0 ? cluster.triggers : ["(no explicit trigger metadata; cluster derived from category/tags)"]) {
36481
+ lines.push(`- ${escapeMarkdown(t)}`);
36482
+ }
36483
+ lines.push("");
36484
+ lines.push("## Required Procedure");
36485
+ lines.push("");
36486
+ if (cluster.required_actions.length > 0) {
36487
+ for (const r of cluster.required_actions)
36488
+ lines.push(`- ${escapeMarkdown(r)}`);
36489
+ } else {
36490
+ lines.push("- Apply the lessons listed under Source Knowledge IDs.");
36491
+ }
36492
+ lines.push("");
36493
+ lines.push("## Forbidden Shortcuts");
36494
+ lines.push("");
36495
+ if (cluster.forbidden_actions.length > 0) {
36496
+ for (const f of cluster.forbidden_actions)
36497
+ lines.push(`- ${escapeMarkdown(f)}`);
36498
+ } else {
36499
+ lines.push("- (none recorded)");
36500
+ }
36501
+ lines.push("");
36502
+ lines.push("## Delegation Template");
36503
+ lines.push("");
36504
+ lines.push("When delegating a task affected by this skill, include:");
36505
+ lines.push("");
36506
+ lines.push("```");
36507
+ lines.push(`SKILLS: file:.opencode/skills/generated/${cluster.slug}/SKILL.md`);
36508
+ lines.push("```");
36509
+ lines.push("");
36510
+ lines.push("## Reviewer Checks");
36511
+ lines.push("");
36512
+ if (cluster.verification_checks.length > 0) {
36513
+ for (const c of cluster.verification_checks)
36514
+ lines.push(`- ${escapeMarkdown(c)}`);
36515
+ } else {
36516
+ lines.push("- Verify each required action above appears in the diff.");
36517
+ }
36518
+ lines.push("");
36519
+ const needsTestEng = cluster.entries.some((e) => e.category === "testing" || (e.tags ?? []).includes("testing"));
36520
+ if (needsTestEng) {
36521
+ lines.push("## Test Engineer Checks");
36522
+ lines.push("");
36523
+ lines.push("- Add or update tests covering the trigger condition and the forbidden shortcut.");
36524
+ lines.push("");
36525
+ }
36526
+ lines.push("## Source Knowledge IDs");
36527
+ lines.push("");
36528
+ for (const e of cluster.entries)
36529
+ lines.push(`- ${e.id} \u2014 ${escapeMarkdown(e.lesson)}`);
36530
+ lines.push("");
36531
+ return lines.join(`
36532
+ `);
36533
+ }
36534
+ function escapeYaml(s) {
36535
+ if (/[:#\n\r"']/.test(s)) {
36536
+ return JSON.stringify(s);
36537
+ }
36538
+ return s;
36539
+ }
36540
+ function escapeMarkdown(s) {
36541
+ return s.replace(/[\r\n]+/g, " ").slice(0, 280);
36542
+ }
36543
+ async function atomicWrite(p, content) {
36544
+ await mkdir5(path14.dirname(p), { recursive: true });
36545
+ const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
36546
+ await writeFile6(tmp, content, "utf-8");
36547
+ await rename3(tmp, p);
36548
+ }
36549
+ async function generateSkills(req) {
36550
+ const minConfidence = req.minConfidence ?? 0.85;
36551
+ const minConfirmations = req.minConfirmations ?? 2;
36552
+ const candidates = await selectCandidateEntries(req.directory, {
36553
+ minConfidence,
36554
+ minConfirmations
36555
+ });
36556
+ let pool;
36557
+ if (req.sourceKnowledgeIds && req.sourceKnowledgeIds.length > 0) {
36558
+ const idSet = new Set(req.sourceKnowledgeIds);
36559
+ const swarm = await readKnowledge(resolveSwarmKnowledgePath(req.directory));
36560
+ const hivePath = resolveHiveKnowledgePath();
36561
+ const hive = existsSync9(hivePath) ? await readKnowledge(hivePath) : [];
36562
+ pool = [...swarm, ...hive].filter((e) => idSet.has(e.id) && e.status !== "archived");
36563
+ } else {
36564
+ pool = candidates;
36565
+ }
36566
+ const clusters = clusterEntries(pool);
36567
+ const result = { written: [], skipped: [] };
36568
+ for (let i = 0;i < clusters.length; i++) {
36569
+ const cluster = clusters[i];
36570
+ if (req.slug && i === 0) {
36571
+ const overridden = sanitizeSlug(req.slug);
36572
+ if (!isValidSlug(overridden)) {
36573
+ result.skipped.push({
36574
+ slug: req.slug,
36575
+ reason: "slug rejected by sanitizer (path traversal or invalid chars)"
36576
+ });
36577
+ continue;
36578
+ }
36579
+ cluster.slug = overridden;
36580
+ }
36581
+ if (!isValidSlug(cluster.slug)) {
36582
+ result.skipped.push({
36583
+ slug: cluster.slug,
36584
+ reason: "computed slug invalid"
36585
+ });
36586
+ continue;
36587
+ }
36588
+ const targetPath = req.mode === "active" ? activePath(req.directory, cluster.slug) : proposalPath(req.directory, cluster.slug);
36589
+ const repoRel = path14.relative(req.directory, targetPath).replace(/\\/g, "/");
36590
+ if (!validateSkillPath(repoRel)) {
36591
+ result.skipped.push({
36592
+ slug: cluster.slug,
36593
+ reason: `target path ${repoRel} not under allowed prefixes (${ALLOWED_SKILL_PATH_PREFIXES.join(", ")})`
36594
+ });
36595
+ continue;
36596
+ }
36597
+ let preserved = false;
36598
+ if (req.mode === "active" && existsSync9(targetPath) && !req.force) {
36599
+ const existing = await readFile5(targetPath, "utf-8");
36600
+ if (!existing.includes("generated by opencode-swarm skill-generator")) {
36601
+ preserved = true;
36602
+ result.skipped.push({
36603
+ slug: cluster.slug,
36604
+ reason: "manually edited skill exists at target path; rerun with force=true to overwrite"
36605
+ });
36606
+ continue;
36607
+ }
36608
+ }
36609
+ const content = renderSkillMarkdown(cluster, req.mode);
36610
+ await atomicWrite(targetPath, content);
36611
+ if (req.mode === "active") {
36612
+ await stampSourceEntries(req.directory, cluster.slug, cluster.entries.map((e) => e.id));
36613
+ }
36614
+ result.written.push({
36615
+ slug: cluster.slug,
36616
+ path: targetPath,
36617
+ mode: req.mode,
36618
+ sourceKnowledgeIds: cluster.entries.map((e) => e.id),
36619
+ preserved
36620
+ });
36621
+ }
36622
+ return result;
36623
+ }
36624
+ async function stampSourceEntries(directory, slug, ids) {
36625
+ if (!ids || ids.length === 0)
36626
+ return;
36627
+ const swarmPath = resolveSwarmKnowledgePath(directory);
36628
+ const swarm = await readKnowledge(swarmPath);
36629
+ const idSet = new Set(ids);
36630
+ let touched = false;
36631
+ const repoRel = activeRepoRelativePath(slug);
36632
+ for (const e of swarm) {
36633
+ if (!idSet.has(e.id))
36634
+ continue;
36635
+ e.generated_skill_slug = slug;
36636
+ e.generated_skill_path = repoRel;
36637
+ e.updated_at = new Date().toISOString();
36638
+ touched = true;
36639
+ }
36640
+ if (touched)
36641
+ await rewriteKnowledge(swarmPath, swarm);
36642
+ const hivePath = resolveHiveKnowledgePath();
36643
+ if (!existsSync9(hivePath))
36644
+ return;
36645
+ const hive = await readKnowledge(hivePath);
36646
+ let touchedHive = false;
36647
+ for (const e of hive) {
36648
+ if (!idSet.has(e.id))
36649
+ continue;
36650
+ e.generated_skill_slug = slug;
36651
+ e.generated_skill_path = repoRel;
36652
+ e.updated_at = new Date().toISOString();
36653
+ touchedHive = true;
36654
+ }
36655
+ if (touchedHive)
36656
+ await rewriteKnowledge(hivePath, hive);
36657
+ }
36658
+ async function listSkills(directory) {
36659
+ const result = {
36660
+ proposals: [],
36661
+ active: []
36662
+ };
36663
+ const proposalsDir = path14.join(directory, ".swarm", "skills", "proposals");
36664
+ const activeDir = path14.join(directory, ".opencode", "skills", "generated");
36665
+ const fs7 = await import("fs/promises");
36666
+ if (existsSync9(proposalsDir)) {
36667
+ const entries = await fs7.readdir(proposalsDir);
36668
+ for (const f of entries) {
36669
+ if (!f.endsWith(".md"))
36670
+ continue;
36671
+ const slug = f.replace(/\.md$/, "");
36672
+ result.proposals.push({
36673
+ slug,
36674
+ path: path14.join(proposalsDir, f)
36675
+ });
36676
+ }
36677
+ }
36678
+ if (existsSync9(activeDir)) {
36679
+ const entries = await fs7.readdir(activeDir, { withFileTypes: true });
36680
+ for (const e of entries) {
36681
+ if (!e.isDirectory())
36682
+ continue;
36683
+ const skillPath = path14.join(activeDir, e.name, "SKILL.md");
36684
+ if (existsSync9(skillPath)) {
36685
+ result.active.push({
36686
+ slug: e.name,
36687
+ path: skillPath
36688
+ });
36689
+ }
36690
+ }
36691
+ }
36692
+ return result;
36693
+ }
36694
+ var SLUG_PATTERN;
36695
+ var init_skill_generator = __esm(() => {
36696
+ init_knowledge_store();
36697
+ init_knowledge_validator();
36698
+ init_logger();
36699
+ SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
36700
+ });
36701
+
36702
+ // src/services/skill-improver-quota.ts
36703
+ import { existsSync as existsSync10 } from "fs";
36704
+ import { mkdir as mkdir6, readFile as readFile6, rename as rename4, writeFile as writeFile7 } from "fs/promises";
36705
+ import * as path15 from "path";
36706
+ async function acquireLock(dir) {
36707
+ const acquire = import_proper_lockfile5.default.lock(dir, LOCK_RETRY_OPTS);
36708
+ let timer;
36709
+ const timeout = new Promise((_, reject) => {
36710
+ timer = setTimeout(() => {
36711
+ reject(new Error(`SKILL_IMPROVER_QUOTA_LOCK_TIMEOUT: failed to acquire lock on ${dir} within ${LOCK_ACQUIRE_TIMEOUT_MS}ms`));
36712
+ }, LOCK_ACQUIRE_TIMEOUT_MS);
36713
+ });
36714
+ try {
36715
+ const release = await Promise.race([acquire, timeout]);
36716
+ return release;
36717
+ } finally {
36718
+ if (timer)
36719
+ clearTimeout(timer);
36720
+ }
36721
+ }
36722
+ function resolveQuotaPath(directory) {
36723
+ return path15.join(directory, ".swarm", "skill-improver-quota.json");
36724
+ }
36725
+ function todayKey(window, now = new Date) {
36726
+ if (window === "utc") {
36727
+ return now.toISOString().slice(0, 10);
36728
+ }
36729
+ const yr = now.getFullYear();
36730
+ const m = String(now.getMonth() + 1).padStart(2, "0");
36731
+ const d = String(now.getDate()).padStart(2, "0");
36732
+ return `${yr}-${m}-${d}`;
36733
+ }
36734
+ async function readState(filePath) {
36735
+ if (!existsSync10(filePath))
36736
+ return null;
36737
+ try {
36738
+ const raw = await readFile6(filePath, "utf-8");
36739
+ const parsed = JSON.parse(raw);
36740
+ if (typeof parsed.date !== "string" || typeof parsed.calls_used !== "number" || typeof parsed.max_calls !== "number" || parsed.window !== "utc" && parsed.window !== "local") {
36741
+ return null;
36742
+ }
36743
+ return parsed;
36744
+ } catch {
36745
+ return null;
36746
+ }
36747
+ }
36748
+ async function writeState(filePath, state) {
36749
+ await mkdir6(path15.dirname(filePath), { recursive: true });
36750
+ const tmp = `${filePath}.tmp-${process.pid}`;
36751
+ await writeFile7(tmp, JSON.stringify(state, null, 2), "utf-8");
36752
+ await rename4(tmp, filePath);
36753
+ }
36754
+ async function getQuotaState(directory, opts) {
36755
+ const filePath = resolveQuotaPath(directory);
36756
+ const today = todayKey(opts.window, opts.now);
36757
+ const existing = await readState(filePath);
36758
+ if (!existing || existing.date !== today || existing.window !== opts.window) {
36759
+ const fresh = {
36760
+ date: today,
36761
+ calls_used: 0,
36762
+ max_calls: opts.maxCalls,
36763
+ window: opts.window
36764
+ };
36765
+ await writeState(filePath, fresh);
36766
+ return fresh;
36767
+ }
36768
+ return { ...existing, max_calls: opts.maxCalls };
36769
+ }
36770
+ async function reserveQuota(directory, opts) {
36771
+ const filePath = resolveQuotaPath(directory);
36772
+ await mkdir6(path15.dirname(filePath), { recursive: true });
36773
+ let release = null;
36774
+ try {
36775
+ release = await acquireLock(path15.dirname(filePath));
36776
+ const state = await getQuotaState(directory, opts);
36777
+ if (state.calls_used + opts.nCalls > opts.maxCalls) {
36778
+ return {
36779
+ allowed: false,
36780
+ state,
36781
+ reason: `daily quota exhausted: used=${state.calls_used} requested=${opts.nCalls} max=${opts.maxCalls}`
36782
+ };
36783
+ }
36784
+ const next = {
36785
+ ...state,
36786
+ calls_used: state.calls_used + opts.nCalls,
36787
+ max_calls: opts.maxCalls,
36788
+ last_run_at: (opts.now ?? new Date).toISOString()
36789
+ };
36790
+ await writeState(filePath, next);
36791
+ return { allowed: true, state: next };
36792
+ } finally {
36793
+ if (release) {
36794
+ try {
36795
+ await release();
36796
+ } catch {}
36797
+ }
36798
+ }
36799
+ }
36800
+ async function releaseQuota(directory, opts) {
36801
+ const filePath = resolveQuotaPath(directory);
36802
+ await mkdir6(path15.dirname(filePath), { recursive: true });
36803
+ let release = null;
36804
+ try {
36805
+ release = await acquireLock(path15.dirname(filePath));
36806
+ const state = await getQuotaState(directory, opts);
36807
+ const next = {
36808
+ ...state,
36809
+ calls_used: Math.max(0, state.calls_used - opts.nCalls),
36810
+ max_calls: opts.maxCalls
36811
+ };
36812
+ await writeState(filePath, next);
36813
+ return next;
36814
+ } finally {
36815
+ if (release) {
36816
+ try {
36817
+ await release();
36818
+ } catch {}
36819
+ }
36820
+ }
36821
+ }
36822
+ var import_proper_lockfile5, LOCK_ACQUIRE_TIMEOUT_MS = 1e4, LOCK_RETRY_OPTS;
36823
+ var init_skill_improver_quota = __esm(() => {
36824
+ import_proper_lockfile5 = __toESM(require_proper_lockfile(), 1);
36825
+ LOCK_RETRY_OPTS = {
36826
+ retries: {
36827
+ retries: 30,
36828
+ minTimeout: 50,
36829
+ maxTimeout: 200,
36830
+ factor: 1.5
36831
+ },
36832
+ stale: 5000
36833
+ };
36834
+ });
36835
+
36836
+ // src/services/skill-improver.ts
36837
+ import { existsSync as existsSync11 } from "fs";
36838
+ import { mkdir as mkdir7, rename as rename5, writeFile as writeFile8 } from "fs/promises";
36839
+ import * as path16 from "path";
36840
+ function timestampSlug(d) {
36841
+ return d.toISOString().replace(/[:.]/g, "-");
36842
+ }
36843
+ async function atomicWrite2(p, content) {
36844
+ await mkdir7(path16.dirname(p), { recursive: true });
36845
+ const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
36846
+ await writeFile8(tmp, content, "utf-8");
36847
+ await rename5(tmp, p);
36848
+ }
36849
+ async function gatherInventory(directory) {
36850
+ const swarm = await readKnowledge(resolveSwarmKnowledgePath(directory));
36851
+ const hivePath = resolveHiveKnowledgePath();
36852
+ const hive = existsSync11(hivePath) ? await readKnowledge(hivePath) : [];
36853
+ const archived = [...swarm, ...hive].filter((e) => e.status === "archived").length;
36854
+ const skills = await listSkills(directory);
36855
+ const matureCandidates = swarm.concat(hive).filter((e) => e.status !== "archived" && e.confidence >= 0.85 && !e.generated_skill_slug && (e.confirmed_by ?? []).length >= 2);
36856
+ return {
36857
+ knowledge: { swarm: swarm.length, hive: hive.length, archived },
36858
+ skills: {
36859
+ proposals: skills.proposals.length,
36860
+ active: skills.active.length
36861
+ },
36862
+ highConfidenceClusters: matureCandidates.length,
36863
+ matureCandidates
36864
+ };
36865
+ }
36866
+ function buildSystemPrompt(targets, cfg) {
36867
+ return `You are skill_improver. Targets: ${targets.join(", ")}. Mode: ${cfg.write_mode}.
36868
+
36869
+ Output a single complete markdown proposal under 6000 chars. Sections required:
36870
+ 1. ## Inventory snapshot
36871
+ 2. ## Repeated ignored or violated directives (cite knowledge ids)
36872
+ 3. ## Concrete recommendations
36873
+ 4. ## Optional cluster suggestions for new draft skills (slug, title, source ids, target agents) \u2014 leave empty if none qualify
36874
+ 5. ## Risks and known limitations
36875
+
36876
+ Do NOT propose direct source-code edits. Do NOT call any tools. Return only the markdown body.`;
36877
+ }
36878
+ function buildUserPrompt(inv) {
36879
+ const matureRows = inv.matureCandidates.slice(0, 25).map((e) => `- ${e.id} | conf=${e.confidence.toFixed(2)} | ${e.category} | "${e.lesson.slice(0, 140).replace(/\n/g, " ")}"`).join(`
36880
+ `);
36881
+ return `INVENTORY
36882
+ swarm_entries: ${inv.knowledge.swarm}
36883
+ hive_entries: ${inv.knowledge.hive}
36884
+ archived: ${inv.knowledge.archived}
36885
+ draft_skills: ${inv.skills.proposals}
36886
+ active_skills: ${inv.skills.active}
36887
+ mature_uncompiled_clusters: ${inv.highConfidenceClusters}
36888
+
36889
+ TOP MATURE CANDIDATES (first 25):
36890
+ ${matureRows || "(none)"}
36891
+ `;
36892
+ }
36893
+ function isAbortError(err) {
36894
+ return err instanceof Error && (err.name === "AbortError" || err.message === "skill_improver_aborted");
36895
+ }
36896
+ function throwIfAborted(signal) {
36897
+ if (!signal?.aborted)
36898
+ return;
36899
+ const err = new Error("skill_improver_aborted");
36900
+ err.name = "AbortError";
36901
+ throw err;
36902
+ }
36903
+ function buildDeterministicProposal(args) {
36904
+ const lines = [];
36905
+ lines.push("---");
36906
+ lines.push("source: deterministic_fallback");
36907
+ lines.push(`generated_at: ${args.now.toISOString()}`);
36908
+ lines.push(`targets: [${args.targets.join(", ")}]`);
36909
+ lines.push(`model: ${args.model ?? "(none \u2014 deterministic body)"}`);
36910
+ lines.push("---");
36911
+ lines.push("");
36912
+ lines.push("# Skill Improvement Proposal (deterministic fallback)");
36913
+ lines.push("");
36914
+ lines.push("> No OpenCode LLM client was available when this proposal was generated. The body below is a deterministic inventory summary, not an LLM-derived analysis. To produce a real LLM proposal, run `skill_improve` from a session where the OpenCode runtime has wired `swarmState.opencodeClient`, or set `skill_improver.allow_deterministic_fallback: false` to force a hard failure instead of this fallback.");
36915
+ lines.push("");
36916
+ lines.push("## Inventory snapshot");
36917
+ lines.push(`- Knowledge entries: swarm=${args.inventory.knowledge.swarm}, hive=${args.inventory.knowledge.hive}, archived=${args.inventory.knowledge.archived}`);
36918
+ lines.push(`- Generated skills: proposals=${args.inventory.skills.proposals}, active=${args.inventory.skills.active}`);
36919
+ lines.push(`- High-confidence un-skill'd clusters: ${args.inventory.highConfidenceClusters}`);
36920
+ lines.push("");
36921
+ lines.push("## Recommendations");
36922
+ if (args.inventory.highConfidenceClusters > 0) {
36923
+ lines.push(`- Run \`skill_generate { mode: "draft" }\` to emit ${args.inventory.highConfidenceClusters} draft SKILL.md proposal(s).`);
36924
+ }
36925
+ if (args.targets.includes("architect_prompt")) {
36926
+ lines.push("- Manually review architect prompt against current high-priority directives.");
36927
+ }
36928
+ if (args.targets.includes("spec")) {
36929
+ lines.push("- Audit `.swarm/spec.md` for drift.");
36930
+ }
36931
+ if (args.targets.includes("knowledge")) {
36932
+ lines.push("- Archive low-confidence (< 0.3) entries with applied_explicit_count == 0 and shown_count > 5.");
36933
+ }
36934
+ lines.push("");
36935
+ return lines.join(`
36936
+ `);
36937
+ }
36938
+ function buildLLMProposalFrame(args) {
36939
+ const lines = [];
36940
+ lines.push("---");
36941
+ lines.push("source: llm");
36942
+ lines.push(`generated_at: ${args.now.toISOString()}`);
36943
+ lines.push(`targets: [${args.targets.join(", ")}]`);
36944
+ if (args.model)
36945
+ lines.push(`model: ${args.model}`);
36946
+ lines.push("---");
36947
+ lines.push("");
36948
+ lines.push(args.body.trim());
36949
+ lines.push("");
36950
+ return lines.join(`
36951
+ `);
36952
+ }
36953
+ async function runSkillImprover(req) {
36954
+ const cfg = req.config;
36955
+ const now = req.now ?? new Date;
36956
+ const targets = req.targets && req.targets.length > 0 ? req.targets : cfg.targets;
36957
+ const writeMode = req.mode ?? cfg.write_mode;
36958
+ const maxCalls = Math.max(1, Math.min(cfg.max_calls_per_day, req.maxCalls ?? 1));
36959
+ const noQuota = {
36960
+ date: now.toISOString().slice(0, 10),
36961
+ calls_used: 0,
36962
+ max_calls: cfg.max_calls_per_day
36963
+ };
36964
+ if (!cfg.enabled) {
36965
+ return {
36966
+ ran: false,
36967
+ reason: "skill_improver.enabled is false",
36968
+ quota: noQuota
36969
+ };
36970
+ }
36971
+ if (req.signal?.aborted) {
36972
+ return {
36973
+ ran: false,
36974
+ reason: "skill_improver aborted",
36975
+ quota: noQuota
36976
+ };
36977
+ }
36978
+ const delegate = req.delegate ?? createSkillImproverLLMDelegate(req.directory, req.sessionId);
36979
+ if (!delegate && !cfg.allow_deterministic_fallback) {
36980
+ return {
36981
+ ran: false,
36982
+ reason: "no_llm_client: skill_improver.allow_deterministic_fallback is false and no OpenCode client is wired. Run from a session where swarmState.opencodeClient is available, or enable allow_deterministic_fallback to use the offline path.",
36983
+ quota: noQuota
36984
+ };
36985
+ }
36986
+ const reservation = await reserveQuota(req.directory, {
36987
+ nCalls: maxCalls,
36988
+ maxCalls: cfg.max_calls_per_day,
36989
+ window: cfg.quota_window,
36990
+ now
36991
+ });
36992
+ if (!reservation.allowed) {
36993
+ return {
36994
+ ran: false,
36995
+ reason: reservation.reason ?? "quota exhausted",
36996
+ quota: {
36997
+ date: reservation.state.date,
36998
+ calls_used: reservation.state.calls_used,
36999
+ max_calls: reservation.state.max_calls
37000
+ }
37001
+ };
37002
+ }
37003
+ let networkStarted = false;
37004
+ let sideEffectsStarted = false;
37005
+ const reservationQuota = {
37006
+ date: reservation.state.date,
37007
+ calls_used: reservation.state.calls_used,
37008
+ max_calls: reservation.state.max_calls
37009
+ };
37010
+ const releaseReservedQuota = async () => {
37011
+ const released = await releaseQuota(req.directory, {
37012
+ nCalls: maxCalls,
37013
+ maxCalls: cfg.max_calls_per_day,
37014
+ window: cfg.quota_window,
37015
+ now
37016
+ }).catch(() => ({
37017
+ ...reservation.state,
37018
+ calls_used: Math.max(0, reservation.state.calls_used - maxCalls)
37019
+ }));
37020
+ return {
37021
+ date: released.date,
37022
+ calls_used: released.calls_used,
37023
+ max_calls: released.max_calls
37024
+ };
37025
+ };
37026
+ const abortResult = async () => ({
37027
+ ran: false,
37028
+ reason: "skill_improver aborted",
37029
+ quota: networkStarted || sideEffectsStarted ? reservationQuota : await releaseReservedQuota()
37030
+ });
37031
+ let inventory;
37032
+ try {
37033
+ throwIfAborted(req.signal);
37034
+ inventory = await gatherInventory(req.directory);
37035
+ throwIfAborted(req.signal);
37036
+ } catch (err) {
37037
+ if (isAbortError(err)) {
37038
+ return await abortResult();
37039
+ }
37040
+ await releaseQuota(req.directory, {
37041
+ nCalls: maxCalls,
37042
+ maxCalls: cfg.max_calls_per_day,
37043
+ window: cfg.quota_window,
37044
+ now
37045
+ }).catch(() => {});
37046
+ return {
37047
+ ran: false,
37048
+ reason: `inventory_failed: ${err instanceof Error ? err.message : String(err)}`,
37049
+ quota: {
37050
+ date: reservation.state.date,
37051
+ calls_used: reservation.state.calls_used - maxCalls,
37052
+ max_calls: reservation.state.max_calls
37053
+ }
37054
+ };
37055
+ }
37056
+ let body;
37057
+ let source;
37058
+ if (delegate) {
37059
+ try {
37060
+ throwIfAborted(req.signal);
37061
+ networkStarted = true;
37062
+ body = await delegate(buildSystemPrompt(targets, { ...cfg, write_mode: writeMode }), buildUserPrompt(inventory), req.signal);
37063
+ throwIfAborted(req.signal);
37064
+ if (!body || body.trim().length === 0) {
37065
+ throw new Error("empty LLM response");
37066
+ }
37067
+ source = "llm";
37068
+ } catch (err) {
37069
+ if (isAbortError(err)) {
37070
+ return await abortResult();
37071
+ }
37072
+ return {
37073
+ ran: false,
37074
+ reason: `llm_call_failed: ${err instanceof Error ? err.message : String(err)}`,
37075
+ quota: {
37076
+ date: reservation.state.date,
37077
+ calls_used: reservation.state.calls_used,
37078
+ max_calls: reservation.state.max_calls
37079
+ }
37080
+ };
37081
+ }
37082
+ } else {
37083
+ try {
37084
+ throwIfAborted(req.signal);
37085
+ } catch (err) {
37086
+ if (isAbortError(err)) {
37087
+ return await abortResult();
37088
+ }
37089
+ throw err;
37090
+ }
37091
+ body = "";
37092
+ source = "deterministic_fallback";
37093
+ }
37094
+ let draftSkillsWritten;
37095
+ if (writeMode === "draft_skills" && inventory.matureCandidates.length > 0) {
37096
+ try {
37097
+ throwIfAborted(req.signal);
37098
+ } catch (err) {
37099
+ if (isAbortError(err)) {
37100
+ return await abortResult();
37101
+ }
37102
+ throw err;
37103
+ }
37104
+ sideEffectsStarted = true;
37105
+ const gen = await generateSkills({
37106
+ directory: req.directory,
37107
+ mode: "draft",
37108
+ minConfidence: 0.85,
37109
+ minConfirmations: 2
37110
+ });
37111
+ draftSkillsWritten = gen.written.map((w) => ({
37112
+ slug: w.slug,
37113
+ path: w.path,
37114
+ sourceKnowledgeIds: w.sourceKnowledgeIds
37115
+ }));
37116
+ }
37117
+ try {
37118
+ throwIfAborted(req.signal);
37119
+ } catch (err) {
37120
+ if (isAbortError(err)) {
37121
+ return await abortResult();
37122
+ }
37123
+ throw err;
37124
+ }
37125
+ const proposalDir = path16.join(req.directory, ".swarm", "skill-improver", "proposals");
37126
+ const proposalFile = path16.join(proposalDir, `${timestampSlug(now)}.md`);
37127
+ const finalBody = source === "llm" ? buildLLMProposalFrame({
37128
+ body,
37129
+ targets,
37130
+ model: cfg.model,
37131
+ now
37132
+ }) : buildDeterministicProposal({
37133
+ targets,
37134
+ inventory,
37135
+ model: cfg.model,
37136
+ now
37137
+ });
37138
+ sideEffectsStarted = true;
37139
+ await atomicWrite2(proposalFile, finalBody);
37140
+ return {
37141
+ ran: true,
37142
+ proposalPath: proposalFile,
37143
+ source,
37144
+ quota: {
37145
+ date: reservation.state.date,
37146
+ calls_used: reservation.state.calls_used,
37147
+ max_calls: reservation.state.max_calls
37148
+ },
37149
+ draftSkillsWritten,
37150
+ model: cfg.model ?? undefined
37151
+ };
37152
+ }
37153
+ var init_skill_improver = __esm(() => {
37154
+ init_knowledge_store();
37155
+ init_skill_improver_llm_factory();
37156
+ init_skill_generator();
37157
+ init_skill_improver_quota();
37158
+ });
37159
+
36094
37160
  // src/tools/write-retro.ts
36095
37161
  async function executeWriteRetro(args, directory) {
36096
37162
  if (/^(CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])(:|$)/i.test(directory)) {
@@ -36392,7 +37458,7 @@ async function executeWriteRetro(args, directory) {
36392
37458
  }, null, 2);
36393
37459
  }
36394
37460
  }
36395
- var write_retro, _internals8;
37461
+ var write_retro, _internals9;
36396
37462
  var init_write_retro = __esm(() => {
36397
37463
  init_zod();
36398
37464
  init_evidence_schema();
@@ -36439,13 +37505,13 @@ var init_write_retro = __esm(() => {
36439
37505
  task_id: args.task_id !== undefined ? String(args.task_id) : undefined,
36440
37506
  metadata: args.metadata
36441
37507
  };
36442
- return await _internals8.executeWriteRetro(writeRetroArgs, directory);
37508
+ return await _internals9.executeWriteRetro(writeRetroArgs, directory);
36443
37509
  } catch {
36444
37510
  return JSON.stringify({ success: false, phase: rawPhase, message: "Invalid arguments" }, null, 2);
36445
37511
  }
36446
37512
  }
36447
37513
  });
36448
- _internals8 = {
37514
+ _internals9 = {
36449
37515
  executeWriteRetro,
36450
37516
  write_retro
36451
37517
  };
@@ -36453,7 +37519,40 @@ var init_write_retro = __esm(() => {
36453
37519
 
36454
37520
  // src/commands/close.ts
36455
37521
  import { promises as fs7 } from "fs";
36456
- import path14 from "path";
37522
+ import path17 from "path";
37523
+ async function runAbortableSkillReview(req, timeoutMs) {
37524
+ const controller = new AbortController;
37525
+ let timeout;
37526
+ const skillReviewPromise = runSkillImprover({
37527
+ ...req,
37528
+ signal: controller.signal
37529
+ });
37530
+ const timeoutPromise = new Promise((_, reject) => {
37531
+ timeout = setTimeout(() => {
37532
+ reject(new Error(`skill_review exceeded ${timeoutMs}ms budget`));
37533
+ controller.abort();
37534
+ }, timeoutMs);
37535
+ });
37536
+ try {
37537
+ return await Promise.race([skillReviewPromise, timeoutPromise]);
37538
+ } finally {
37539
+ if (timeout)
37540
+ clearTimeout(timeout);
37541
+ }
37542
+ }
37543
+ function countSessionKnowledgeEntries(entries, sessionStart, fallbackCount) {
37544
+ if (!sessionStart)
37545
+ return fallbackCount;
37546
+ const sessionStartMs = Date.parse(sessionStart);
37547
+ if (!Number.isFinite(sessionStartMs))
37548
+ return fallbackCount;
37549
+ return entries.filter((entry) => {
37550
+ if (typeof entry.created_at !== "string")
37551
+ return false;
37552
+ const createdAtMs = Date.parse(entry.created_at);
37553
+ return Number.isFinite(createdAtMs) && createdAtMs >= sessionStartMs;
37554
+ }).length;
37555
+ }
36457
37556
  function guaranteeAllPlansComplete(planData) {
36458
37557
  const closedPhaseIds = [];
36459
37558
  const closedTaskIds = [];
@@ -36474,12 +37573,12 @@ function guaranteeAllPlansComplete(planData) {
36474
37573
  }
36475
37574
  return { closedPhaseIds, closedTaskIds };
36476
37575
  }
36477
- async function handleCloseCommand(directory, args) {
37576
+ async function handleCloseCommand(directory, args, options = {}) {
36478
37577
  const planPath = validateSwarmPath(directory, "plan.json");
36479
- const swarmDir = path14.join(directory, ".swarm");
37578
+ const swarmDir = path17.join(directory, ".swarm");
36480
37579
  let planExists = false;
36481
37580
  let planData = {
36482
- title: path14.basename(directory) || "Ad-hoc session",
37581
+ title: path17.basename(directory) || "Ad-hoc session",
36483
37582
  phases: []
36484
37583
  };
36485
37584
  try {
@@ -36498,6 +37597,7 @@ async function handleCloseCommand(directory, args) {
36498
37597
  const phases = planData.phases ?? [];
36499
37598
  const inProgressPhases = phases.filter((p) => p.status === "in_progress");
36500
37599
  const isForced = args.includes("--force");
37600
+ const runSkillReview = args.includes("--skill-review");
36501
37601
  let planAlreadyDone = false;
36502
37602
  if (planExists) {
36503
37603
  planAlreadyDone = phases.length > 0 && phases.every((p) => p.status === "complete" || p.status === "completed" || p.status === "blocked" || p.status === "closed");
@@ -36585,7 +37685,7 @@ async function handleCloseCommand(directory, args) {
36585
37685
  warnings.push(`Session retrospective write threw: ${retroError instanceof Error ? retroError.message : String(retroError)}`);
36586
37686
  }
36587
37687
  }
36588
- const lessonsFilePath = path14.join(swarmDir, "close-lessons.md");
37688
+ const lessonsFilePath = path17.join(swarmDir, "close-lessons.md");
36589
37689
  let explicitLessons = [];
36590
37690
  try {
36591
37691
  const lessonsText = await fs7.readFile(lessonsFilePath, "utf-8");
@@ -36594,11 +37694,11 @@ async function handleCloseCommand(directory, args) {
36594
37694
  } catch {}
36595
37695
  const retroLessons = [];
36596
37696
  try {
36597
- const evidenceDir = path14.join(swarmDir, "evidence");
37697
+ const evidenceDir = path17.join(swarmDir, "evidence");
36598
37698
  const evidenceEntries = await fs7.readdir(evidenceDir);
36599
37699
  const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-")).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
36600
37700
  for (const retroDir of retroDirs) {
36601
- const evidencePath = path14.join(evidenceDir, retroDir, "evidence.json");
37701
+ const evidencePath = path17.join(evidenceDir, retroDir, "evidence.json");
36602
37702
  try {
36603
37703
  const content = await fs7.readFile(evidencePath, "utf-8");
36604
37704
  const parsed = JSON.parse(content);
@@ -36617,8 +37717,9 @@ async function handleCloseCommand(directory, args) {
36617
37717
  } catch {}
36618
37718
  const allLessons = [...new Set([...explicitLessons, ...retroLessons])];
36619
37719
  let curationSucceeded = false;
37720
+ let curationResult;
36620
37721
  try {
36621
- await curateAndStoreSwarm(allLessons, projectName, { phase_number: 0 }, directory, config3);
37722
+ curationResult = await curateAndStoreSwarm(allLessons, projectName, { phase_number: 0 }, directory, config3);
36622
37723
  curationSucceeded = true;
36623
37724
  } catch (error93) {
36624
37725
  const msg = error93 instanceof Error ? error93.message : String(error93);
@@ -36648,6 +37749,44 @@ async function handleCloseCommand(directory, args) {
36648
37749
  warnings.push(`Hive promotion failed: ${msg}`);
36649
37750
  }
36650
37751
  }
37752
+ const fallbackKnowledgeCreated = curationResult?.stored ?? 0;
37753
+ let sessionKnowledgeCreated = fallbackKnowledgeCreated;
37754
+ try {
37755
+ const knowledgePath = resolveSwarmKnowledgePath(directory);
37756
+ const entries = await readKnowledge(knowledgePath);
37757
+ sessionKnowledgeCreated = countSessionKnowledgeEntries(entries, sessionStart, fallbackKnowledgeCreated);
37758
+ } catch (knowledgeErr) {
37759
+ const msg = knowledgeErr instanceof Error ? knowledgeErr.message : String(knowledgeErr);
37760
+ warnings.push(`Knowledge session count failed: ${msg}`);
37761
+ }
37762
+ const knowledgeSkillHint = sessionKnowledgeCreated > 0 ? `${sessionKnowledgeCreated} knowledge entries created this session. Consider running skill_improve or skill_generate to compile mature entries into skills.` : "";
37763
+ let skillReviewSummary = "";
37764
+ if (runSkillReview) {
37765
+ try {
37766
+ const { config: loadedConfig } = loadPluginConfigWithMeta(directory);
37767
+ const skillImproverConfig = SkillImproverConfigSchema.parse(loadedConfig.skill_improver ?? {});
37768
+ const skillReviewResult = await runAbortableSkillReview({
37769
+ directory,
37770
+ config: skillImproverConfig,
37771
+ targets: ["skills", "knowledge"],
37772
+ mode: "proposal",
37773
+ sessionId: options.sessionID
37774
+ }, options.skillReviewTimeoutMs ?? CLOSE_SKILL_REVIEW_TIMEOUT_MS);
37775
+ if (skillReviewResult.ran) {
37776
+ const proposal = skillReviewResult.proposalPath ? ` Proposal: ${skillReviewResult.proposalPath}.` : "";
37777
+ const source = skillReviewResult.source ? ` Source: ${skillReviewResult.source}.` : "";
37778
+ skillReviewSummary = `Skill review proposal generated.${proposal}${source}`;
37779
+ } else {
37780
+ const reason = skillReviewResult.reason ?? "unknown reason";
37781
+ skillReviewSummary = `Skill review skipped: ${reason}`;
37782
+ warnings.push(skillReviewSummary);
37783
+ }
37784
+ } catch (skillReviewErr) {
37785
+ const msg = skillReviewErr instanceof Error ? skillReviewErr.message : String(skillReviewErr);
37786
+ skillReviewSummary = `Skill review failed: ${msg}`;
37787
+ warnings.push(skillReviewSummary);
37788
+ }
37789
+ }
36651
37790
  if (planExists) {
36652
37791
  const guaranteeResult = guaranteeAllPlansComplete(planData);
36653
37792
  for (const phaseId of guaranteeResult.closedPhaseIds) {
@@ -36672,7 +37811,7 @@ async function handleCloseCommand(directory, args) {
36672
37811
  }
36673
37812
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
36674
37813
  const suffix = Math.random().toString(36).slice(2, 8);
36675
- const archiveDir = path14.join(swarmDir, "archive", `swarm-${timestamp}-${suffix}`);
37814
+ const archiveDir = path17.join(swarmDir, "archive", `swarm-${timestamp}-${suffix}`);
36676
37815
  let archiveResult = "";
36677
37816
  let archivedFileCount = 0;
36678
37817
  const archivedActiveStateFiles = new Set;
@@ -36680,8 +37819,8 @@ async function handleCloseCommand(directory, args) {
36680
37819
  try {
36681
37820
  await fs7.mkdir(archiveDir, { recursive: true });
36682
37821
  for (const artifact of ARCHIVE_ARTIFACTS) {
36683
- const srcPath = path14.join(swarmDir, artifact);
36684
- const destPath = path14.join(archiveDir, artifact);
37822
+ const srcPath = path17.join(swarmDir, artifact);
37823
+ const destPath = path17.join(archiveDir, artifact);
36685
37824
  try {
36686
37825
  await fs7.copyFile(srcPath, destPath);
36687
37826
  archivedFileCount++;
@@ -36691,22 +37830,22 @@ async function handleCloseCommand(directory, args) {
36691
37830
  } catch {}
36692
37831
  }
36693
37832
  for (const dirName of ACTIVE_STATE_DIRS_TO_CLEAN) {
36694
- const srcDir = path14.join(swarmDir, dirName);
36695
- const destDir = path14.join(archiveDir, dirName);
37833
+ const srcDir = path17.join(swarmDir, dirName);
37834
+ const destDir = path17.join(archiveDir, dirName);
36696
37835
  try {
36697
37836
  const entries = await fs7.readdir(srcDir);
36698
37837
  if (entries.length > 0) {
36699
37838
  await fs7.mkdir(destDir, { recursive: true });
36700
37839
  for (const entry of entries) {
36701
- const srcEntry = path14.join(srcDir, entry);
36702
- const destEntry = path14.join(destDir, entry);
37840
+ const srcEntry = path17.join(srcDir, entry);
37841
+ const destEntry = path17.join(destDir, entry);
36703
37842
  try {
36704
37843
  const stat2 = await fs7.stat(srcEntry);
36705
37844
  if (stat2.isDirectory()) {
36706
37845
  await fs7.mkdir(destEntry, { recursive: true });
36707
37846
  const subEntries = await fs7.readdir(srcEntry);
36708
37847
  for (const sub of subEntries) {
36709
- await fs7.copyFile(path14.join(srcEntry, sub), path14.join(destEntry, sub)).catch(() => {});
37848
+ await fs7.copyFile(path17.join(srcEntry, sub), path17.join(destEntry, sub)).catch(() => {});
36710
37849
  }
36711
37850
  } else {
36712
37851
  await fs7.copyFile(srcEntry, destEntry);
@@ -36738,7 +37877,7 @@ async function handleCloseCommand(directory, args) {
36738
37877
  warnings.push(`Preserved ${artifact} because it was not successfully archived.`);
36739
37878
  continue;
36740
37879
  }
36741
- const filePath = path14.join(swarmDir, artifact);
37880
+ const filePath = path17.join(swarmDir, artifact);
36742
37881
  try {
36743
37882
  await fs7.unlink(filePath);
36744
37883
  cleanedFiles.push(artifact);
@@ -36751,7 +37890,7 @@ async function handleCloseCommand(directory, args) {
36751
37890
  if (!archivedActiveStateDirs.has(dirName)) {
36752
37891
  continue;
36753
37892
  }
36754
- const dirPath = path14.join(swarmDir, dirName);
37893
+ const dirPath = path17.join(swarmDir, dirName);
36755
37894
  try {
36756
37895
  await fs7.rm(dirPath, { recursive: true, force: true });
36757
37896
  cleanedFiles.push(`${dirName}/`);
@@ -36762,23 +37901,23 @@ async function handleCloseCommand(directory, args) {
36762
37901
  const configBackups = swarmFiles.filter((f) => f.startsWith("config-backup-") && f.endsWith(".json"));
36763
37902
  for (const backup of configBackups) {
36764
37903
  try {
36765
- await fs7.unlink(path14.join(swarmDir, backup));
37904
+ await fs7.unlink(path17.join(swarmDir, backup));
36766
37905
  configBackupsRemoved++;
36767
37906
  } catch {}
36768
37907
  }
36769
37908
  const ledgerSiblings = swarmFiles.filter((f) => (f.startsWith("plan-ledger.archived-") || f.startsWith("plan-ledger.backup-")) && f.endsWith(".jsonl"));
36770
37909
  for (const sibling of ledgerSiblings) {
36771
37910
  try {
36772
- await fs7.unlink(path14.join(swarmDir, sibling));
37911
+ await fs7.unlink(path17.join(swarmDir, sibling));
36773
37912
  } catch {}
36774
37913
  }
36775
37914
  } catch {}
36776
37915
  let swarmPlanFilesRemoved = 0;
36777
37916
  const candidates = [
36778
- path14.join(directory, ".swarm", "SWARM_PLAN.json"),
36779
- path14.join(directory, ".swarm", "SWARM_PLAN.md"),
36780
- path14.join(directory, "SWARM_PLAN.json"),
36781
- path14.join(directory, "SWARM_PLAN.md")
37917
+ path17.join(directory, ".swarm", "SWARM_PLAN.json"),
37918
+ path17.join(directory, ".swarm", "SWARM_PLAN.md"),
37919
+ path17.join(directory, "SWARM_PLAN.json"),
37920
+ path17.join(directory, "SWARM_PLAN.md")
36782
37921
  ];
36783
37922
  for (const candidate of candidates) {
36784
37923
  try {
@@ -36786,12 +37925,12 @@ async function handleCloseCommand(directory, args) {
36786
37925
  swarmPlanFilesRemoved++;
36787
37926
  } catch (err) {
36788
37927
  if (err?.code !== "ENOENT") {
36789
- warnings.push(`Failed to remove ${path14.basename(candidate)}: ${err instanceof Error ? err.message : String(err)}`);
37928
+ warnings.push(`Failed to remove ${path17.basename(candidate)}: ${err instanceof Error ? err.message : String(err)}`);
36790
37929
  }
36791
37930
  }
36792
37931
  }
36793
37932
  clearAllScopes(directory);
36794
- const contextPath = path14.join(swarmDir, "context.md");
37933
+ const contextPath = path17.join(swarmDir, "context.md");
36795
37934
  const contextContent = [
36796
37935
  "# Context",
36797
37936
  "",
@@ -36864,6 +38003,12 @@ async function handleCloseCommand(directory, args) {
36864
38003
  "## Lessons Committed",
36865
38004
  allLessons.length > 0 ? `| # | Lesson |` : "_No lessons committed_",
36866
38005
  ...allLessons.length > 0 ? ["| --- | --- |", ...allLessons.map((l, i) => `| ${i + 1} | ${l} |`)] : [],
38006
+ ...knowledgeSkillHint ? ["", knowledgeSkillHint] : [],
38007
+ ...runSkillReview ? [
38008
+ "",
38009
+ "## Skill Review",
38010
+ skillReviewSummary || "Skill review completed without details."
38011
+ ] : [],
36867
38012
  "",
36868
38013
  "## Local Repo State",
36869
38014
  ...gitAlignResult ? [`- **Git:** ${gitAlignResult}`] : ["- Git alignment skipped"],
@@ -36894,11 +38039,13 @@ async function handleCloseCommand(directory, args) {
36894
38039
  const preservedFullAutoFlag = swarmState.fullAutoEnabledInConfig;
36895
38040
  const preservedCuratorInitNames = swarmState.curatorInitAgentNames;
36896
38041
  const preservedCuratorPhaseNames = swarmState.curatorPhaseAgentNames;
38042
+ const preservedSkillImproverAgentNames = swarmState.skillImproverAgentNames;
36897
38043
  resetSwarmState();
36898
38044
  swarmState.opencodeClient = preservedClient;
36899
38045
  swarmState.fullAutoEnabledInConfig = preservedFullAutoFlag;
36900
38046
  swarmState.curatorInitAgentNames = preservedCuratorInitNames;
36901
38047
  swarmState.curatorPhaseAgentNames = preservedCuratorPhaseNames;
38048
+ swarmState.skillImproverAgentNames = preservedSkillImproverAgentNames;
36902
38049
  const retroWarnings = warnings.filter((w) => w.includes("Retrospective write") || w.includes("retrospective write") || w.includes("Session retrospective"));
36903
38050
  const otherWarnings = warnings.filter((w) => !w.includes("Retrospective write") && !w.includes("retrospective write") && !w.includes("Session retrospective"));
36904
38051
  let warningMsg = "";
@@ -36919,19 +38066,26 @@ ${otherWarnings.map((w) => `- ${w}`).join(`
36919
38066
  const lessonSummary = curationSucceeded && allLessons.length > 0 ? `
36920
38067
 
36921
38068
  **Lessons Committed:** ${allLessons.length} lesson(s) committed to knowledge store` : "";
38069
+ const knowledgeHintSummary = knowledgeSkillHint ? `
38070
+
38071
+ **Knowledge Review:** ${knowledgeSkillHint}` : "";
38072
+ const skillReviewOutput = skillReviewSummary ? `
38073
+
38074
+ **Skill Review:** ${skillReviewSummary}` : "";
36922
38075
  if (planAlreadyDone) {
36923
38076
  return `\u2705 Session finalized. Plan was already in a terminal state \u2014 cleanup and archive applied.
36924
38077
 
36925
38078
  **Archive:** ${archiveResult}
36926
- **Git:** ${gitAlignResult}${lessonSummary}${warningMsg}`;
38079
+ **Git:** ${gitAlignResult}${lessonSummary}${knowledgeHintSummary}${skillReviewOutput}${warningMsg}`;
36927
38080
  }
36928
38081
  return `\u2705 Swarm finalized. ${closedPhases.length} phase(s) closed, ${closedTasks.length} incomplete task(s) marked closed.
36929
38082
 
36930
38083
  **Archive:** ${archiveResult}
36931
- **Git:** ${gitAlignResult}${lessonSummary}${warningMsg}`;
38084
+ **Git:** ${gitAlignResult}${lessonSummary}${knowledgeHintSummary}${skillReviewOutput}${warningMsg}`;
36932
38085
  }
36933
- var ARCHIVE_ARTIFACTS, ACTIVE_STATE_TO_CLEAN, ACTIVE_STATE_DIRS_TO_CLEAN;
38086
+ var CLOSE_SKILL_REVIEW_TIMEOUT_MS = 120000, ARCHIVE_ARTIFACTS, ACTIVE_STATE_TO_CLEAN, ACTIVE_STATE_DIRS_TO_CLEAN;
36934
38087
  var init_close = __esm(() => {
38088
+ init_config();
36935
38089
  init_schema();
36936
38090
  init_manager2();
36937
38091
  init_branch();
@@ -36940,6 +38094,7 @@ var init_close = __esm(() => {
36940
38094
  init_knowledge_store();
36941
38095
  init_utils2();
36942
38096
  init_scope_persistence();
38097
+ init_skill_improver();
36943
38098
  init_state();
36944
38099
  init_write_retro();
36945
38100
  ARCHIVE_ARTIFACTS = [
@@ -36995,14 +38150,14 @@ var init_close = __esm(() => {
36995
38150
 
36996
38151
  // src/commands/config.ts
36997
38152
  import * as os4 from "os";
36998
- import * as path15 from "path";
38153
+ import * as path18 from "path";
36999
38154
  function getUserConfigDir2() {
37000
- return process.env.XDG_CONFIG_HOME || path15.join(os4.homedir(), ".config");
38155
+ return process.env.XDG_CONFIG_HOME || path18.join(os4.homedir(), ".config");
37001
38156
  }
37002
38157
  async function handleConfigCommand(directory, _args) {
37003
38158
  const config3 = loadPluginConfig(directory);
37004
- const userConfigPath = path15.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
37005
- const projectConfigPath = path15.join(directory, ".opencode", "opencode-swarm.json");
38159
+ const userConfigPath = path18.join(getUserConfigDir2(), "opencode", "opencode-swarm.json");
38160
+ const projectConfigPath = path18.join(directory, ".opencode", "opencode-swarm.json");
37006
38161
  const lines = [
37007
38162
  "## Swarm Configuration",
37008
38163
  "",
@@ -37128,8 +38283,8 @@ var init_curate = __esm(() => {
37128
38283
  // src/tools/co-change-analyzer.ts
37129
38284
  import * as child_process3 from "child_process";
37130
38285
  import { randomUUID } from "crypto";
37131
- import { readdir, readFile as readFile5, stat as stat2 } from "fs/promises";
37132
- import * as path16 from "path";
38286
+ import { readdir, readFile as readFile7, stat as stat2 } from "fs/promises";
38287
+ import * as path19 from "path";
37133
38288
  import { promisify } from "util";
37134
38289
  function getExecFileAsync() {
37135
38290
  return promisify(child_process3.execFile);
@@ -37231,7 +38386,7 @@ async function scanSourceFiles(dir) {
37231
38386
  try {
37232
38387
  const entries = await readdir(dir, { withFileTypes: true });
37233
38388
  for (const entry of entries) {
37234
- const fullPath = path16.join(dir, entry.name);
38389
+ const fullPath = path19.join(dir, entry.name);
37235
38390
  if (entry.isDirectory()) {
37236
38391
  if (skipDirs.has(entry.name)) {
37237
38392
  continue;
@@ -37239,7 +38394,7 @@ async function scanSourceFiles(dir) {
37239
38394
  const subFiles = await scanSourceFiles(fullPath);
37240
38395
  results.push(...subFiles);
37241
38396
  } else if (entry.isFile()) {
37242
- const ext = path16.extname(entry.name);
38397
+ const ext = path19.extname(entry.name);
37243
38398
  if ([".ts", ".tsx", ".js", ".jsx", ".mjs"].includes(ext)) {
37244
38399
  results.push(fullPath);
37245
38400
  }
@@ -37253,7 +38408,7 @@ async function getStaticEdges(directory) {
37253
38408
  const sourceFiles = await scanSourceFiles(directory);
37254
38409
  for (const sourceFile of sourceFiles) {
37255
38410
  try {
37256
- const content = await readFile5(sourceFile, "utf-8");
38411
+ const content = await readFile7(sourceFile, "utf-8");
37257
38412
  const importRegex = /(?:import|require)\s*(?:\(?\s*['"`]|.*?from\s+['"`])([^'"`]+)['"`]/g;
37258
38413
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
37259
38414
  const importPath = match[1].trim();
@@ -37261,8 +38416,8 @@ async function getStaticEdges(directory) {
37261
38416
  continue;
37262
38417
  }
37263
38418
  try {
37264
- const sourceDir = path16.dirname(sourceFile);
37265
- const resolvedPath = path16.resolve(sourceDir, importPath);
38419
+ const sourceDir = path19.dirname(sourceFile);
38420
+ const resolvedPath = path19.resolve(sourceDir, importPath);
37266
38421
  const extensions = [
37267
38422
  "",
37268
38423
  ".ts",
@@ -37287,8 +38442,8 @@ async function getStaticEdges(directory) {
37287
38442
  if (!targetFile) {
37288
38443
  continue;
37289
38444
  }
37290
- const relSource = path16.relative(directory, sourceFile).replace(/\\/g, "/");
37291
- const relTarget = path16.relative(directory, targetFile).replace(/\\/g, "/");
38445
+ const relSource = path19.relative(directory, sourceFile).replace(/\\/g, "/");
38446
+ const relTarget = path19.relative(directory, targetFile).replace(/\\/g, "/");
37292
38447
  const [key] = relSource < relTarget ? [`${relSource}::${relTarget}`, relSource, relTarget] : [`${relTarget}::${relSource}`, relTarget, relSource];
37293
38448
  edges.add(key);
37294
38449
  } catch {}
@@ -37300,7 +38455,7 @@ async function getStaticEdges(directory) {
37300
38455
  function isTestImplementationPair(fileA, fileB) {
37301
38456
  const testPatterns = [".test.ts", ".test.js", ".spec.ts", ".spec.js"];
37302
38457
  const getBaseName = (filePath) => {
37303
- const base = path16.basename(filePath);
38458
+ const base = path19.basename(filePath);
37304
38459
  for (const pattern of testPatterns) {
37305
38460
  if (base.endsWith(pattern)) {
37306
38461
  return base.slice(0, -pattern.length);
@@ -37310,16 +38465,16 @@ function isTestImplementationPair(fileA, fileB) {
37310
38465
  };
37311
38466
  const baseA = getBaseName(fileA);
37312
38467
  const baseB = getBaseName(fileB);
37313
- return baseA === baseB && baseA !== path16.basename(fileA) && baseA !== path16.basename(fileB);
38468
+ return baseA === baseB && baseA !== path19.basename(fileA) && baseA !== path19.basename(fileB);
37314
38469
  }
37315
38470
  function hasSharedPrefix(fileA, fileB) {
37316
- const dirA = path16.dirname(fileA);
37317
- const dirB = path16.dirname(fileB);
38471
+ const dirA = path19.dirname(fileA);
38472
+ const dirB = path19.dirname(fileB);
37318
38473
  if (dirA !== dirB) {
37319
38474
  return false;
37320
38475
  }
37321
- const baseA = path16.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
37322
- const baseB = path16.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
38476
+ const baseA = path19.basename(fileA).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
38477
+ const baseB = path19.basename(fileB).replace(/\.(ts|js|tsx|jsx|mjs)$/, "");
37323
38478
  if (baseA.startsWith(baseB) || baseB.startsWith(baseA)) {
37324
38479
  return true;
37325
38480
  }
@@ -37347,9 +38502,9 @@ async function detectDarkMatter(directory, options) {
37347
38502
  } catch {
37348
38503
  return [];
37349
38504
  }
37350
- const commitMap = await _internals9.parseGitLog(directory, maxCommitsToAnalyze);
37351
- const matrix = _internals9.buildCoChangeMatrix(commitMap);
37352
- const staticEdges = await _internals9.getStaticEdges(directory);
38505
+ const commitMap = await _internals10.parseGitLog(directory, maxCommitsToAnalyze);
38506
+ const matrix = _internals10.buildCoChangeMatrix(commitMap);
38507
+ const staticEdges = await _internals10.getStaticEdges(directory);
37353
38508
  const results = [];
37354
38509
  for (const entry of matrix.values()) {
37355
38510
  const key = `${entry.fileA}::${entry.fileB}`;
@@ -37373,8 +38528,8 @@ function darkMatterToKnowledgeEntries(pairs, projectName) {
37373
38528
  const entries = [];
37374
38529
  const now = new Date().toISOString();
37375
38530
  for (const pair of pairs.slice(0, 10)) {
37376
- const baseA = path16.basename(pair.fileA);
37377
- const baseB = path16.basename(pair.fileB);
38531
+ const baseA = path19.basename(pair.fileA);
38532
+ const baseB = path19.basename(pair.fileB);
37378
38533
  let lesson = `Files ${pair.fileA} and ${pair.fileB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
37379
38534
  if (lesson.length > 280) {
37380
38535
  lesson = `Files ${baseA} and ${baseB} co-change with NPMI=${pair.npmi.toFixed(3)} but have no import relationship. This hidden coupling suggests a shared architectural concern \u2014 changes to one likely require changes to the other.`;
@@ -37429,7 +38584,7 @@ ${rows}
37429
38584
  These pairs likely share an architectural concern invisible to static analysis.
37430
38585
  Consider adding explicit documentation or extracting the shared concern.`;
37431
38586
  }
37432
- var co_change_analyzer, _internals9;
38587
+ var co_change_analyzer, _internals10;
37433
38588
  var init_co_change_analyzer = __esm(() => {
37434
38589
  init_zod();
37435
38590
  init_create_tool();
@@ -37461,11 +38616,11 @@ var init_co_change_analyzer = __esm(() => {
37461
38616
  npmiThreshold,
37462
38617
  maxCommitsToAnalyze
37463
38618
  };
37464
- const pairs = await _internals9.detectDarkMatter(directory, options);
37465
- return _internals9.formatDarkMatterOutput(pairs);
38619
+ const pairs = await _internals10.detectDarkMatter(directory, options);
38620
+ return _internals10.formatDarkMatterOutput(pairs);
37466
38621
  }
37467
38622
  });
37468
- _internals9 = {
38623
+ _internals10 = {
37469
38624
  parseGitLog,
37470
38625
  buildCoChangeMatrix,
37471
38626
  getStaticEdges,
@@ -37476,7 +38631,7 @@ var init_co_change_analyzer = __esm(() => {
37476
38631
  });
37477
38632
 
37478
38633
  // src/commands/dark-matter.ts
37479
- import path17 from "path";
38634
+ import path20 from "path";
37480
38635
  async function handleDarkMatterCommand(directory, args) {
37481
38636
  const options = {};
37482
38637
  for (let i = 0;i < args.length; i++) {
@@ -37496,7 +38651,7 @@ async function handleDarkMatterCommand(directory, args) {
37496
38651
  }
37497
38652
  let pairs;
37498
38653
  try {
37499
- pairs = await _internals9.detectDarkMatter(directory, options);
38654
+ pairs = await _internals10.detectDarkMatter(directory, options);
37500
38655
  } catch (err) {
37501
38656
  const errMsg = err instanceof Error ? err.message : String(err);
37502
38657
  return `## Dark Matter Analysis Failed
@@ -37508,7 +38663,7 @@ Ensure this is a git repository with commit history.`;
37508
38663
  const output = formatDarkMatterOutput(pairs);
37509
38664
  if (pairs.length > 0) {
37510
38665
  try {
37511
- const projectName = path17.basename(path17.resolve(directory));
38666
+ const projectName = path20.basename(path20.resolve(directory));
37512
38667
  const entries = darkMatterToKnowledgeEntries(pairs, projectName);
37513
38668
  if (entries.length > 0) {
37514
38669
  const knowledgePath = resolveSwarmKnowledgePath(directory);
@@ -37645,67 +38800,67 @@ var init_deep_dive = __esm(() => {
37645
38800
 
37646
38801
  // src/config/cache-paths.ts
37647
38802
  import * as os5 from "os";
37648
- import * as path18 from "path";
38803
+ import * as path21 from "path";
37649
38804
  function getPluginConfigDir() {
37650
- return path18.join(process.env.XDG_CONFIG_HOME || path18.join(os5.homedir(), ".config"), "opencode");
38805
+ return path21.join(process.env.XDG_CONFIG_HOME || path21.join(os5.homedir(), ".config"), "opencode");
37651
38806
  }
37652
38807
  function getPluginCachePaths() {
37653
- const cacheBase = process.env.XDG_CACHE_HOME || path18.join(os5.homedir(), ".cache");
38808
+ const cacheBase = process.env.XDG_CACHE_HOME || path21.join(os5.homedir(), ".cache");
37654
38809
  const configDir = getPluginConfigDir();
37655
38810
  const paths = [
37656
- path18.join(cacheBase, "opencode", "node_modules", "opencode-swarm"),
37657
- path18.join(cacheBase, "opencode", "packages", "opencode-swarm@latest"),
37658
- path18.join(configDir, "node_modules", "opencode-swarm")
38811
+ path21.join(cacheBase, "opencode", "node_modules", "opencode-swarm"),
38812
+ path21.join(cacheBase, "opencode", "packages", "opencode-swarm@latest"),
38813
+ path21.join(configDir, "node_modules", "opencode-swarm")
37659
38814
  ];
37660
38815
  if (process.platform === "darwin") {
37661
- const libCaches = path18.join(os5.homedir(), "Library", "Caches");
37662
- paths.push(path18.join(libCaches, "opencode", "node_modules", "opencode-swarm"), path18.join(libCaches, "opencode", "packages", "opencode-swarm@latest"));
38816
+ const libCaches = path21.join(os5.homedir(), "Library", "Caches");
38817
+ paths.push(path21.join(libCaches, "opencode", "node_modules", "opencode-swarm"), path21.join(libCaches, "opencode", "packages", "opencode-swarm@latest"));
37663
38818
  }
37664
38819
  if (process.platform === "win32") {
37665
- const localAppData = process.env.LOCALAPPDATA || path18.join(os5.homedir(), "AppData", "Local");
37666
- const appData = process.env.APPDATA || path18.join(os5.homedir(), "AppData", "Roaming");
37667
- paths.push(path18.join(localAppData, "opencode", "node_modules", "opencode-swarm"), path18.join(localAppData, "opencode", "packages", "opencode-swarm@latest"), path18.join(appData, "opencode", "node_modules", "opencode-swarm"));
38820
+ const localAppData = process.env.LOCALAPPDATA || path21.join(os5.homedir(), "AppData", "Local");
38821
+ const appData = process.env.APPDATA || path21.join(os5.homedir(), "AppData", "Roaming");
38822
+ paths.push(path21.join(localAppData, "opencode", "node_modules", "opencode-swarm"), path21.join(localAppData, "opencode", "packages", "opencode-swarm@latest"), path21.join(appData, "opencode", "node_modules", "opencode-swarm"));
37668
38823
  }
37669
38824
  return paths;
37670
38825
  }
37671
38826
  function getPluginLockFilePaths() {
37672
- const cacheBase = process.env.XDG_CACHE_HOME || path18.join(os5.homedir(), ".cache");
38827
+ const cacheBase = process.env.XDG_CACHE_HOME || path21.join(os5.homedir(), ".cache");
37673
38828
  const configDir = getPluginConfigDir();
37674
38829
  const paths = [
37675
- path18.join(cacheBase, "opencode", "bun.lock"),
37676
- path18.join(cacheBase, "opencode", "bun.lockb"),
37677
- path18.join(configDir, "package-lock.json")
38830
+ path21.join(cacheBase, "opencode", "bun.lock"),
38831
+ path21.join(cacheBase, "opencode", "bun.lockb"),
38832
+ path21.join(configDir, "package-lock.json")
37678
38833
  ];
37679
38834
  if (process.platform === "darwin") {
37680
- const libCaches = path18.join(os5.homedir(), "Library", "Caches");
37681
- paths.push(path18.join(libCaches, "opencode", "bun.lock"), path18.join(libCaches, "opencode", "bun.lockb"));
38835
+ const libCaches = path21.join(os5.homedir(), "Library", "Caches");
38836
+ paths.push(path21.join(libCaches, "opencode", "bun.lock"), path21.join(libCaches, "opencode", "bun.lockb"));
37682
38837
  }
37683
38838
  if (process.platform === "win32") {
37684
- const localAppData = process.env.LOCALAPPDATA || path18.join(os5.homedir(), "AppData", "Local");
37685
- paths.push(path18.join(localAppData, "opencode", "bun.lock"), path18.join(localAppData, "opencode", "bun.lockb"));
38839
+ const localAppData = process.env.LOCALAPPDATA || path21.join(os5.homedir(), "AppData", "Local");
38840
+ paths.push(path21.join(localAppData, "opencode", "bun.lock"), path21.join(localAppData, "opencode", "bun.lockb"));
37686
38841
  }
37687
38842
  return paths;
37688
38843
  }
37689
38844
  var init_cache_paths = () => {};
37690
38845
 
37691
38846
  // src/services/version-check.ts
37692
- import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
38847
+ import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
37693
38848
  import { homedir as homedir5 } from "os";
37694
- import { join as join17 } from "path";
38849
+ import { join as join20 } from "path";
37695
38850
  function cacheDir() {
37696
38851
  const xdg = process.env.XDG_CACHE_HOME;
37697
- const base = xdg && xdg.length > 0 ? xdg : join17(homedir5(), ".cache");
37698
- return join17(base, "opencode-swarm");
38852
+ const base = xdg && xdg.length > 0 ? xdg : join20(homedir5(), ".cache");
38853
+ return join20(base, "opencode-swarm");
37699
38854
  }
37700
38855
  function cacheFile() {
37701
- return join17(cacheDir(), "version-check.json");
38856
+ return join20(cacheDir(), "version-check.json");
37702
38857
  }
37703
38858
  function readVersionCache() {
37704
38859
  try {
37705
- const path19 = cacheFile();
37706
- if (!existsSync9(path19))
38860
+ const path22 = cacheFile();
38861
+ if (!existsSync12(path22))
37707
38862
  return null;
37708
- const raw = readFileSync5(path19, "utf-8");
38863
+ const raw = readFileSync6(path22, "utf-8");
37709
38864
  const parsed = JSON.parse(raw);
37710
38865
  if (typeof parsed?.checkedAt !== "number")
37711
38866
  return null;
@@ -37744,8 +38899,8 @@ var init_version_check = __esm(() => {
37744
38899
 
37745
38900
  // src/services/diagnose-service.ts
37746
38901
  import * as child_process4 from "child_process";
37747
- import { existsSync as existsSync10, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync6 } from "fs";
37748
- import path19 from "path";
38902
+ import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync6 } from "fs";
38903
+ import path22 from "path";
37749
38904
  import { fileURLToPath } from "url";
37750
38905
  function validateTaskDag(plan) {
37751
38906
  const allTaskIds = new Set;
@@ -37978,7 +39133,7 @@ async function checkConfigBackups(directory) {
37978
39133
  }
37979
39134
  async function checkGitRepository(directory) {
37980
39135
  try {
37981
- if (!existsSync10(directory) || !statSync6(directory).isDirectory()) {
39136
+ if (!existsSync13(directory) || !statSync6(directory).isDirectory()) {
37982
39137
  return {
37983
39138
  name: "Git Repository",
37984
39139
  status: "\u274C",
@@ -38042,8 +39197,8 @@ async function checkSpecStaleness(directory, plan) {
38042
39197
  };
38043
39198
  }
38044
39199
  async function checkConfigParseability(directory) {
38045
- const configPath = path19.join(directory, ".opencode/opencode-swarm.json");
38046
- if (!existsSync10(configPath)) {
39200
+ const configPath = path22.join(directory, ".opencode/opencode-swarm.json");
39201
+ if (!existsSync13(configPath)) {
38047
39202
  return {
38048
39203
  name: "Config Parseability",
38049
39204
  status: "\u2705",
@@ -38051,7 +39206,7 @@ async function checkConfigParseability(directory) {
38051
39206
  };
38052
39207
  }
38053
39208
  try {
38054
- const content = readFileSync6(configPath, "utf-8");
39209
+ const content = readFileSync7(configPath, "utf-8");
38055
39210
  JSON.parse(content);
38056
39211
  return {
38057
39212
  name: "Config Parseability",
@@ -38071,7 +39226,7 @@ function resolveGrammarDir(thisDir) {
38071
39226
  const normalized = thisDir.replace(/\\/g, "/");
38072
39227
  const isSource = normalized.endsWith("/src/services");
38073
39228
  const isCliBundle = normalized.endsWith("/cli");
38074
- return isSource || isCliBundle ? path19.join(thisDir, "..", "lang", "grammars") : path19.join(thisDir, "lang", "grammars");
39229
+ return isSource || isCliBundle ? path22.join(thisDir, "..", "lang", "grammars") : path22.join(thisDir, "lang", "grammars");
38075
39230
  }
38076
39231
  async function checkGrammarWasmFiles() {
38077
39232
  const grammarFiles = [
@@ -38095,14 +39250,14 @@ async function checkGrammarWasmFiles() {
38095
39250
  "tree-sitter-ini.wasm",
38096
39251
  "tree-sitter-regex.wasm"
38097
39252
  ];
38098
- const thisDir = path19.dirname(fileURLToPath(import.meta.url));
39253
+ const thisDir = path22.dirname(fileURLToPath(import.meta.url));
38099
39254
  const grammarDir = resolveGrammarDir(thisDir);
38100
39255
  const missing = [];
38101
- if (!existsSync10(path19.join(grammarDir, "tree-sitter.wasm"))) {
39256
+ if (!existsSync13(path22.join(grammarDir, "tree-sitter.wasm"))) {
38102
39257
  missing.push("tree-sitter.wasm (core runtime)");
38103
39258
  }
38104
39259
  for (const file3 of grammarFiles) {
38105
- if (!existsSync10(path19.join(grammarDir, file3))) {
39260
+ if (!existsSync13(path22.join(grammarDir, file3))) {
38106
39261
  missing.push(file3);
38107
39262
  }
38108
39263
  }
@@ -38120,8 +39275,8 @@ async function checkGrammarWasmFiles() {
38120
39275
  };
38121
39276
  }
38122
39277
  async function checkCheckpointManifest(directory) {
38123
- const manifestPath = path19.join(directory, ".swarm/checkpoints.json");
38124
- if (!existsSync10(manifestPath)) {
39278
+ const manifestPath = path22.join(directory, ".swarm/checkpoints.json");
39279
+ if (!existsSync13(manifestPath)) {
38125
39280
  return {
38126
39281
  name: "Checkpoint Manifest",
38127
39282
  status: "\u2705",
@@ -38129,7 +39284,7 @@ async function checkCheckpointManifest(directory) {
38129
39284
  };
38130
39285
  }
38131
39286
  try {
38132
- const content = readFileSync6(manifestPath, "utf-8");
39287
+ const content = readFileSync7(manifestPath, "utf-8");
38133
39288
  const parsed = JSON.parse(content);
38134
39289
  if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
38135
39290
  return {
@@ -38172,8 +39327,8 @@ async function checkCheckpointManifest(directory) {
38172
39327
  }
38173
39328
  }
38174
39329
  async function checkEventStreamIntegrity(directory) {
38175
- const eventsPath = path19.join(directory, ".swarm/events.jsonl");
38176
- if (!existsSync10(eventsPath)) {
39330
+ const eventsPath = path22.join(directory, ".swarm/events.jsonl");
39331
+ if (!existsSync13(eventsPath)) {
38177
39332
  return {
38178
39333
  name: "Event Stream",
38179
39334
  status: "\u2705",
@@ -38181,7 +39336,7 @@ async function checkEventStreamIntegrity(directory) {
38181
39336
  };
38182
39337
  }
38183
39338
  try {
38184
- const content = readFileSync6(eventsPath, "utf-8");
39339
+ const content = readFileSync7(eventsPath, "utf-8");
38185
39340
  const lines = content.split(`
38186
39341
  `).filter((line) => line.trim() !== "");
38187
39342
  let malformedCount = 0;
@@ -38213,8 +39368,8 @@ async function checkEventStreamIntegrity(directory) {
38213
39368
  }
38214
39369
  }
38215
39370
  async function checkSteeringDirectives(directory) {
38216
- const eventsPath = path19.join(directory, ".swarm/events.jsonl");
38217
- if (!existsSync10(eventsPath)) {
39371
+ const eventsPath = path22.join(directory, ".swarm/events.jsonl");
39372
+ if (!existsSync13(eventsPath)) {
38218
39373
  return {
38219
39374
  name: "Steering Directives",
38220
39375
  status: "\u2705",
@@ -38222,7 +39377,7 @@ async function checkSteeringDirectives(directory) {
38222
39377
  };
38223
39378
  }
38224
39379
  try {
38225
- const content = readFileSync6(eventsPath, "utf-8");
39380
+ const content = readFileSync7(eventsPath, "utf-8");
38226
39381
  const lines = content.split(`
38227
39382
  `).filter((line) => line.trim() !== "");
38228
39383
  const directivesIssued = [];
@@ -38269,8 +39424,8 @@ async function checkCurator(directory) {
38269
39424
  detail: "Disabled (enable via curator.enabled)"
38270
39425
  };
38271
39426
  }
38272
- const summaryPath = path19.join(directory, ".swarm/curator-summary.json");
38273
- if (!existsSync10(summaryPath)) {
39427
+ const summaryPath = path22.join(directory, ".swarm/curator-summary.json");
39428
+ if (!existsSync13(summaryPath)) {
38274
39429
  return {
38275
39430
  name: "Curator",
38276
39431
  status: "\u2705",
@@ -38278,7 +39433,7 @@ async function checkCurator(directory) {
38278
39433
  };
38279
39434
  }
38280
39435
  try {
38281
- const content = readFileSync6(summaryPath, "utf-8");
39436
+ const content = readFileSync7(summaryPath, "utf-8");
38282
39437
  const parsed = JSON.parse(content);
38283
39438
  if (typeof parsed.schema_version !== "number" || parsed.schema_version !== 1) {
38284
39439
  return {
@@ -38435,8 +39590,8 @@ async function getDiagnoseData(directory) {
38435
39590
  checks5.push(await checkSteeringDirectives(directory));
38436
39591
  checks5.push(await checkCurator(directory));
38437
39592
  try {
38438
- const evidenceDir = path19.join(directory, ".swarm", "evidence");
38439
- const snapshotFiles = existsSync10(evidenceDir) ? readdirSync4(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
39593
+ const evidenceDir = path22.join(directory, ".swarm", "evidence");
39594
+ const snapshotFiles = existsSync13(evidenceDir) ? readdirSync4(evidenceDir).filter((f) => f.startsWith("agent-tools-") && f.endsWith(".json")) : [];
38440
39595
  if (snapshotFiles.length > 0) {
38441
39596
  const latest = snapshotFiles.sort().pop();
38442
39597
  checks5.push({
@@ -38469,13 +39624,13 @@ async function getDiagnoseData(directory) {
38469
39624
  const cacheRows = [];
38470
39625
  for (const cachePath of cachePaths) {
38471
39626
  try {
38472
- if (!existsSync10(cachePath)) {
39627
+ if (!existsSync13(cachePath)) {
38473
39628
  cacheRows.push(`\u2B1C ${cachePath} \u2014 absent`);
38474
39629
  continue;
38475
39630
  }
38476
- const pkgJsonPath = path19.join(cachePath, "package.json");
39631
+ const pkgJsonPath = path22.join(cachePath, "package.json");
38477
39632
  try {
38478
- const raw = readFileSync6(pkgJsonPath, "utf-8");
39633
+ const raw = readFileSync7(pkgJsonPath, "utf-8");
38479
39634
  const parsed = JSON.parse(raw);
38480
39635
  const installedVersion = typeof parsed.version === "string" ? parsed.version : "?";
38481
39636
  cacheRows.push(`\u2705 ${cachePath} \u2014 v${installedVersion}`);
@@ -38561,13 +39716,13 @@ __export(exports_config_doctor, {
38561
39716
  import * as crypto3 from "crypto";
38562
39717
  import * as fs8 from "fs";
38563
39718
  import * as os6 from "os";
38564
- import * as path20 from "path";
39719
+ import * as path23 from "path";
38565
39720
  function getUserConfigDir3() {
38566
- return process.env.XDG_CONFIG_HOME || path20.join(os6.homedir(), ".config");
39721
+ return process.env.XDG_CONFIG_HOME || path23.join(os6.homedir(), ".config");
38567
39722
  }
38568
39723
  function getConfigPaths(directory) {
38569
- const userConfigPath = path20.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
38570
- const projectConfigPath = path20.join(directory, ".opencode", "opencode-swarm.json");
39724
+ const userConfigPath = path23.join(getUserConfigDir3(), "opencode", "opencode-swarm.json");
39725
+ const projectConfigPath = path23.join(directory, ".opencode", "opencode-swarm.json");
38571
39726
  return { userConfigPath, projectConfigPath };
38572
39727
  }
38573
39728
  function computeHash(content) {
@@ -38592,9 +39747,9 @@ function isValidConfigPath(configPath, directory) {
38592
39747
  const normalizedUser = userConfigPath.replace(/\\/g, "/");
38593
39748
  const normalizedProject = projectConfigPath.replace(/\\/g, "/");
38594
39749
  try {
38595
- const resolvedConfig = path20.resolve(configPath);
38596
- const resolvedUser = path20.resolve(normalizedUser);
38597
- const resolvedProject = path20.resolve(normalizedProject);
39750
+ const resolvedConfig = path23.resolve(configPath);
39751
+ const resolvedUser = path23.resolve(normalizedUser);
39752
+ const resolvedProject = path23.resolve(normalizedProject);
38598
39753
  return resolvedConfig === resolvedUser || resolvedConfig === resolvedProject;
38599
39754
  } catch {
38600
39755
  return false;
@@ -38634,12 +39789,12 @@ function createConfigBackup(directory) {
38634
39789
  };
38635
39790
  }
38636
39791
  function writeBackupArtifact(directory, backup) {
38637
- const swarmDir = path20.join(directory, ".swarm");
39792
+ const swarmDir = path23.join(directory, ".swarm");
38638
39793
  if (!fs8.existsSync(swarmDir)) {
38639
39794
  fs8.mkdirSync(swarmDir, { recursive: true });
38640
39795
  }
38641
39796
  const backupFilename = `config-backup-${backup.createdAt}.json`;
38642
- const backupPath = path20.join(swarmDir, backupFilename);
39797
+ const backupPath = path23.join(swarmDir, backupFilename);
38643
39798
  const artifact = {
38644
39799
  createdAt: backup.createdAt,
38645
39800
  configPath: backup.configPath,
@@ -38669,7 +39824,7 @@ function restoreFromBackup(backupPath, directory) {
38669
39824
  return null;
38670
39825
  }
38671
39826
  const targetPath = artifact.configPath;
38672
- const targetDir = path20.dirname(targetPath);
39827
+ const targetDir = path23.dirname(targetPath);
38673
39828
  if (!fs8.existsSync(targetDir)) {
38674
39829
  fs8.mkdirSync(targetDir, { recursive: true });
38675
39830
  }
@@ -38700,9 +39855,9 @@ function readConfigFromFile(directory) {
38700
39855
  return null;
38701
39856
  }
38702
39857
  }
38703
- function validateConfigKey(path21, value, _config) {
39858
+ function validateConfigKey(path24, value, _config) {
38704
39859
  const findings = [];
38705
- switch (path21) {
39860
+ switch (path24) {
38706
39861
  case "agents": {
38707
39862
  if (value !== undefined) {
38708
39863
  findings.push({
@@ -38949,27 +40104,27 @@ function validateConfigKey(path21, value, _config) {
38949
40104
  }
38950
40105
  return findings;
38951
40106
  }
38952
- function walkConfigAndValidate(obj, path21, config3, findings) {
40107
+ function walkConfigAndValidate(obj, path24, config3, findings) {
38953
40108
  if (obj === null || obj === undefined) {
38954
40109
  return;
38955
40110
  }
38956
- if (path21 && typeof obj === "object" && !Array.isArray(obj)) {
38957
- const keyFindings = validateConfigKey(path21, obj, config3);
40111
+ if (path24 && typeof obj === "object" && !Array.isArray(obj)) {
40112
+ const keyFindings = validateConfigKey(path24, obj, config3);
38958
40113
  findings.push(...keyFindings);
38959
40114
  }
38960
40115
  if (typeof obj !== "object") {
38961
- const keyFindings = validateConfigKey(path21, obj, config3);
40116
+ const keyFindings = validateConfigKey(path24, obj, config3);
38962
40117
  findings.push(...keyFindings);
38963
40118
  return;
38964
40119
  }
38965
40120
  if (Array.isArray(obj)) {
38966
40121
  obj.forEach((item, index) => {
38967
- walkConfigAndValidate(item, `${path21}[${index}]`, config3, findings);
40122
+ walkConfigAndValidate(item, `${path24}[${index}]`, config3, findings);
38968
40123
  });
38969
40124
  return;
38970
40125
  }
38971
40126
  for (const [key, value] of Object.entries(obj)) {
38972
- const newPath = path21 ? `${path21}.${key}` : key;
40127
+ const newPath = path24 ? `${path24}.${key}` : key;
38973
40128
  walkConfigAndValidate(value, newPath, config3, findings);
38974
40129
  }
38975
40130
  }
@@ -39089,7 +40244,7 @@ function applySafeAutoFixes(directory, result) {
39089
40244
  }
39090
40245
  }
39091
40246
  if (appliedFixes.length > 0) {
39092
- const configDir = path20.dirname(configPath);
40247
+ const configDir = path23.dirname(configPath);
39093
40248
  if (!fs8.existsSync(configDir)) {
39094
40249
  fs8.mkdirSync(configDir, { recursive: true });
39095
40250
  }
@@ -39099,12 +40254,12 @@ function applySafeAutoFixes(directory, result) {
39099
40254
  return { appliedFixes, updatedConfigPath };
39100
40255
  }
39101
40256
  function writeDoctorArtifact(directory, result) {
39102
- const swarmDir = path20.join(directory, ".swarm");
40257
+ const swarmDir = path23.join(directory, ".swarm");
39103
40258
  if (!fs8.existsSync(swarmDir)) {
39104
40259
  fs8.mkdirSync(swarmDir, { recursive: true });
39105
40260
  }
39106
40261
  const artifactFilename = "config-doctor.json";
39107
- const artifactPath = path20.join(swarmDir, artifactFilename);
40262
+ const artifactPath = path23.join(swarmDir, artifactFilename);
39108
40263
  const guiOutput = {
39109
40264
  timestamp: result.timestamp,
39110
40265
  summary: result.summary,
@@ -40151,7 +41306,7 @@ var init_profiles = __esm(() => {
40151
41306
 
40152
41307
  // src/lang/detector.ts
40153
41308
  import { access as access3, readdir as readdir2 } from "fs/promises";
40154
- import { extname as extname2, join as join19 } from "path";
41309
+ import { extname as extname2, join as join22 } from "path";
40155
41310
  async function detectProjectLanguages(projectDir) {
40156
41311
  const detected = new Set;
40157
41312
  async function scanDir(dir) {
@@ -40167,7 +41322,7 @@ async function detectProjectLanguages(projectDir) {
40167
41322
  if (detectFile.includes("*") || detectFile.includes("?"))
40168
41323
  continue;
40169
41324
  try {
40170
- await access3(join19(dir, detectFile));
41325
+ await access3(join22(dir, detectFile));
40171
41326
  detected.add(profile.id);
40172
41327
  break;
40173
41328
  } catch {}
@@ -40188,7 +41343,7 @@ async function detectProjectLanguages(projectDir) {
40188
41343
  const topEntries = await readdir2(projectDir, { withFileTypes: true });
40189
41344
  for (const entry of topEntries) {
40190
41345
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
40191
- await scanDir(join19(projectDir, entry.name));
41346
+ await scanDir(join22(projectDir, entry.name));
40192
41347
  }
40193
41348
  }
40194
41349
  } catch {}
@@ -40207,7 +41362,7 @@ var init_detector = __esm(() => {
40207
41362
 
40208
41363
  // src/build/discovery.ts
40209
41364
  import * as fs9 from "fs";
40210
- import * as path21 from "path";
41365
+ import * as path24 from "path";
40211
41366
  function isCommandAvailable(command) {
40212
41367
  if (toolchainCache.has(command)) {
40213
41368
  return toolchainCache.get(command);
@@ -40239,11 +41394,11 @@ function findBuildFiles(workingDir, patterns) {
40239
41394
  const regex = simpleGlobToRegex(pattern);
40240
41395
  const matches = files.filter((f) => regex.test(f));
40241
41396
  if (matches.length > 0) {
40242
- return path21.join(dir, matches[0]);
41397
+ return path24.join(dir, matches[0]);
40243
41398
  }
40244
41399
  } catch {}
40245
41400
  } else {
40246
- const filePath = path21.join(workingDir, pattern);
41401
+ const filePath = path24.join(workingDir, pattern);
40247
41402
  if (fs9.existsSync(filePath)) {
40248
41403
  return filePath;
40249
41404
  }
@@ -40252,7 +41407,7 @@ function findBuildFiles(workingDir, patterns) {
40252
41407
  return null;
40253
41408
  }
40254
41409
  function getRepoDefinedScripts(workingDir, scripts) {
40255
- const packageJsonPath = path21.join(workingDir, "package.json");
41410
+ const packageJsonPath = path24.join(workingDir, "package.json");
40256
41411
  if (!fs9.existsSync(packageJsonPath)) {
40257
41412
  return [];
40258
41413
  }
@@ -40293,7 +41448,7 @@ function findAllBuildFiles(workingDir) {
40293
41448
  const regex = simpleGlobToRegex(pattern);
40294
41449
  findFilesRecursive(workingDir, regex, allBuildFiles);
40295
41450
  } else {
40296
- const filePath = path21.join(workingDir, pattern);
41451
+ const filePath = path24.join(workingDir, pattern);
40297
41452
  if (fs9.existsSync(filePath)) {
40298
41453
  allBuildFiles.add(filePath);
40299
41454
  }
@@ -40306,7 +41461,7 @@ function findFilesRecursive(dir, regex, results) {
40306
41461
  try {
40307
41462
  const entries = fs9.readdirSync(dir, { withFileTypes: true });
40308
41463
  for (const entry of entries) {
40309
- const fullPath = path21.join(dir, entry.name);
41464
+ const fullPath = path24.join(dir, entry.name);
40310
41465
  if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
40311
41466
  findFilesRecursive(fullPath, regex, results);
40312
41467
  } else if (entry.isFile() && regex.test(entry.name)) {
@@ -40329,7 +41484,7 @@ async function discoverBuildCommandsFromProfiles(workingDir) {
40329
41484
  let foundCommand = false;
40330
41485
  for (const cmd of sortedCommands) {
40331
41486
  if (cmd.detectFile) {
40332
- const detectFilePath = path21.join(workingDir, cmd.detectFile);
41487
+ const detectFilePath = path24.join(workingDir, cmd.detectFile);
40333
41488
  if (!fs9.existsSync(detectFilePath)) {
40334
41489
  continue;
40335
41490
  }
@@ -40362,7 +41517,7 @@ async function discoverBuildCommands(workingDir, options) {
40362
41517
  const scope = options?.scope ?? "all";
40363
41518
  const changedFiles = options?.changedFiles ?? [];
40364
41519
  const _filesToCheck = filterByScope(workingDir, scope, changedFiles);
40365
- const profileResult = await _internals10.discoverBuildCommandsFromProfiles(workingDir);
41520
+ const profileResult = await _internals11.discoverBuildCommandsFromProfiles(workingDir);
40366
41521
  const profileCommands = profileResult.commands;
40367
41522
  const profileSkipped = profileResult.skipped;
40368
41523
  const coveredEcosystems = new Set;
@@ -40425,7 +41580,7 @@ function clearToolchainCache() {
40425
41580
  function getEcosystems() {
40426
41581
  return ECOSYSTEMS.map((e) => e.ecosystem);
40427
41582
  }
40428
- var ECOSYSTEMS, PROFILE_TO_ECOSYSTEM_NAMES, toolchainCache, _internals10, build_discovery;
41583
+ var ECOSYSTEMS, PROFILE_TO_ECOSYSTEM_NAMES, toolchainCache, _internals11, build_discovery;
40429
41584
  var init_discovery = __esm(() => {
40430
41585
  init_dist();
40431
41586
  init_detector();
@@ -40543,7 +41698,7 @@ var init_discovery = __esm(() => {
40543
41698
  php: ["php-composer"]
40544
41699
  };
40545
41700
  toolchainCache = new Map;
40546
- _internals10 = {
41701
+ _internals11 = {
40547
41702
  isCommandAvailable,
40548
41703
  discoverBuildCommandsFromProfiles,
40549
41704
  discoverBuildCommands,
@@ -40569,7 +41724,7 @@ var init_discovery = __esm(() => {
40569
41724
 
40570
41725
  // src/services/tool-doctor.ts
40571
41726
  import * as fs10 from "fs";
40572
- import * as path22 from "path";
41727
+ import * as path25 from "path";
40573
41728
  function extractRegisteredToolKeys(indexPath) {
40574
41729
  const registeredKeys = new Set;
40575
41730
  try {
@@ -40624,8 +41779,8 @@ function checkBinaryReadiness() {
40624
41779
  }
40625
41780
  function runToolDoctor(_directory, pluginRoot) {
40626
41781
  const findings = [];
40627
- const resolvedPluginRoot = pluginRoot ?? path22.resolve(import.meta.dir, "..", "..");
40628
- const indexPath = path22.join(resolvedPluginRoot, "src", "index.ts");
41782
+ const resolvedPluginRoot = pluginRoot ?? path25.resolve(import.meta.dir, "..", "..");
41783
+ const indexPath = path25.join(resolvedPluginRoot, "src", "index.ts");
40629
41784
  if (!fs10.existsSync(indexPath)) {
40630
41785
  return {
40631
41786
  findings: [
@@ -40795,7 +41950,7 @@ var exports_evidence_summary_service = {};
40795
41950
  __export(exports_evidence_summary_service, {
40796
41951
  isAutoSummaryEnabled: () => isAutoSummaryEnabled,
40797
41952
  buildEvidenceSummary: () => buildEvidenceSummary,
40798
- _internals: () => _internals11,
41953
+ _internals: () => _internals12,
40799
41954
  REQUIRED_EVIDENCE_TYPES: () => REQUIRED_EVIDENCE_TYPES,
40800
41955
  EVIDENCE_SUMMARY_VERSION: () => EVIDENCE_SUMMARY_VERSION
40801
41956
  });
@@ -40833,14 +41988,14 @@ function getTaskStatus(task, bundle) {
40833
41988
  if (task?.status) {
40834
41989
  return task.status;
40835
41990
  }
40836
- const entries = _internals11.normalizeBundleEntries(bundle);
41991
+ const entries = _internals12.normalizeBundleEntries(bundle);
40837
41992
  if (entries.length > 0) {
40838
41993
  return "completed";
40839
41994
  }
40840
41995
  return "pending";
40841
41996
  }
40842
41997
  function isEvidenceComplete(bundle) {
40843
- const entries = _internals11.normalizeBundleEntries(bundle);
41998
+ const entries = _internals12.normalizeBundleEntries(bundle);
40844
41999
  if (entries.length === 0) {
40845
42000
  return {
40846
42001
  isComplete: false,
@@ -40876,10 +42031,10 @@ async function buildTaskSummary(directory, task, taskId) {
40876
42031
  const result = await loadEvidence(directory, taskId);
40877
42032
  const bundle = result.status === "found" ? result.bundle : null;
40878
42033
  const phase = task?.phase ?? 0;
40879
- const status = _internals11.getTaskStatus(task, bundle);
40880
- const evidenceCheck = _internals11.isEvidenceComplete(bundle);
40881
- const blockers = _internals11.getTaskBlockers(task, evidenceCheck, status);
40882
- const entries = _internals11.normalizeBundleEntries(bundle);
42034
+ const status = _internals12.getTaskStatus(task, bundle);
42035
+ const evidenceCheck = _internals12.isEvidenceComplete(bundle);
42036
+ const blockers = _internals12.getTaskBlockers(task, evidenceCheck, status);
42037
+ const entries = _internals12.normalizeBundleEntries(bundle);
40883
42038
  const hasReview = entries.some((e) => e.type === "review");
40884
42039
  const hasTest = entries.some((e) => e.type === "test");
40885
42040
  const hasApproval = entries.some((e) => e.type === "approval");
@@ -40908,12 +42063,12 @@ async function buildPhaseSummary(directory, phase) {
40908
42063
  const taskSummaries = [];
40909
42064
  const _taskMap = new Map(phase.tasks.map((t) => [t.id, t]));
40910
42065
  for (const task of phase.tasks) {
40911
- const summary = await _internals11.buildTaskSummary(directory, task, task.id);
42066
+ const summary = await _internals12.buildTaskSummary(directory, task, task.id);
40912
42067
  taskSummaries.push(summary);
40913
42068
  }
40914
42069
  const extraTaskIds = taskIds.filter((id) => !phaseTaskIds.has(id));
40915
42070
  for (const taskId of extraTaskIds) {
40916
- const summary = await _internals11.buildTaskSummary(directory, undefined, taskId);
42071
+ const summary = await _internals12.buildTaskSummary(directory, undefined, taskId);
40917
42072
  if (summary.phase === phase.id) {
40918
42073
  taskSummaries.push(summary);
40919
42074
  }
@@ -41014,7 +42169,7 @@ async function buildEvidenceSummary(directory, currentPhase) {
41014
42169
  let totalTasks = 0;
41015
42170
  let completedTasks = 0;
41016
42171
  for (const phase of phasesToProcess) {
41017
- const summary = await _internals11.buildPhaseSummary(directory, phase);
42172
+ const summary = await _internals12.buildPhaseSummary(directory, phase);
41018
42173
  phaseSummaries.push(summary);
41019
42174
  totalTasks += summary.totalTasks;
41020
42175
  completedTasks += summary.completedTasks;
@@ -41036,7 +42191,7 @@ async function buildEvidenceSummary(directory, currentPhase) {
41036
42191
  overallBlockers,
41037
42192
  summaryText: ""
41038
42193
  };
41039
- artifact.summaryText = _internals11.generateSummaryText(artifact);
42194
+ artifact.summaryText = _internals12.generateSummaryText(artifact);
41040
42195
  log("[EvidenceSummary] Summary built", {
41041
42196
  phases: phaseSummaries.length,
41042
42197
  totalTasks,
@@ -41055,7 +42210,7 @@ function isAutoSummaryEnabled(automationConfig) {
41055
42210
  }
41056
42211
  return automationConfig.capabilities?.evidence_auto_summaries === true;
41057
42212
  }
41058
- var VALID_EVIDENCE_TYPES2, REQUIRED_EVIDENCE_TYPES, EVIDENCE_SUMMARY_VERSION = "1.0.0", _internals11;
42213
+ var VALID_EVIDENCE_TYPES2, REQUIRED_EVIDENCE_TYPES, EVIDENCE_SUMMARY_VERSION = "1.0.0", _internals12;
41059
42214
  var init_evidence_summary_service = __esm(() => {
41060
42215
  init_manager2();
41061
42216
  init_manager();
@@ -41069,7 +42224,7 @@ var init_evidence_summary_service = __esm(() => {
41069
42224
  "retrospective"
41070
42225
  ]);
41071
42226
  REQUIRED_EVIDENCE_TYPES = ["review", "test"];
41072
- _internals11 = {
42227
+ _internals12 = {
41073
42228
  buildEvidenceSummary,
41074
42229
  isAutoSummaryEnabled,
41075
42230
  normalizeBundleEntries,
@@ -41316,12 +42471,12 @@ var init_export = __esm(() => {
41316
42471
 
41317
42472
  // src/full-auto/state.ts
41318
42473
  import * as fs11 from "fs";
41319
- import * as path23 from "path";
42474
+ import * as path26 from "path";
41320
42475
  function nowISO() {
41321
42476
  return new Date().toISOString();
41322
42477
  }
41323
42478
  function ensureSwarmDir(directory) {
41324
- const swarmDir = path23.resolve(directory, ".swarm");
42479
+ const swarmDir = path26.resolve(directory, ".swarm");
41325
42480
  if (!fs11.existsSync(swarmDir)) {
41326
42481
  fs11.mkdirSync(swarmDir, { recursive: true });
41327
42482
  }
@@ -41366,7 +42521,7 @@ function withStateLock(directory, fn) {
41366
42521
  fs11.writeFileSync(lockTarget, `${JSON.stringify(seed, null, 2)}
41367
42522
  `, "utf-8");
41368
42523
  }
41369
- release = lockfile5.lockSync(lockTarget, {
42524
+ release = lockfile6.lockSync(lockTarget, {
41370
42525
  retries: { retries: 5, minTimeout: 5, maxTimeout: 50 },
41371
42526
  stale: 5000
41372
42527
  });
@@ -41552,12 +42707,12 @@ function terminateFullAutoRun(directory, sessionID, reason) {
41552
42707
  return state;
41553
42708
  });
41554
42709
  }
41555
- var import_proper_lockfile5, lockfile5, STATE_FILE = "full-auto-state.json", stateUnreadable = false, stateUnreadableReason = "";
42710
+ var import_proper_lockfile6, lockfile6, STATE_FILE = "full-auto-state.json", stateUnreadable = false, stateUnreadableReason = "";
41556
42711
  var init_state2 = __esm(() => {
41557
42712
  init_utils2();
41558
42713
  init_logger();
41559
- import_proper_lockfile5 = __toESM(require_proper_lockfile(), 1);
41560
- lockfile5 = import_proper_lockfile5.default;
42714
+ import_proper_lockfile6 = __toESM(require_proper_lockfile(), 1);
42715
+ lockfile6 = import_proper_lockfile6.default;
41561
42716
  });
41562
42717
 
41563
42718
  // src/commands/full-auto.ts
@@ -41672,7 +42827,7 @@ function extractCurrentPhaseFromPlan2(plan) {
41672
42827
  if (!plan) {
41673
42828
  return { currentPhase: null, currentTask: null, incompleteTasks: [] };
41674
42829
  }
41675
- if (!_internals12.validatePlanPhases(plan)) {
42830
+ if (!_internals13.validatePlanPhases(plan)) {
41676
42831
  return { currentPhase: null, currentTask: null, incompleteTasks: [] };
41677
42832
  }
41678
42833
  let currentPhase = null;
@@ -41814,9 +42969,9 @@ function extractPhaseMetrics(content) {
41814
42969
  async function getHandoffData(directory) {
41815
42970
  const now = new Date().toISOString();
41816
42971
  const sessionContent = await readSwarmFileAsync(directory, "session/state.json");
41817
- const sessionState = _internals12.parseSessionState(sessionContent);
42972
+ const sessionState = _internals13.parseSessionState(sessionContent);
41818
42973
  const plan = await loadPlanJsonOnly(directory);
41819
- const planInfo = _internals12.extractCurrentPhaseFromPlan(plan);
42974
+ const planInfo = _internals13.extractCurrentPhaseFromPlan(plan);
41820
42975
  if (!plan) {
41821
42976
  const planMdContent = await readSwarmFileAsync(directory, "plan.md");
41822
42977
  if (planMdContent) {
@@ -41835,8 +42990,8 @@ async function getHandoffData(directory) {
41835
42990
  }
41836
42991
  }
41837
42992
  const contextContent = await readSwarmFileAsync(directory, "context.md");
41838
- const recentDecisions = _internals12.extractDecisions(contextContent);
41839
- const rawPhaseMetrics = _internals12.extractPhaseMetrics(contextContent);
42993
+ const recentDecisions = _internals13.extractDecisions(contextContent);
42994
+ const rawPhaseMetrics = _internals13.extractPhaseMetrics(contextContent);
41840
42995
  const phaseMetrics = sanitizeString(rawPhaseMetrics, 1000);
41841
42996
  let delegationState = null;
41842
42997
  if (sessionState?.delegationState) {
@@ -42000,13 +43155,13 @@ ${lines.join(`
42000
43155
  `)}
42001
43156
  \`\`\``;
42002
43157
  }
42003
- var RTL_OVERRIDE_PATTERN, MAX_TASK_ID_LENGTH = 100, MAX_DECISION_LENGTH = 500, MAX_INCOMPLETE_TASKS = 20, _internals12;
43158
+ var RTL_OVERRIDE_PATTERN, MAX_TASK_ID_LENGTH = 100, MAX_DECISION_LENGTH = 500, MAX_INCOMPLETE_TASKS = 20, _internals13;
42004
43159
  var init_handoff_service = __esm(() => {
42005
43160
  init_utils2();
42006
43161
  init_manager();
42007
43162
  init_utils();
42008
43163
  RTL_OVERRIDE_PATTERN = /[\u202e\u202d\u202c\u200f]/g;
42009
- _internals12 = {
43164
+ _internals13 = {
42010
43165
  getHandoffData,
42011
43166
  formatHandoffMarkdown,
42012
43167
  formatContinuationPrompt,
@@ -42022,7 +43177,7 @@ var init_handoff_service = __esm(() => {
42022
43177
 
42023
43178
  // src/session/snapshot-writer.ts
42024
43179
  import { mkdirSync as mkdirSync10, renameSync as renameSync6 } from "fs";
42025
- import * as path24 from "path";
43180
+ import * as path27 from "path";
42026
43181
  function serializeAgentSession(s) {
42027
43182
  const gateLog = {};
42028
43183
  const rawGateLog = s.gateLog ?? new Map;
@@ -42112,7 +43267,7 @@ async function writeSnapshot(directory, state) {
42112
43267
  }
42113
43268
  const content = JSON.stringify(snapshot, null, 2);
42114
43269
  const resolvedPath = validateSwarmPath(directory, "session/state.json");
42115
- const dir = path24.dirname(resolvedPath);
43270
+ const dir = path27.dirname(resolvedPath);
42116
43271
  mkdirSync10(dir, { recursive: true });
42117
43272
  const tempPath = `${resolvedPath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
42118
43273
  await bunWrite(tempPath, content);
@@ -42125,22 +43280,22 @@ async function writeSnapshot(directory, state) {
42125
43280
  }
42126
43281
  function createSnapshotWriterHook(directory) {
42127
43282
  return (_input, _output) => {
42128
- _writeInFlight = _writeInFlight.then(() => _internals13.writeSnapshot(directory, swarmState), () => _internals13.writeSnapshot(directory, swarmState));
43283
+ _writeInFlight = _writeInFlight.then(() => _internals14.writeSnapshot(directory, swarmState), () => _internals14.writeSnapshot(directory, swarmState));
42129
43284
  return _writeInFlight;
42130
43285
  };
42131
43286
  }
42132
43287
  async function flushPendingSnapshot(directory) {
42133
- _writeInFlight = _writeInFlight.then(() => _internals13.writeSnapshot(directory, swarmState), () => _internals13.writeSnapshot(directory, swarmState));
43288
+ _writeInFlight = _writeInFlight.then(() => _internals14.writeSnapshot(directory, swarmState), () => _internals14.writeSnapshot(directory, swarmState));
42134
43289
  await _writeInFlight;
42135
43290
  }
42136
- var _writeInFlight, _internals13;
43291
+ var _writeInFlight, _internals14;
42137
43292
  var init_snapshot_writer = __esm(() => {
42138
43293
  init_utils2();
42139
43294
  init_state();
42140
43295
  init_utils();
42141
43296
  init_bun_compat();
42142
43297
  _writeInFlight = Promise.resolve();
42143
- _internals13 = {
43298
+ _internals14 = {
42144
43299
  writeSnapshot,
42145
43300
  createSnapshotWriterHook,
42146
43301
  flushPendingSnapshot
@@ -42561,9 +43716,9 @@ var init_issue = __esm(() => {
42561
43716
 
42562
43717
  // src/hooks/knowledge-migrator.ts
42563
43718
  import { randomUUID as randomUUID2 } from "crypto";
42564
- import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
42565
- import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
42566
- import * as path25 from "path";
43719
+ import { existsSync as existsSync18, readFileSync as readFileSync12 } from "fs";
43720
+ import { mkdir as mkdir8, readFile as readFile8, writeFile as writeFile9 } from "fs/promises";
43721
+ import * as path28 from "path";
42567
43722
  async function migrateKnowledgeToExternal(_directory, _config) {
42568
43723
  return {
42569
43724
  migrated: false,
@@ -42574,10 +43729,10 @@ async function migrateKnowledgeToExternal(_directory, _config) {
42574
43729
  };
42575
43730
  }
42576
43731
  async function migrateContextToKnowledge(directory, config3) {
42577
- const sentinelPath = path25.join(directory, ".swarm", ".knowledge-migrated");
42578
- const contextPath = path25.join(directory, ".swarm", "context.md");
43732
+ const sentinelPath = path28.join(directory, ".swarm", ".knowledge-migrated");
43733
+ const contextPath = path28.join(directory, ".swarm", "context.md");
42579
43734
  const knowledgePath = resolveSwarmKnowledgePath(directory);
42580
- if (existsSync15(sentinelPath)) {
43735
+ if (existsSync18(sentinelPath)) {
42581
43736
  return {
42582
43737
  migrated: false,
42583
43738
  entriesMigrated: 0,
@@ -42586,7 +43741,7 @@ async function migrateContextToKnowledge(directory, config3) {
42586
43741
  skippedReason: "sentinel-exists"
42587
43742
  };
42588
43743
  }
42589
- if (!existsSync15(contextPath)) {
43744
+ if (!existsSync18(contextPath)) {
42590
43745
  return {
42591
43746
  migrated: false,
42592
43747
  entriesMigrated: 0,
@@ -42595,7 +43750,7 @@ async function migrateContextToKnowledge(directory, config3) {
42595
43750
  skippedReason: "no-context-file"
42596
43751
  };
42597
43752
  }
42598
- const contextContent = await readFile6(contextPath, "utf-8");
43753
+ const contextContent = await readFile8(contextPath, "utf-8");
42599
43754
  if (contextContent.trim().length === 0) {
42600
43755
  return {
42601
43756
  migrated: false,
@@ -42605,9 +43760,9 @@ async function migrateContextToKnowledge(directory, config3) {
42605
43760
  skippedReason: "empty-context"
42606
43761
  };
42607
43762
  }
42608
- const rawEntries = _internals14.parseContextMd(contextContent);
43763
+ const rawEntries = _internals15.parseContextMd(contextContent);
42609
43764
  if (rawEntries.length === 0) {
42610
- await _internals14.writeSentinel(sentinelPath, 0, 0);
43765
+ await _internals15.writeSentinel(sentinelPath, 0, 0);
42611
43766
  return {
42612
43767
  migrated: true,
42613
43768
  entriesMigrated: 0,
@@ -42618,10 +43773,10 @@ async function migrateContextToKnowledge(directory, config3) {
42618
43773
  const existing = await readKnowledge(knowledgePath);
42619
43774
  let migrated = 0;
42620
43775
  let dropped = 0;
42621
- const projectName = _internals14.inferProjectName(directory);
43776
+ const projectName = _internals15.inferProjectName(directory);
42622
43777
  for (const raw of rawEntries) {
42623
43778
  if (config3.validation_enabled !== false) {
42624
- const category = raw.categoryHint ?? _internals14.inferCategoryFromText(raw.text);
43779
+ const category = raw.categoryHint ?? _internals15.inferCategoryFromText(raw.text);
42625
43780
  const result = validateLesson(raw.text, existing.map((e) => e.lesson), {
42626
43781
  category,
42627
43782
  scope: "global",
@@ -42641,8 +43796,8 @@ async function migrateContextToKnowledge(directory, config3) {
42641
43796
  const entry = {
42642
43797
  id: randomUUID2(),
42643
43798
  tier: "swarm",
42644
- lesson: _internals14.truncateLesson(raw.text),
42645
- category: raw.categoryHint ?? _internals14.inferCategoryFromText(raw.text),
43799
+ lesson: _internals15.truncateLesson(raw.text),
43800
+ category: raw.categoryHint ?? _internals15.inferCategoryFromText(raw.text),
42646
43801
  tags: [...inferredTags, `migration:${raw.sourceSection}`],
42647
43802
  scope: "global",
42648
43803
  confidence: 0.3,
@@ -42665,7 +43820,7 @@ async function migrateContextToKnowledge(directory, config3) {
42665
43820
  if (migrated > 0) {
42666
43821
  await rewriteKnowledge(knowledgePath, existing);
42667
43822
  }
42668
- await _internals14.writeSentinel(sentinelPath, migrated, dropped);
43823
+ await _internals15.writeSentinel(sentinelPath, migrated, dropped);
42669
43824
  log(`[knowledge-migrator] Migrated ${migrated} entries, dropped ${dropped}`);
42670
43825
  return {
42671
43826
  migrated: true,
@@ -42675,7 +43830,7 @@ async function migrateContextToKnowledge(directory, config3) {
42675
43830
  };
42676
43831
  }
42677
43832
  function parseContextMd(content) {
42678
- const sections = _internals14.splitIntoSections(content);
43833
+ const sections = _internals15.splitIntoSections(content);
42679
43834
  const entries = [];
42680
43835
  const seen = new Set;
42681
43836
  const sectionPatterns = [
@@ -42691,7 +43846,7 @@ function parseContextMd(content) {
42691
43846
  const match = sectionPatterns.find((sp) => sp.pattern.test(section.heading));
42692
43847
  if (!match)
42693
43848
  continue;
42694
- const bullets = _internals14.extractBullets(section.body);
43849
+ const bullets = _internals15.extractBullets(section.body);
42695
43850
  for (const bullet of bullets) {
42696
43851
  if (bullet.length < 15)
42697
43852
  continue;
@@ -42700,9 +43855,9 @@ function parseContextMd(content) {
42700
43855
  continue;
42701
43856
  seen.add(normalized);
42702
43857
  entries.push({
42703
- text: _internals14.truncateLesson(bullet),
43858
+ text: _internals15.truncateLesson(bullet),
42704
43859
  sourceSection: match.sourceSection,
42705
- categoryHint: _internals14.inferCategoryFromText(bullet)
43860
+ categoryHint: _internals15.inferCategoryFromText(bullet)
42706
43861
  });
42707
43862
  }
42708
43863
  }
@@ -42771,16 +43926,16 @@ function truncateLesson(text) {
42771
43926
  return `${text.slice(0, 277)}...`;
42772
43927
  }
42773
43928
  function inferProjectName(directory) {
42774
- const packageJsonPath = path25.join(directory, "package.json");
42775
- if (existsSync15(packageJsonPath)) {
43929
+ const packageJsonPath = path28.join(directory, "package.json");
43930
+ if (existsSync18(packageJsonPath)) {
42776
43931
  try {
42777
- const pkg = JSON.parse(readFileSync11(packageJsonPath, "utf-8"));
43932
+ const pkg = JSON.parse(readFileSync12(packageJsonPath, "utf-8"));
42778
43933
  if (pkg.name && typeof pkg.name === "string") {
42779
43934
  return pkg.name;
42780
43935
  }
42781
43936
  } catch {}
42782
43937
  }
42783
- return path25.basename(directory);
43938
+ return path28.basename(directory);
42784
43939
  }
42785
43940
  async function writeSentinel(sentinelPath, migrated, dropped) {
42786
43941
  const sentinel = {
@@ -42792,15 +43947,15 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
42792
43947
  schema_version: 1,
42793
43948
  migration_tool: "knowledge-migrator.ts"
42794
43949
  };
42795
- await mkdir5(path25.dirname(sentinelPath), { recursive: true });
42796
- await writeFile6(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
43950
+ await mkdir8(path28.dirname(sentinelPath), { recursive: true });
43951
+ await writeFile9(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
42797
43952
  }
42798
- var _internals14;
43953
+ var _internals15;
42799
43954
  var init_knowledge_migrator = __esm(() => {
42800
43955
  init_logger();
42801
43956
  init_knowledge_store();
42802
43957
  init_knowledge_validator();
42803
- _internals14 = {
43958
+ _internals15 = {
42804
43959
  migrateContextToKnowledge,
42805
43960
  migrateKnowledgeToExternal,
42806
43961
  parseContextMd,
@@ -42814,7 +43969,7 @@ var init_knowledge_migrator = __esm(() => {
42814
43969
  });
42815
43970
 
42816
43971
  // src/commands/knowledge.ts
42817
- import { join as join23 } from "path";
43972
+ import { join as join26 } from "path";
42818
43973
  function resolveEntryByPrefix(entries, inputId) {
42819
43974
  const exact = entries.find((e) => e.id === inputId);
42820
43975
  if (exact)
@@ -42865,7 +44020,7 @@ async function handleKnowledgeRestoreCommand(directory, args) {
42865
44020
  return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
42866
44021
  }
42867
44022
  try {
42868
- const quarantinePath = join23(directory, ".swarm", "knowledge-quarantined.jsonl");
44023
+ const quarantinePath = join26(directory, ".swarm", "knowledge-quarantined.jsonl");
42869
44024
  const entries = await readKnowledge(quarantinePath);
42870
44025
  const resolved = resolveEntryByPrefix(entries, inputId);
42871
44026
  if ("error" in resolved) {
@@ -43314,7 +44469,7 @@ var init_path_security = () => {};
43314
44469
 
43315
44470
  // src/tools/lint.ts
43316
44471
  import * as fs12 from "fs";
43317
- import * as path26 from "path";
44472
+ import * as path29 from "path";
43318
44473
  function validateArgs(args) {
43319
44474
  if (typeof args !== "object" || args === null)
43320
44475
  return false;
@@ -43325,9 +44480,9 @@ function validateArgs(args) {
43325
44480
  }
43326
44481
  function getLinterCommand(linter, mode, projectDir) {
43327
44482
  const isWindows = process.platform === "win32";
43328
- const binDir = path26.join(projectDir, "node_modules", ".bin");
43329
- const biomeBin = isWindows ? path26.join(binDir, "biome.EXE") : path26.join(binDir, "biome");
43330
- const eslintBin = isWindows ? path26.join(binDir, "eslint.cmd") : path26.join(binDir, "eslint");
44483
+ const binDir = path29.join(projectDir, "node_modules", ".bin");
44484
+ const biomeBin = isWindows ? path29.join(binDir, "biome.EXE") : path29.join(binDir, "biome");
44485
+ const eslintBin = isWindows ? path29.join(binDir, "eslint.cmd") : path29.join(binDir, "eslint");
43331
44486
  switch (linter) {
43332
44487
  case "biome":
43333
44488
  if (mode === "fix") {
@@ -43343,7 +44498,7 @@ function getLinterCommand(linter, mode, projectDir) {
43343
44498
  }
43344
44499
  function getAdditionalLinterCommand(linter, mode, cwd) {
43345
44500
  const gradlewName = process.platform === "win32" ? "gradlew.bat" : "gradlew";
43346
- const gradlew = fs12.existsSync(path26.join(cwd, gradlewName)) ? path26.join(cwd, gradlewName) : null;
44501
+ const gradlew = fs12.existsSync(path29.join(cwd, gradlewName)) ? path29.join(cwd, gradlewName) : null;
43347
44502
  switch (linter) {
43348
44503
  case "ruff":
43349
44504
  return mode === "fix" ? ["ruff", "check", "--fix", "."] : ["ruff", "check", "."];
@@ -43377,10 +44532,10 @@ function getAdditionalLinterCommand(linter, mode, cwd) {
43377
44532
  }
43378
44533
  }
43379
44534
  function detectRuff(cwd) {
43380
- if (fs12.existsSync(path26.join(cwd, "ruff.toml")))
44535
+ if (fs12.existsSync(path29.join(cwd, "ruff.toml")))
43381
44536
  return isCommandAvailable("ruff");
43382
44537
  try {
43383
- const pyproject = path26.join(cwd, "pyproject.toml");
44538
+ const pyproject = path29.join(cwd, "pyproject.toml");
43384
44539
  if (fs12.existsSync(pyproject)) {
43385
44540
  const content = fs12.readFileSync(pyproject, "utf-8");
43386
44541
  if (content.includes("[tool.ruff]"))
@@ -43390,19 +44545,19 @@ function detectRuff(cwd) {
43390
44545
  return false;
43391
44546
  }
43392
44547
  function detectClippy(cwd) {
43393
- return fs12.existsSync(path26.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
44548
+ return fs12.existsSync(path29.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
43394
44549
  }
43395
44550
  function detectGolangciLint(cwd) {
43396
- return fs12.existsSync(path26.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
44551
+ return fs12.existsSync(path29.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
43397
44552
  }
43398
44553
  function detectCheckstyle(cwd) {
43399
- const hasMaven = fs12.existsSync(path26.join(cwd, "pom.xml"));
43400
- const hasGradle = fs12.existsSync(path26.join(cwd, "build.gradle")) || fs12.existsSync(path26.join(cwd, "build.gradle.kts"));
43401
- const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs12.existsSync(path26.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
44554
+ const hasMaven = fs12.existsSync(path29.join(cwd, "pom.xml"));
44555
+ const hasGradle = fs12.existsSync(path29.join(cwd, "build.gradle")) || fs12.existsSync(path29.join(cwd, "build.gradle.kts"));
44556
+ const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs12.existsSync(path29.join(cwd, "gradlew")) || isCommandAvailable("gradle"));
43402
44557
  return (hasMaven || hasGradle) && hasBinary;
43403
44558
  }
43404
44559
  function detectKtlint(cwd) {
43405
- const hasKotlin = fs12.existsSync(path26.join(cwd, "build.gradle.kts")) || fs12.existsSync(path26.join(cwd, "build.gradle")) || (() => {
44560
+ const hasKotlin = fs12.existsSync(path29.join(cwd, "build.gradle.kts")) || fs12.existsSync(path29.join(cwd, "build.gradle")) || (() => {
43406
44561
  try {
43407
44562
  return fs12.readdirSync(cwd).some((f) => f.endsWith(".kt") || f.endsWith(".kts"));
43408
44563
  } catch {
@@ -43421,11 +44576,11 @@ function detectDotnetFormat(cwd) {
43421
44576
  }
43422
44577
  }
43423
44578
  function detectCppcheck(cwd) {
43424
- if (fs12.existsSync(path26.join(cwd, "CMakeLists.txt"))) {
44579
+ if (fs12.existsSync(path29.join(cwd, "CMakeLists.txt"))) {
43425
44580
  return isCommandAvailable("cppcheck");
43426
44581
  }
43427
44582
  try {
43428
- const dirsToCheck = [cwd, path26.join(cwd, "src")];
44583
+ const dirsToCheck = [cwd, path29.join(cwd, "src")];
43429
44584
  const hasCpp = dirsToCheck.some((dir) => {
43430
44585
  try {
43431
44586
  return fs12.readdirSync(dir).some((f) => /\.(c|cpp|cc|cxx|h|hpp)$/.test(f));
@@ -43439,13 +44594,13 @@ function detectCppcheck(cwd) {
43439
44594
  }
43440
44595
  }
43441
44596
  function detectSwiftlint(cwd) {
43442
- return fs12.existsSync(path26.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
44597
+ return fs12.existsSync(path29.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
43443
44598
  }
43444
44599
  function detectDartAnalyze(cwd) {
43445
- return fs12.existsSync(path26.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
44600
+ return fs12.existsSync(path29.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
43446
44601
  }
43447
44602
  function detectRubocop(cwd) {
43448
- return (fs12.existsSync(path26.join(cwd, "Gemfile")) || fs12.existsSync(path26.join(cwd, "gems.rb")) || fs12.existsSync(path26.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
44603
+ return (fs12.existsSync(path29.join(cwd, "Gemfile")) || fs12.existsSync(path29.join(cwd, "gems.rb")) || fs12.existsSync(path29.join(cwd, ".rubocop.yml"))) && (isCommandAvailable("rubocop") || isCommandAvailable("bundle"));
43449
44604
  }
43450
44605
  function detectAdditionalLinter(cwd) {
43451
44606
  if (detectRuff(cwd))
@@ -43473,10 +44628,10 @@ function detectAdditionalLinter(cwd) {
43473
44628
  function findBinInAncestors(startDir, binName) {
43474
44629
  let dir = startDir;
43475
44630
  while (true) {
43476
- const candidate = path26.join(dir, "node_modules", ".bin", binName);
44631
+ const candidate = path29.join(dir, "node_modules", ".bin", binName);
43477
44632
  if (fs12.existsSync(candidate))
43478
44633
  return candidate;
43479
- const parent = path26.dirname(dir);
44634
+ const parent = path29.dirname(dir);
43480
44635
  if (parent === dir)
43481
44636
  break;
43482
44637
  dir = parent;
@@ -43485,10 +44640,10 @@ function findBinInAncestors(startDir, binName) {
43485
44640
  }
43486
44641
  function findBinInEnvPath(binName) {
43487
44642
  const searchPath = process.env.PATH ?? "";
43488
- for (const dir of searchPath.split(path26.delimiter)) {
44643
+ for (const dir of searchPath.split(path29.delimiter)) {
43489
44644
  if (!dir)
43490
44645
  continue;
43491
- const candidate = path26.join(dir, binName);
44646
+ const candidate = path29.join(dir, binName);
43492
44647
  if (fs12.existsSync(candidate))
43493
44648
  return candidate;
43494
44649
  }
@@ -43501,13 +44656,13 @@ async function detectAvailableLinter(directory) {
43501
44656
  return null;
43502
44657
  const projectDir = directory;
43503
44658
  const isWindows = process.platform === "win32";
43504
- const biomeBin = isWindows ? path26.join(projectDir, "node_modules", ".bin", "biome.EXE") : path26.join(projectDir, "node_modules", ".bin", "biome");
43505
- const eslintBin = isWindows ? path26.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path26.join(projectDir, "node_modules", ".bin", "eslint");
44659
+ const biomeBin = isWindows ? path29.join(projectDir, "node_modules", ".bin", "biome.EXE") : path29.join(projectDir, "node_modules", ".bin", "biome");
44660
+ const eslintBin = isWindows ? path29.join(projectDir, "node_modules", ".bin", "eslint.cmd") : path29.join(projectDir, "node_modules", ".bin", "eslint");
43506
44661
  const localResult = await _detectAvailableLinter(projectDir, biomeBin, eslintBin);
43507
44662
  if (localResult)
43508
44663
  return localResult;
43509
- const biomeAncestor = findBinInAncestors(path26.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
43510
- const eslintAncestor = findBinInAncestors(path26.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
44664
+ const biomeAncestor = findBinInAncestors(path29.dirname(projectDir), isWindows ? "biome.EXE" : "biome");
44665
+ const eslintAncestor = findBinInAncestors(path29.dirname(projectDir), isWindows ? "eslint.cmd" : "eslint");
43511
44666
  if (biomeAncestor || eslintAncestor) {
43512
44667
  return _detectAvailableLinter(projectDir, biomeAncestor ?? biomeBin, eslintAncestor ?? eslintBin);
43513
44668
  }
@@ -43666,7 +44821,7 @@ async function runAdditionalLint(linter, mode, cwd) {
43666
44821
  };
43667
44822
  }
43668
44823
  }
43669
- var MAX_OUTPUT_BYTES = 512000, MAX_COMMAND_LENGTH = 500, lint, _internals15;
44824
+ var MAX_OUTPUT_BYTES = 512000, MAX_COMMAND_LENGTH = 500, lint, _internals16;
43670
44825
  var init_lint = __esm(() => {
43671
44826
  init_zod();
43672
44827
  init_discovery();
@@ -43698,15 +44853,15 @@ var init_lint = __esm(() => {
43698
44853
  }
43699
44854
  const { mode } = args;
43700
44855
  const cwd = directory;
43701
- const linter = await _internals15.detectAvailableLinter(directory);
44856
+ const linter = await _internals16.detectAvailableLinter(directory);
43702
44857
  if (linter) {
43703
- const result = await _internals15.runLint(linter, mode, directory);
44858
+ const result = await _internals16.runLint(linter, mode, directory);
43704
44859
  return JSON.stringify(result, null, 2);
43705
44860
  }
43706
- const additionalLinter = _internals15.detectAdditionalLinter(cwd);
44861
+ const additionalLinter = _internals16.detectAdditionalLinter(cwd);
43707
44862
  if (additionalLinter) {
43708
44863
  warn(`[lint] Using ${additionalLinter} linter for this project`);
43709
- const result = await _internals15.runAdditionalLint(additionalLinter, mode, cwd);
44864
+ const result = await _internals16.runAdditionalLint(additionalLinter, mode, cwd);
43710
44865
  return JSON.stringify(result, null, 2);
43711
44866
  }
43712
44867
  const errorResult = {
@@ -43720,7 +44875,7 @@ For Rust: rustup component add clippy`
43720
44875
  return JSON.stringify(errorResult, null, 2);
43721
44876
  }
43722
44877
  });
43723
- _internals15 = {
44878
+ _internals16 = {
43724
44879
  detectAvailableLinter,
43725
44880
  runLint,
43726
44881
  detectAdditionalLinter,
@@ -43730,7 +44885,7 @@ For Rust: rustup component add clippy`
43730
44885
 
43731
44886
  // src/tools/secretscan.ts
43732
44887
  import * as fs13 from "fs";
43733
- import * as path27 from "path";
44888
+ import * as path30 from "path";
43734
44889
  function calculateShannonEntropy(str) {
43735
44890
  if (str.length === 0)
43736
44891
  return 0;
@@ -43778,7 +44933,7 @@ function isGlobOrPathPattern(pattern) {
43778
44933
  return pattern.includes("/") || pattern.includes("\\") || /[*?[\]{}]/.test(pattern);
43779
44934
  }
43780
44935
  function loadSecretScanIgnore(scanDir) {
43781
- const ignorePath = path27.join(scanDir, ".secretscanignore");
44936
+ const ignorePath = path30.join(scanDir, ".secretscanignore");
43782
44937
  try {
43783
44938
  if (!fs13.existsSync(ignorePath))
43784
44939
  return [];
@@ -43801,7 +44956,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
43801
44956
  if (exactNames.has(entry))
43802
44957
  return true;
43803
44958
  for (const pattern of globPatterns) {
43804
- if (path27.matchesGlob(relPath, pattern))
44959
+ if (path30.matchesGlob(relPath, pattern))
43805
44960
  return true;
43806
44961
  }
43807
44962
  return false;
@@ -43822,7 +44977,7 @@ function validateDirectoryInput(dir) {
43822
44977
  return null;
43823
44978
  }
43824
44979
  function isBinaryFile(filePath, buffer) {
43825
- const ext = path27.extname(filePath).toLowerCase();
44980
+ const ext = path30.extname(filePath).toLowerCase();
43826
44981
  if (DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
43827
44982
  return true;
43828
44983
  }
@@ -43958,9 +45113,9 @@ function isSymlinkLoop(realPath, visited) {
43958
45113
  return false;
43959
45114
  }
43960
45115
  function isPathWithinScope(realPath, scanDir) {
43961
- const resolvedScanDir = path27.resolve(scanDir);
43962
- const resolvedRealPath = path27.resolve(realPath);
43963
- return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path27.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
45116
+ const resolvedScanDir = path30.resolve(scanDir);
45117
+ const resolvedRealPath = path30.resolve(realPath);
45118
+ return resolvedRealPath === resolvedScanDir || resolvedRealPath.startsWith(resolvedScanDir + path30.sep) || resolvedRealPath.startsWith(`${resolvedScanDir}/`) || resolvedRealPath.startsWith(`${resolvedScanDir}\\`);
43964
45119
  }
43965
45120
  function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, stats = {
43966
45121
  skippedDirs: 0,
@@ -43986,8 +45141,8 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
43986
45141
  return a.localeCompare(b);
43987
45142
  });
43988
45143
  for (const entry of entries) {
43989
- const fullPath = path27.join(dir, entry);
43990
- const relPath = path27.relative(scanDir, fullPath).replace(/\\/g, "/");
45144
+ const fullPath = path30.join(dir, entry);
45145
+ const relPath = path30.relative(scanDir, fullPath).replace(/\\/g, "/");
43991
45146
  if (isExcluded(entry, relPath, excludeExact, excludeGlobs)) {
43992
45147
  stats.skippedDirs++;
43993
45148
  continue;
@@ -44022,7 +45177,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
44022
45177
  const subFiles = findScannableFiles(fullPath, excludeExact, excludeGlobs, scanDir, visited, stats);
44023
45178
  files.push(...subFiles);
44024
45179
  } else if (lstat.isFile()) {
44025
- const ext = path27.extname(fullPath).toLowerCase();
45180
+ const ext = path30.extname(fullPath).toLowerCase();
44026
45181
  if (!DEFAULT_EXCLUDE_EXTENSIONS.has(ext)) {
44027
45182
  files.push(fullPath);
44028
45183
  } else {
@@ -44034,7 +45189,7 @@ function findScannableFiles(dir, excludeExact, excludeGlobs, scanDir, visited, s
44034
45189
  }
44035
45190
  async function runSecretscan(directory) {
44036
45191
  try {
44037
- const result = await _internals16.secretscan.execute({ directory }, {});
45192
+ const result = await _internals17.secretscan.execute({ directory }, {});
44038
45193
  const jsonStr = typeof result === "string" ? result : result.output;
44039
45194
  return JSON.parse(jsonStr);
44040
45195
  } catch (e) {
@@ -44049,7 +45204,7 @@ async function runSecretscan(directory) {
44049
45204
  return errorResult;
44050
45205
  }
44051
45206
  }
44052
- var MAX_FILE_PATH_LENGTH = 500, MAX_FILE_SIZE_BYTES, MAX_FILES_SCANNED = 1000, MAX_FINDINGS = 100, MAX_OUTPUT_BYTES2 = 512000, MAX_LINE_LENGTH = 1e4, MAX_CONTENT_BYTES, BINARY_SIGNATURES, BINARY_PREFIX_BYTES = 4, BINARY_NULL_CHECK_BYTES = 8192, BINARY_NULL_THRESHOLD = 0.1, DEFAULT_EXCLUDE_DIRS, DEFAULT_EXCLUDE_EXTENSIONS, SECRET_PATTERNS, O_NOFOLLOW, secretscan, _internals16;
45207
+ var MAX_FILE_PATH_LENGTH = 500, MAX_FILE_SIZE_BYTES, MAX_FILES_SCANNED = 1000, MAX_FINDINGS = 100, MAX_OUTPUT_BYTES2 = 512000, MAX_LINE_LENGTH = 1e4, MAX_CONTENT_BYTES, BINARY_SIGNATURES, BINARY_PREFIX_BYTES = 4, BINARY_NULL_CHECK_BYTES = 8192, BINARY_NULL_THRESHOLD = 0.1, DEFAULT_EXCLUDE_DIRS, DEFAULT_EXCLUDE_EXTENSIONS, SECRET_PATTERNS, O_NOFOLLOW, secretscan, _internals17;
44053
45208
  var init_secretscan = __esm(() => {
44054
45209
  init_zod();
44055
45210
  init_path_security();
@@ -44282,7 +45437,7 @@ var init_secretscan = __esm(() => {
44282
45437
  }
44283
45438
  }
44284
45439
  try {
44285
- const _scanDirRaw = path27.resolve(directory);
45440
+ const _scanDirRaw = path30.resolve(directory);
44286
45441
  const scanDir = (() => {
44287
45442
  try {
44288
45443
  return fs13.realpathSync(_scanDirRaw);
@@ -44421,7 +45576,7 @@ var init_secretscan = __esm(() => {
44421
45576
  }
44422
45577
  }
44423
45578
  });
44424
- _internals16 = {
45579
+ _internals17 = {
44425
45580
  secretscan,
44426
45581
  runSecretscan
44427
45582
  };
@@ -44429,7 +45584,7 @@ var init_secretscan = __esm(() => {
44429
45584
 
44430
45585
  // src/test-impact/analyzer.ts
44431
45586
  import fs14 from "fs";
44432
- import path28 from "path";
45587
+ import path31 from "path";
44433
45588
  function normalizePath(p) {
44434
45589
  return p.replace(/\\/g, "/");
44435
45590
  }
@@ -44450,8 +45605,8 @@ function resolveRelativeImport(fromDir, importPath) {
44450
45605
  if (!importPath.startsWith(".")) {
44451
45606
  return null;
44452
45607
  }
44453
- const resolved = path28.resolve(fromDir, importPath);
44454
- if (path28.extname(resolved)) {
45608
+ const resolved = path31.resolve(fromDir, importPath);
45609
+ if (path31.extname(resolved)) {
44455
45610
  if (fs14.existsSync(resolved) && fs14.statSync(resolved).isFile()) {
44456
45611
  return normalizePath(resolved);
44457
45612
  }
@@ -44496,12 +45651,12 @@ function findTestFilesSync(cwd) {
44496
45651
  for (const entry of entries) {
44497
45652
  if (entry.isDirectory()) {
44498
45653
  if (!skipDirs.has(entry.name)) {
44499
- walk(path28.join(dir, entry.name), visitedInodes);
45654
+ walk(path31.join(dir, entry.name), visitedInodes);
44500
45655
  }
44501
45656
  } else if (entry.isFile()) {
44502
45657
  const name = entry.name;
44503
45658
  if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(name) || dir.includes("__tests__") && /\.(ts|tsx|js|jsx)$/.test(name)) {
44504
- testFiles.push(normalizePath(path28.join(dir, entry.name)));
45659
+ testFiles.push(normalizePath(path31.join(dir, entry.name)));
44505
45660
  }
44506
45661
  }
44507
45662
  }
@@ -44539,7 +45694,7 @@ async function buildImpactMapInternal(cwd) {
44539
45694
  continue;
44540
45695
  }
44541
45696
  const imports = extractImports(content);
44542
- const testDir = path28.dirname(testFile);
45697
+ const testDir = path31.dirname(testFile);
44543
45698
  for (const importPath of imports) {
44544
45699
  const resolvedSource = resolveRelativeImport(testDir, importPath);
44545
45700
  if (resolvedSource === null) {
@@ -44556,28 +45711,28 @@ async function buildImpactMapInternal(cwd) {
44556
45711
  return impactMap;
44557
45712
  }
44558
45713
  async function buildImpactMap(cwd) {
44559
- const impactMap = await _internals17.buildImpactMapInternal(cwd);
44560
- await _internals17.saveImpactMap(cwd, impactMap);
45714
+ const impactMap = await _internals18.buildImpactMapInternal(cwd);
45715
+ await _internals18.saveImpactMap(cwd, impactMap);
44561
45716
  return impactMap;
44562
45717
  }
44563
45718
  async function loadImpactMap(cwd) {
44564
- const cachePath = path28.join(cwd, ".swarm", "cache", "impact-map.json");
45719
+ const cachePath = path31.join(cwd, ".swarm", "cache", "impact-map.json");
44565
45720
  if (fs14.existsSync(cachePath)) {
44566
45721
  try {
44567
45722
  const content = fs14.readFileSync(cachePath, "utf-8");
44568
45723
  const data = JSON.parse(content);
44569
45724
  const map3 = data.map;
44570
45725
  const generatedAt = new Date(data.generatedAt).getTime();
44571
- if (!_internals17.isCacheStale(map3, generatedAt)) {
45726
+ if (!_internals18.isCacheStale(map3, generatedAt)) {
44572
45727
  return map3;
44573
45728
  }
44574
45729
  } catch {}
44575
45730
  }
44576
- return _internals17.buildImpactMap(cwd);
45731
+ return _internals18.buildImpactMap(cwd);
44577
45732
  }
44578
45733
  async function saveImpactMap(cwd, impactMap) {
44579
- const cacheDir2 = path28.join(cwd, ".swarm", "cache");
44580
- const cachePath = path28.join(cacheDir2, "impact-map.json");
45734
+ const cacheDir2 = path31.join(cwd, ".swarm", "cache");
45735
+ const cachePath = path31.join(cacheDir2, "impact-map.json");
44581
45736
  if (!fs14.existsSync(cacheDir2)) {
44582
45737
  fs14.mkdirSync(cacheDir2, { recursive: true });
44583
45738
  }
@@ -44599,11 +45754,11 @@ async function analyzeImpact(changedFiles, cwd) {
44599
45754
  };
44600
45755
  }
44601
45756
  const validFiles = changedFiles.filter((f) => typeof f === "string" && f.length > 0 && !f.includes("\x00"));
44602
- const impactMap = await _internals17.loadImpactMap(cwd);
45757
+ const impactMap = await _internals18.loadImpactMap(cwd);
44603
45758
  const impactedTestsSet = new Set;
44604
45759
  const untestedFiles = [];
44605
45760
  for (const changedFile of validFiles) {
44606
- const normalizedChanged = normalizePath(path28.resolve(changedFile));
45761
+ const normalizedChanged = normalizePath(path31.resolve(changedFile));
44607
45762
  const tests = impactMap[normalizedChanged];
44608
45763
  if (tests && tests.length > 0) {
44609
45764
  for (const test of tests) {
@@ -44640,13 +45795,13 @@ async function analyzeImpact(changedFiles, cwd) {
44640
45795
  impactMap
44641
45796
  };
44642
45797
  }
44643
- var IMPORT_REGEX_ES, IMPORT_REGEX_REQUIRE, IMPORT_REGEX_REEXPORT, EXTENSIONS_TO_TRY, _internals17;
45798
+ var IMPORT_REGEX_ES, IMPORT_REGEX_REQUIRE, IMPORT_REGEX_REEXPORT, EXTENSIONS_TO_TRY, _internals18;
44644
45799
  var init_analyzer = __esm(() => {
44645
45800
  IMPORT_REGEX_ES = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
44646
45801
  IMPORT_REGEX_REQUIRE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
44647
45802
  IMPORT_REGEX_REEXPORT = /export\s+(?:\{[^}]*\}|\*)\s+from\s+['"]([^'"]+)['"]/g;
44648
45803
  EXTENSIONS_TO_TRY = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
44649
- _internals17 = {
45804
+ _internals18 = {
44650
45805
  normalizePath,
44651
45806
  isCacheStale,
44652
45807
  resolveRelativeImport,
@@ -44867,9 +46022,9 @@ var FLAKY_THRESHOLD = 0.3, MIN_RUNS_FOR_QUARANTINE = 5, MAX_HISTORY_RUNS = 20;
44867
46022
 
44868
46023
  // src/test-impact/history-store.ts
44869
46024
  import fs15 from "fs";
44870
- import path29 from "path";
46025
+ import path32 from "path";
44871
46026
  function getHistoryPath(workingDir) {
44872
- return path29.join(workingDir || process.cwd(), ".swarm", "cache", "test-history.jsonl");
46027
+ return path32.join(workingDir || process.cwd(), ".swarm", "cache", "test-history.jsonl");
44873
46028
  }
44874
46029
  function sanitizeErrorMessage(errorMessage) {
44875
46030
  if (errorMessage === undefined) {
@@ -44924,7 +46079,7 @@ function appendTestRun(record3, workingDir) {
44924
46079
  changedFiles: sanitizeChangedFiles(record3.changedFiles || [])
44925
46080
  };
44926
46081
  const historyPath = getHistoryPath(workingDir);
44927
- const historyDir = path29.dirname(historyPath);
46082
+ const historyDir = path32.dirname(historyPath);
44928
46083
  if (!fs15.existsSync(historyDir)) {
44929
46084
  fs15.mkdirSync(historyDir, { recursive: true });
44930
46085
  }
@@ -45006,7 +46161,7 @@ var init_history_store = __esm(() => {
45006
46161
 
45007
46162
  // src/tools/resolve-working-directory.ts
45008
46163
  import * as fs16 from "fs";
45009
- import * as path30 from "path";
46164
+ import * as path33 from "path";
45010
46165
  function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
45011
46166
  if (workingDirectory == null || workingDirectory === "") {
45012
46167
  return { success: true, directory: fallbackDirectory };
@@ -45026,15 +46181,15 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
45026
46181
  };
45027
46182
  }
45028
46183
  }
45029
- const normalizedDir = path30.normalize(workingDirectory);
45030
- const pathParts = normalizedDir.split(path30.sep);
46184
+ const normalizedDir = path33.normalize(workingDirectory);
46185
+ const pathParts = normalizedDir.split(path33.sep);
45031
46186
  if (pathParts.includes("..")) {
45032
46187
  return {
45033
46188
  success: false,
45034
46189
  message: "Invalid working_directory: path traversal sequences (..) are not allowed"
45035
46190
  };
45036
46191
  }
45037
- const resolvedDir = path30.resolve(normalizedDir);
46192
+ const resolvedDir = path33.resolve(normalizedDir);
45038
46193
  let statResult;
45039
46194
  try {
45040
46195
  statResult = fs16.statSync(resolvedDir);
@@ -45050,7 +46205,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
45050
46205
  message: `Invalid working_directory: path "${resolvedDir}" is not a directory`
45051
46206
  };
45052
46207
  }
45053
- const resolvedFallback = path30.resolve(fallbackDirectory);
46208
+ const resolvedFallback = path33.resolve(fallbackDirectory);
45054
46209
  let fallbackExists = false;
45055
46210
  try {
45056
46211
  fs16.statSync(resolvedFallback);
@@ -45060,7 +46215,7 @@ function resolveWorkingDirectory(workingDirectory, fallbackDirectory) {
45060
46215
  }
45061
46216
  if (workingDirectory != null && workingDirectory !== "") {
45062
46217
  if (fallbackExists) {
45063
- const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path30.sep);
46218
+ const isSubdirectory = resolvedDir.startsWith(resolvedFallback + path33.sep);
45064
46219
  if (isSubdirectory) {
45065
46220
  return {
45066
46221
  success: false,
@@ -45082,7 +46237,7 @@ var init_resolve_working_directory = () => {};
45082
46237
 
45083
46238
  // src/tools/test-runner.ts
45084
46239
  import * as fs17 from "fs";
45085
- import * as path31 from "path";
46240
+ import * as path34 from "path";
45086
46241
  function isAbsolutePath(str) {
45087
46242
  if (str.startsWith("/"))
45088
46243
  return true;
@@ -45147,14 +46302,14 @@ function hasDevDependency(devDeps, ...patterns) {
45147
46302
  return hasPackageJsonDependency(devDeps, ...patterns);
45148
46303
  }
45149
46304
  function detectGoTest(cwd) {
45150
- return fs17.existsSync(path31.join(cwd, "go.mod")) && isCommandAvailable("go");
46305
+ return fs17.existsSync(path34.join(cwd, "go.mod")) && isCommandAvailable("go");
45151
46306
  }
45152
46307
  function detectJavaMaven(cwd) {
45153
- return fs17.existsSync(path31.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
46308
+ return fs17.existsSync(path34.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
45154
46309
  }
45155
46310
  function detectGradle(cwd) {
45156
- const hasBuildFile = fs17.existsSync(path31.join(cwd, "build.gradle")) || fs17.existsSync(path31.join(cwd, "build.gradle.kts"));
45157
- const hasGradlew = fs17.existsSync(path31.join(cwd, "gradlew")) || fs17.existsSync(path31.join(cwd, "gradlew.bat"));
46311
+ const hasBuildFile = fs17.existsSync(path34.join(cwd, "build.gradle")) || fs17.existsSync(path34.join(cwd, "build.gradle.kts"));
46312
+ const hasGradlew = fs17.existsSync(path34.join(cwd, "gradlew")) || fs17.existsSync(path34.join(cwd, "gradlew.bat"));
45158
46313
  return hasBuildFile && (hasGradlew || isCommandAvailable("gradle"));
45159
46314
  }
45160
46315
  function detectDotnetTest(cwd) {
@@ -45167,30 +46322,30 @@ function detectDotnetTest(cwd) {
45167
46322
  }
45168
46323
  }
45169
46324
  function detectCTest(cwd) {
45170
- const hasSource = fs17.existsSync(path31.join(cwd, "CMakeLists.txt"));
45171
- const hasBuildCache = fs17.existsSync(path31.join(cwd, "CMakeCache.txt")) || fs17.existsSync(path31.join(cwd, "build", "CMakeCache.txt"));
46325
+ const hasSource = fs17.existsSync(path34.join(cwd, "CMakeLists.txt"));
46326
+ const hasBuildCache = fs17.existsSync(path34.join(cwd, "CMakeCache.txt")) || fs17.existsSync(path34.join(cwd, "build", "CMakeCache.txt"));
45172
46327
  return (hasSource || hasBuildCache) && isCommandAvailable("ctest");
45173
46328
  }
45174
46329
  function detectSwiftTest(cwd) {
45175
- return fs17.existsSync(path31.join(cwd, "Package.swift")) && isCommandAvailable("swift");
46330
+ return fs17.existsSync(path34.join(cwd, "Package.swift")) && isCommandAvailable("swift");
45176
46331
  }
45177
46332
  function detectDartTest(cwd) {
45178
- return fs17.existsSync(path31.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
46333
+ return fs17.existsSync(path34.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
45179
46334
  }
45180
46335
  function detectRSpec(cwd) {
45181
- const hasRSpecFile = fs17.existsSync(path31.join(cwd, ".rspec"));
45182
- const hasGemfile = fs17.existsSync(path31.join(cwd, "Gemfile"));
45183
- const hasSpecDir = fs17.existsSync(path31.join(cwd, "spec"));
46336
+ const hasRSpecFile = fs17.existsSync(path34.join(cwd, ".rspec"));
46337
+ const hasGemfile = fs17.existsSync(path34.join(cwd, "Gemfile"));
46338
+ const hasSpecDir = fs17.existsSync(path34.join(cwd, "spec"));
45184
46339
  const hasRSpec = hasRSpecFile || hasGemfile && hasSpecDir;
45185
46340
  return hasRSpec && (isCommandAvailable("bundle") || isCommandAvailable("rspec"));
45186
46341
  }
45187
46342
  function detectMinitest(cwd) {
45188
- return fs17.existsSync(path31.join(cwd, "test")) && (fs17.existsSync(path31.join(cwd, "Gemfile")) || fs17.existsSync(path31.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
46343
+ return fs17.existsSync(path34.join(cwd, "test")) && (fs17.existsSync(path34.join(cwd, "Gemfile")) || fs17.existsSync(path34.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
45189
46344
  }
45190
46345
  async function detectTestFramework(cwd) {
45191
46346
  const baseDir = cwd;
45192
46347
  try {
45193
- const packageJsonPath = path31.join(baseDir, "package.json");
46348
+ const packageJsonPath = path34.join(baseDir, "package.json");
45194
46349
  if (fs17.existsSync(packageJsonPath)) {
45195
46350
  const content = fs17.readFileSync(packageJsonPath, "utf-8");
45196
46351
  const pkg = JSON.parse(content);
@@ -45211,16 +46366,16 @@ async function detectTestFramework(cwd) {
45211
46366
  return "jest";
45212
46367
  if (hasDevDependency(devDeps, "mocha", "@types/mocha"))
45213
46368
  return "mocha";
45214
- if (fs17.existsSync(path31.join(baseDir, "bun.lockb")) || fs17.existsSync(path31.join(baseDir, "bun.lock"))) {
46369
+ if (fs17.existsSync(path34.join(baseDir, "bun.lockb")) || fs17.existsSync(path34.join(baseDir, "bun.lock"))) {
45215
46370
  if (scripts.test?.includes("bun"))
45216
46371
  return "bun";
45217
46372
  }
45218
46373
  }
45219
46374
  } catch {}
45220
46375
  try {
45221
- const pyprojectTomlPath = path31.join(baseDir, "pyproject.toml");
45222
- const setupCfgPath = path31.join(baseDir, "setup.cfg");
45223
- const requirementsTxtPath = path31.join(baseDir, "requirements.txt");
46376
+ const pyprojectTomlPath = path34.join(baseDir, "pyproject.toml");
46377
+ const setupCfgPath = path34.join(baseDir, "setup.cfg");
46378
+ const requirementsTxtPath = path34.join(baseDir, "requirements.txt");
45224
46379
  if (fs17.existsSync(pyprojectTomlPath)) {
45225
46380
  const content = fs17.readFileSync(pyprojectTomlPath, "utf-8");
45226
46381
  if (content.includes("[tool.pytest"))
@@ -45240,7 +46395,7 @@ async function detectTestFramework(cwd) {
45240
46395
  }
45241
46396
  } catch {}
45242
46397
  try {
45243
- const cargoTomlPath = path31.join(baseDir, "Cargo.toml");
46398
+ const cargoTomlPath = path34.join(baseDir, "Cargo.toml");
45244
46399
  if (fs17.existsSync(cargoTomlPath)) {
45245
46400
  const content = fs17.readFileSync(cargoTomlPath, "utf-8");
45246
46401
  if (content.includes("[dev-dependencies]")) {
@@ -45251,9 +46406,9 @@ async function detectTestFramework(cwd) {
45251
46406
  }
45252
46407
  } catch {}
45253
46408
  try {
45254
- const pesterConfigPath = path31.join(baseDir, "pester.config.ps1");
45255
- const pesterConfigJsonPath = path31.join(baseDir, "pester.config.ps1.json");
45256
- const pesterPs1Path = path31.join(baseDir, "tests.ps1");
46409
+ const pesterConfigPath = path34.join(baseDir, "pester.config.ps1");
46410
+ const pesterConfigJsonPath = path34.join(baseDir, "pester.config.ps1.json");
46411
+ const pesterPs1Path = path34.join(baseDir, "tests.ps1");
45257
46412
  if (fs17.existsSync(pesterConfigPath) || fs17.existsSync(pesterConfigJsonPath) || fs17.existsSync(pesterPs1Path)) {
45258
46413
  return "pester";
45259
46414
  }
@@ -45282,12 +46437,12 @@ function isTestDirectoryPath(normalizedPath) {
45282
46437
  return normalizedPath.split("/").some((segment) => TEST_DIRECTORY_NAMES.includes(segment));
45283
46438
  }
45284
46439
  function resolveWorkspacePath(file3, workingDir) {
45285
- return path31.isAbsolute(file3) ? path31.resolve(file3) : path31.resolve(workingDir, file3);
46440
+ return path34.isAbsolute(file3) ? path34.resolve(file3) : path34.resolve(workingDir, file3);
45286
46441
  }
45287
46442
  function toWorkspaceOutputPath(absolutePath, workingDir, preferRelative) {
45288
46443
  if (!preferRelative)
45289
46444
  return absolutePath;
45290
- return path31.relative(workingDir, absolutePath);
46445
+ return path34.relative(workingDir, absolutePath);
45291
46446
  }
45292
46447
  function dedupePush(target, value) {
45293
46448
  if (!target.includes(value)) {
@@ -45324,18 +46479,18 @@ function buildLanguageSpecificTestNames(nameWithoutExt, ext) {
45324
46479
  }
45325
46480
  }
45326
46481
  function getRepoLevelCandidateDirectories(workingDir, relativePath, ext) {
45327
- const relativeDir = path31.dirname(relativePath);
46482
+ const relativeDir = path34.dirname(relativePath);
45328
46483
  const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
45329
46484
  const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
45330
- const rootDir = path31.join(workingDir, dirName);
45331
- return nestedRelativeDir ? [rootDir, path31.join(rootDir, nestedRelativeDir)] : [rootDir];
46485
+ const rootDir = path34.join(workingDir, dirName);
46486
+ return nestedRelativeDir ? [rootDir, path34.join(rootDir, nestedRelativeDir)] : [rootDir];
45332
46487
  });
45333
46488
  const normalizedRelativePath = relativePath.replace(/\\/g, "/");
45334
46489
  if (ext === ".java" && normalizedRelativePath.startsWith("src/main/java/")) {
45335
- directories.push(path31.join(workingDir, "src/test/java", path31.dirname(normalizedRelativePath.slice("src/main/java/".length))));
46490
+ directories.push(path34.join(workingDir, "src/test/java", path34.dirname(normalizedRelativePath.slice("src/main/java/".length))));
45336
46491
  }
45337
46492
  if ((ext === ".kt" || ext === ".java") && normalizedRelativePath.startsWith("src/main/kotlin/")) {
45338
- directories.push(path31.join(workingDir, "src/test/kotlin", path31.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
46493
+ directories.push(path34.join(workingDir, "src/test/kotlin", path34.dirname(normalizedRelativePath.slice("src/main/kotlin/".length))));
45339
46494
  }
45340
46495
  return [...new Set(directories)];
45341
46496
  }
@@ -45363,23 +46518,23 @@ function isLanguageSpecificTestFile(basename5) {
45363
46518
  }
45364
46519
  function isConventionTestFilePath(filePath) {
45365
46520
  const normalizedPath = filePath.replace(/\\/g, "/");
45366
- const basename5 = path31.basename(filePath);
46521
+ const basename5 = path34.basename(filePath);
45367
46522
  return hasCompoundTestExtension(basename5) || basename5.includes(".spec.") || basename5.includes(".test.") || isLanguageSpecificTestFile(basename5) || isTestDirectoryPath(normalizedPath);
45368
46523
  }
45369
46524
  function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45370
46525
  const testFiles = [];
45371
46526
  for (const file3 of sourceFiles) {
45372
46527
  const absoluteFile = resolveWorkspacePath(file3, workingDir);
45373
- const relativeFile = path31.relative(workingDir, absoluteFile);
45374
- const basename5 = path31.basename(absoluteFile);
45375
- const dirname13 = path31.dirname(absoluteFile);
45376
- const preferRelativeOutput = !path31.isAbsolute(file3);
46528
+ const relativeFile = path34.relative(workingDir, absoluteFile);
46529
+ const basename5 = path34.basename(absoluteFile);
46530
+ const dirname16 = path34.dirname(absoluteFile);
46531
+ const preferRelativeOutput = !path34.isAbsolute(file3);
45377
46532
  if (isConventionTestFilePath(relativeFile) || isConventionTestFilePath(file3)) {
45378
46533
  dedupePush(testFiles, toWorkspaceOutputPath(absoluteFile, workingDir, preferRelativeOutput));
45379
46534
  continue;
45380
46535
  }
45381
46536
  const nameWithoutExt = basename5.replace(/\.[^.]+$/, "");
45382
- const ext = path31.extname(basename5);
46537
+ const ext = path34.extname(basename5);
45383
46538
  const genericTestNames = [
45384
46539
  `${nameWithoutExt}.spec${ext}`,
45385
46540
  `${nameWithoutExt}.test${ext}`
@@ -45388,7 +46543,7 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45388
46543
  const colocatedCandidates = [
45389
46544
  ...genericTestNames,
45390
46545
  ...languageSpecificTestNames
45391
- ].map((candidateName) => path31.join(dirname13, candidateName));
46546
+ ].map((candidateName) => path34.join(dirname16, candidateName));
45392
46547
  const testDirectoryNames = [
45393
46548
  basename5,
45394
46549
  ...genericTestNames,
@@ -45397,8 +46552,8 @@ function getTestFilesFromConvention(sourceFiles, workingDir = process.cwd()) {
45397
46552
  const repoLevelDirectories = getRepoLevelCandidateDirectories(workingDir, relativeFile, ext);
45398
46553
  const possibleTestFiles = [
45399
46554
  ...colocatedCandidates,
45400
- ...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path31.join(dirname13, dirName, candidateName))),
45401
- ...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path31.join(candidateDir, candidateName)))
46555
+ ...TEST_DIRECTORY_NAMES.flatMap((dirName) => testDirectoryNames.map((candidateName) => path34.join(dirname16, dirName, candidateName))),
46556
+ ...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((candidateName) => path34.join(candidateDir, candidateName)))
45402
46557
  ];
45403
46558
  for (const testFile of possibleTestFiles) {
45404
46559
  if (fs17.existsSync(testFile)) {
@@ -45419,7 +46574,7 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
45419
46574
  try {
45420
46575
  const absoluteTestFile = resolveWorkspacePath(testFile, workingDir);
45421
46576
  const content = fs17.readFileSync(absoluteTestFile, "utf-8");
45422
- const testDir = path31.dirname(absoluteTestFile);
46577
+ const testDir = path34.dirname(absoluteTestFile);
45423
46578
  const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
45424
46579
  let match;
45425
46580
  match = importRegex.exec(content);
@@ -45427,8 +46582,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
45427
46582
  const importPath = match[1];
45428
46583
  let resolvedImport;
45429
46584
  if (importPath.startsWith(".")) {
45430
- resolvedImport = path31.resolve(testDir, importPath);
45431
- const existingExt = path31.extname(resolvedImport);
46585
+ resolvedImport = path34.resolve(testDir, importPath);
46586
+ const existingExt = path34.extname(resolvedImport);
45432
46587
  if (!existingExt) {
45433
46588
  for (const extToTry of [
45434
46589
  ".ts",
@@ -45448,12 +46603,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
45448
46603
  } else {
45449
46604
  continue;
45450
46605
  }
45451
- const importBasename = path31.basename(resolvedImport, path31.extname(resolvedImport));
45452
- const importDir = path31.dirname(resolvedImport);
46606
+ const importBasename = path34.basename(resolvedImport, path34.extname(resolvedImport));
46607
+ const importDir = path34.dirname(resolvedImport);
45453
46608
  for (const sourceFile of absoluteSourceFiles) {
45454
- const sourceDir = path31.dirname(sourceFile);
45455
- const sourceBasename = path31.basename(sourceFile, path31.extname(sourceFile));
45456
- const isRelatedDir = importDir === sourceDir || importDir === path31.join(sourceDir, "__tests__") || importDir === path31.join(sourceDir, "tests") || importDir === path31.join(sourceDir, "test") || importDir === path31.join(sourceDir, "spec");
46609
+ const sourceDir = path34.dirname(sourceFile);
46610
+ const sourceBasename = path34.basename(sourceFile, path34.extname(sourceFile));
46611
+ const isRelatedDir = importDir === sourceDir || importDir === path34.join(sourceDir, "__tests__") || importDir === path34.join(sourceDir, "tests") || importDir === path34.join(sourceDir, "test") || importDir === path34.join(sourceDir, "spec");
45457
46612
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
45458
46613
  dedupePush(testFiles, testFile);
45459
46614
  break;
@@ -45466,8 +46621,8 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
45466
46621
  while (match !== null) {
45467
46622
  const importPath = match[1];
45468
46623
  if (importPath.startsWith(".")) {
45469
- let resolvedImport = path31.resolve(testDir, importPath);
45470
- const existingExt = path31.extname(resolvedImport);
46624
+ let resolvedImport = path34.resolve(testDir, importPath);
46625
+ const existingExt = path34.extname(resolvedImport);
45471
46626
  if (!existingExt) {
45472
46627
  for (const extToTry of [
45473
46628
  ".ts",
@@ -45484,12 +46639,12 @@ async function getTestFilesFromGraph(sourceFiles, workingDir) {
45484
46639
  }
45485
46640
  }
45486
46641
  }
45487
- const importDir = path31.dirname(resolvedImport);
45488
- const importBasename = path31.basename(resolvedImport, path31.extname(resolvedImport));
46642
+ const importDir = path34.dirname(resolvedImport);
46643
+ const importBasename = path34.basename(resolvedImport, path34.extname(resolvedImport));
45489
46644
  for (const sourceFile of absoluteSourceFiles) {
45490
- const sourceDir = path31.dirname(sourceFile);
45491
- const sourceBasename = path31.basename(sourceFile, path31.extname(sourceFile));
45492
- const isRelatedDir = importDir === sourceDir || importDir === path31.join(sourceDir, "__tests__") || importDir === path31.join(sourceDir, "tests") || importDir === path31.join(sourceDir, "test") || importDir === path31.join(sourceDir, "spec");
46645
+ const sourceDir = path34.dirname(sourceFile);
46646
+ const sourceBasename = path34.basename(sourceFile, path34.extname(sourceFile));
46647
+ const isRelatedDir = importDir === sourceDir || importDir === path34.join(sourceDir, "__tests__") || importDir === path34.join(sourceDir, "tests") || importDir === path34.join(sourceDir, "test") || importDir === path34.join(sourceDir, "spec");
45493
46648
  if (resolvedImport === sourceFile || importBasename === sourceBasename && isRelatedDir) {
45494
46649
  dedupePush(testFiles, testFile);
45495
46650
  break;
@@ -45592,8 +46747,8 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
45592
46747
  return ["mvn", "test"];
45593
46748
  case "gradle": {
45594
46749
  const isWindows = process.platform === "win32";
45595
- const hasGradlewBat = fs17.existsSync(path31.join(baseDir, "gradlew.bat"));
45596
- const hasGradlew = fs17.existsSync(path31.join(baseDir, "gradlew"));
46750
+ const hasGradlewBat = fs17.existsSync(path34.join(baseDir, "gradlew.bat"));
46751
+ const hasGradlew = fs17.existsSync(path34.join(baseDir, "gradlew"));
45597
46752
  if (hasGradlewBat && isWindows)
45598
46753
  return ["gradlew.bat", "test"];
45599
46754
  if (hasGradlew)
@@ -45610,7 +46765,7 @@ function buildTestCommand(framework, scope, files, coverage, baseDir) {
45610
46765
  "cmake-build-release",
45611
46766
  "out"
45612
46767
  ];
45613
- const actualBuildDir = buildDirCandidates.find((d) => fs17.existsSync(path31.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
46768
+ const actualBuildDir = buildDirCandidates.find((d) => fs17.existsSync(path34.join(baseDir, d, "CMakeCache.txt"))) ?? "build";
45614
46769
  return ["ctest", "--test-dir", actualBuildDir];
45615
46770
  }
45616
46771
  case "swift-test":
@@ -46263,7 +47418,7 @@ var init_test_runner = __esm(() => {
46263
47418
  const sourceFiles = args.files.filter((file3) => {
46264
47419
  if (directTestFiles.includes(file3))
46265
47420
  return false;
46266
- const ext = path31.extname(file3).toLowerCase();
47421
+ const ext = path34.extname(file3).toLowerCase();
46267
47422
  return SOURCE_EXTENSIONS.has(ext);
46268
47423
  });
46269
47424
  const invalidFiles = args.files.filter((file3) => !directTestFiles.includes(file3) && !sourceFiles.includes(file3));
@@ -46298,7 +47453,7 @@ var init_test_runner = __esm(() => {
46298
47453
  if (isConventionTestFilePath(f)) {
46299
47454
  return false;
46300
47455
  }
46301
- const ext = path31.extname(f).toLowerCase();
47456
+ const ext = path34.extname(f).toLowerCase();
46302
47457
  return SOURCE_EXTENSIONS.has(ext);
46303
47458
  });
46304
47459
  if (sourceFiles.length === 0) {
@@ -46325,7 +47480,7 @@ var init_test_runner = __esm(() => {
46325
47480
  if (isConventionTestFilePath(f)) {
46326
47481
  return false;
46327
47482
  }
46328
- const ext = path31.extname(f).toLowerCase();
47483
+ const ext = path34.extname(f).toLowerCase();
46329
47484
  return SOURCE_EXTENSIONS.has(ext);
46330
47485
  });
46331
47486
  if (sourceFiles.length === 0) {
@@ -46343,8 +47498,8 @@ var init_test_runner = __esm(() => {
46343
47498
  const impactResult = await analyzeImpact(sourceFiles, workingDir);
46344
47499
  if (impactResult.impactedTests.length > 0) {
46345
47500
  testFiles = impactResult.impactedTests.map((absPath) => {
46346
- const relativePath = path31.relative(workingDir, absPath);
46347
- return path31.isAbsolute(relativePath) ? absPath : relativePath;
47501
+ const relativePath = path34.relative(workingDir, absPath);
47502
+ return path34.isAbsolute(relativePath) ? absPath : relativePath;
46348
47503
  });
46349
47504
  } else {
46350
47505
  graphFallbackReason = "no impacted tests found via impact analysis, falling back to graph";
@@ -46420,7 +47575,7 @@ var init_test_runner = __esm(() => {
46420
47575
 
46421
47576
  // src/services/preflight-service.ts
46422
47577
  import * as fs18 from "fs";
46423
- import * as path32 from "path";
47578
+ import * as path35 from "path";
46424
47579
  function validateDirectoryPath(dir) {
46425
47580
  if (!dir || typeof dir !== "string") {
46426
47581
  throw new Error("Directory path is required");
@@ -46428,8 +47583,8 @@ function validateDirectoryPath(dir) {
46428
47583
  if (dir.includes("..")) {
46429
47584
  throw new Error("Directory path must not contain path traversal sequences");
46430
47585
  }
46431
- const normalized = path32.normalize(dir);
46432
- const absolutePath = path32.isAbsolute(normalized) ? normalized : path32.resolve(normalized);
47586
+ const normalized = path35.normalize(dir);
47587
+ const absolutePath = path35.isAbsolute(normalized) ? normalized : path35.resolve(normalized);
46433
47588
  return absolutePath;
46434
47589
  }
46435
47590
  function validateTimeout(timeoutMs, defaultValue) {
@@ -46452,7 +47607,7 @@ function validateTimeout(timeoutMs, defaultValue) {
46452
47607
  }
46453
47608
  function getPackageVersion(dir) {
46454
47609
  try {
46455
- const packagePath = path32.join(dir, "package.json");
47610
+ const packagePath = path35.join(dir, "package.json");
46456
47611
  if (fs18.existsSync(packagePath)) {
46457
47612
  const content = fs18.readFileSync(packagePath, "utf-8");
46458
47613
  const pkg = JSON.parse(content);
@@ -46463,7 +47618,7 @@ function getPackageVersion(dir) {
46463
47618
  }
46464
47619
  function getChangelogVersion(dir) {
46465
47620
  try {
46466
- const changelogPath = path32.join(dir, "CHANGELOG.md");
47621
+ const changelogPath = path35.join(dir, "CHANGELOG.md");
46467
47622
  if (fs18.existsSync(changelogPath)) {
46468
47623
  const content = fs18.readFileSync(changelogPath, "utf-8");
46469
47624
  const match = content.match(/^##\s*\[?(\d+\.\d+\.\d+)\]?/m);
@@ -46477,7 +47632,7 @@ function getChangelogVersion(dir) {
46477
47632
  function getVersionFileVersion(dir) {
46478
47633
  const possibleFiles = ["VERSION.txt", "version.txt", "VERSION", "version"];
46479
47634
  for (const file3 of possibleFiles) {
46480
- const filePath = path32.join(dir, file3);
47635
+ const filePath = path35.join(dir, file3);
46481
47636
  if (fs18.existsSync(filePath)) {
46482
47637
  try {
46483
47638
  const content = fs18.readFileSync(filePath, "utf-8").trim();
@@ -46493,9 +47648,9 @@ function getVersionFileVersion(dir) {
46493
47648
  async function runVersionCheck(dir, _timeoutMs) {
46494
47649
  const startTime = Date.now();
46495
47650
  try {
46496
- const packageVersion = _internals18.getPackageVersion(dir);
46497
- const changelogVersion = _internals18.getChangelogVersion(dir);
46498
- const versionFileVersion = _internals18.getVersionFileVersion(dir);
47651
+ const packageVersion = _internals19.getPackageVersion(dir);
47652
+ const changelogVersion = _internals19.getChangelogVersion(dir);
47653
+ const versionFileVersion = _internals19.getVersionFileVersion(dir);
46499
47654
  const versions3 = [];
46500
47655
  if (packageVersion)
46501
47656
  versions3.push(`package.json: ${packageVersion}`);
@@ -46804,7 +47959,7 @@ async function runEvidenceCheck(dir) {
46804
47959
  async function runRequirementCoverageCheck(dir, currentPhase) {
46805
47960
  const startTime = Date.now();
46806
47961
  try {
46807
- const specPath = path32.join(dir, ".swarm", "spec.md");
47962
+ const specPath = path35.join(dir, ".swarm", "spec.md");
46808
47963
  if (!fs18.existsSync(specPath)) {
46809
47964
  return {
46810
47965
  type: "req_coverage",
@@ -46845,7 +48000,7 @@ async function runPreflight(dir, phase, config3) {
46845
48000
  const reportId = `preflight-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
46846
48001
  let validatedDir;
46847
48002
  try {
46848
- validatedDir = _internals18.validateDirectoryPath(dir);
48003
+ validatedDir = _internals19.validateDirectoryPath(dir);
46849
48004
  } catch (error93) {
46850
48005
  return {
46851
48006
  id: reportId,
@@ -46865,7 +48020,7 @@ async function runPreflight(dir, phase, config3) {
46865
48020
  }
46866
48021
  let validatedTimeout;
46867
48022
  try {
46868
- validatedTimeout = _internals18.validateTimeout(config3?.checkTimeoutMs, DEFAULT_CONFIG.checkTimeoutMs);
48023
+ validatedTimeout = _internals19.validateTimeout(config3?.checkTimeoutMs, DEFAULT_CONFIG.checkTimeoutMs);
46869
48024
  } catch (error93) {
46870
48025
  return {
46871
48026
  id: reportId,
@@ -46906,12 +48061,12 @@ async function runPreflight(dir, phase, config3) {
46906
48061
  });
46907
48062
  const checks5 = [];
46908
48063
  log("[Preflight] Running lint check...");
46909
- const lintResult = await _internals18.runLintCheck(validatedDir, cfg.linter, cfg.checkTimeoutMs);
48064
+ const lintResult = await _internals19.runLintCheck(validatedDir, cfg.linter, cfg.checkTimeoutMs);
46910
48065
  checks5.push(lintResult);
46911
48066
  log(`[Preflight] Lint check: ${lintResult.status} ${lintResult.message}`);
46912
48067
  if (!cfg.skipTests) {
46913
48068
  log("[Preflight] Running tests check...");
46914
- const testsResult = await _internals18.runTestsCheck(validatedDir, cfg.testScope, cfg.checkTimeoutMs);
48069
+ const testsResult = await _internals19.runTestsCheck(validatedDir, cfg.testScope, cfg.checkTimeoutMs);
46915
48070
  checks5.push(testsResult);
46916
48071
  log(`[Preflight] Tests check: ${testsResult.status} ${testsResult.message}`);
46917
48072
  } else {
@@ -46923,7 +48078,7 @@ async function runPreflight(dir, phase, config3) {
46923
48078
  }
46924
48079
  if (!cfg.skipSecrets) {
46925
48080
  log("[Preflight] Running secrets check...");
46926
- const secretsResult = await _internals18.runSecretsCheck(validatedDir, cfg.checkTimeoutMs);
48081
+ const secretsResult = await _internals19.runSecretsCheck(validatedDir, cfg.checkTimeoutMs);
46927
48082
  checks5.push(secretsResult);
46928
48083
  log(`[Preflight] Secrets check: ${secretsResult.status} ${secretsResult.message}`);
46929
48084
  } else {
@@ -46935,7 +48090,7 @@ async function runPreflight(dir, phase, config3) {
46935
48090
  }
46936
48091
  if (!cfg.skipEvidence) {
46937
48092
  log("[Preflight] Running evidence check...");
46938
- const evidenceResult = await _internals18.runEvidenceCheck(validatedDir);
48093
+ const evidenceResult = await _internals19.runEvidenceCheck(validatedDir);
46939
48094
  checks5.push(evidenceResult);
46940
48095
  log(`[Preflight] Evidence check: ${evidenceResult.status} ${evidenceResult.message}`);
46941
48096
  } else {
@@ -46946,12 +48101,12 @@ async function runPreflight(dir, phase, config3) {
46946
48101
  });
46947
48102
  }
46948
48103
  log("[Preflight] Running requirement coverage check...");
46949
- const reqCoverageResult = await _internals18.runRequirementCoverageCheck(validatedDir, phase);
48104
+ const reqCoverageResult = await _internals19.runRequirementCoverageCheck(validatedDir, phase);
46950
48105
  checks5.push(reqCoverageResult);
46951
48106
  log(`[Preflight] Requirement coverage check: ${reqCoverageResult.status} ${reqCoverageResult.message}`);
46952
48107
  if (!cfg.skipVersion) {
46953
48108
  log("[Preflight] Running version check...");
46954
- const versionResult = await _internals18.runVersionCheck(validatedDir, cfg.checkTimeoutMs);
48109
+ const versionResult = await _internals19.runVersionCheck(validatedDir, cfg.checkTimeoutMs);
46955
48110
  checks5.push(versionResult);
46956
48111
  log(`[Preflight] Version check: ${versionResult.status} ${versionResult.message}`);
46957
48112
  } else {
@@ -47014,10 +48169,10 @@ function formatPreflightMarkdown(report) {
47014
48169
  async function handlePreflightCommand(directory, _args) {
47015
48170
  const plan = await loadPlan(directory);
47016
48171
  const phase = plan?.current_phase ?? 1;
47017
- const report = await _internals18.runPreflight(directory, phase);
47018
- return _internals18.formatPreflightMarkdown(report);
48172
+ const report = await _internals19.runPreflight(directory, phase);
48173
+ return _internals19.formatPreflightMarkdown(report);
47019
48174
  }
47020
- var MIN_CHECK_TIMEOUT_MS = 5000, MAX_CHECK_TIMEOUT_MS = 300000, DEFAULT_CONFIG, _internals18;
48175
+ var MIN_CHECK_TIMEOUT_MS = 5000, MAX_CHECK_TIMEOUT_MS = 300000, DEFAULT_CONFIG, _internals19;
47021
48176
  var init_preflight_service = __esm(() => {
47022
48177
  init_manager2();
47023
48178
  init_manager();
@@ -47034,7 +48189,7 @@ var init_preflight_service = __esm(() => {
47034
48189
  testScope: "convention",
47035
48190
  linter: "biome"
47036
48191
  };
47037
- _internals18 = {
48192
+ _internals19 = {
47038
48193
  runPreflight,
47039
48194
  formatPreflightMarkdown,
47040
48195
  handlePreflightCommand,
@@ -47921,7 +49076,7 @@ var init_manager3 = __esm(() => {
47921
49076
 
47922
49077
  // src/commands/reset.ts
47923
49078
  import * as fs19 from "fs";
47924
- import * as path33 from "path";
49079
+ import * as path36 from "path";
47925
49080
  async function handleResetCommand(directory, args) {
47926
49081
  const hasConfirm = args.includes("--confirm");
47927
49082
  if (!hasConfirm) {
@@ -47961,7 +49116,7 @@ async function handleResetCommand(directory, args) {
47961
49116
  }
47962
49117
  for (const filename of ["SWARM_PLAN.md", "SWARM_PLAN.json"]) {
47963
49118
  try {
47964
- const rootPath = path33.join(directory, filename);
49119
+ const rootPath = path36.join(directory, filename);
47965
49120
  if (fs19.existsSync(rootPath)) {
47966
49121
  fs19.unlinkSync(rootPath);
47967
49122
  results.push(`- \u2705 Deleted ${filename} (root)`);
@@ -48001,7 +49156,7 @@ var init_reset = __esm(() => {
48001
49156
 
48002
49157
  // src/commands/reset-session.ts
48003
49158
  import * as fs20 from "fs";
48004
- import * as path34 from "path";
49159
+ import * as path37 from "path";
48005
49160
  async function handleResetSessionCommand(directory, _args) {
48006
49161
  const results = [];
48007
49162
  try {
@@ -48016,13 +49171,13 @@ async function handleResetSessionCommand(directory, _args) {
48016
49171
  results.push("\u274C Failed to delete state.json");
48017
49172
  }
48018
49173
  try {
48019
- const sessionDir = path34.dirname(validateSwarmPath(directory, "session/state.json"));
49174
+ const sessionDir = path37.dirname(validateSwarmPath(directory, "session/state.json"));
48020
49175
  if (fs20.existsSync(sessionDir)) {
48021
49176
  const files = fs20.readdirSync(sessionDir);
48022
49177
  const otherFiles = files.filter((f) => f !== "state.json");
48023
49178
  let deletedCount = 0;
48024
49179
  for (const file3 of otherFiles) {
48025
- const filePath = path34.join(sessionDir, file3);
49180
+ const filePath = path37.join(sessionDir, file3);
48026
49181
  if (fs20.lstatSync(filePath).isFile()) {
48027
49182
  fs20.unlinkSync(filePath);
48028
49183
  deletedCount++;
@@ -48054,7 +49209,7 @@ var init_reset_session = __esm(() => {
48054
49209
  });
48055
49210
 
48056
49211
  // src/summaries/manager.ts
48057
- import * as path35 from "path";
49212
+ import * as path38 from "path";
48058
49213
  function sanitizeSummaryId(id) {
48059
49214
  if (!id || id.length === 0) {
48060
49215
  throw new Error("Invalid summary ID: empty string");
@@ -48077,7 +49232,7 @@ function sanitizeSummaryId(id) {
48077
49232
  }
48078
49233
  async function loadFullOutput(directory, id) {
48079
49234
  const sanitizedId = sanitizeSummaryId(id);
48080
- const relativePath = path35.join("summaries", `${sanitizedId}.json`);
49235
+ const relativePath = path38.join("summaries", `${sanitizedId}.json`);
48081
49236
  validateSwarmPath(directory, relativePath);
48082
49237
  const content = await readSwarmFileAsync(directory, relativePath);
48083
49238
  if (content === null) {
@@ -48140,7 +49295,7 @@ var init_retrieve = __esm(() => {
48140
49295
 
48141
49296
  // src/commands/rollback.ts
48142
49297
  import * as fs21 from "fs";
48143
- import * as path36 from "path";
49298
+ import * as path39 from "path";
48144
49299
  async function handleRollbackCommand(directory, args) {
48145
49300
  const phaseArg = args[0];
48146
49301
  if (!phaseArg) {
@@ -48205,8 +49360,8 @@ async function handleRollbackCommand(directory, args) {
48205
49360
  if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
48206
49361
  continue;
48207
49362
  }
48208
- const src = path36.join(checkpointDir, file3);
48209
- const dest = path36.join(swarmDir, file3);
49363
+ const src = path39.join(checkpointDir, file3);
49364
+ const dest = path39.join(swarmDir, file3);
48210
49365
  try {
48211
49366
  fs21.cpSync(src, dest, { recursive: true, force: true });
48212
49367
  successes.push(file3);
@@ -48225,12 +49380,12 @@ async function handleRollbackCommand(directory, args) {
48225
49380
  ].join(`
48226
49381
  `);
48227
49382
  }
48228
- const existingLedgerPath = path36.join(swarmDir, "plan-ledger.jsonl");
49383
+ const existingLedgerPath = path39.join(swarmDir, "plan-ledger.jsonl");
48229
49384
  if (fs21.existsSync(existingLedgerPath)) {
48230
49385
  fs21.unlinkSync(existingLedgerPath);
48231
49386
  }
48232
49387
  try {
48233
- const planJsonPath = path36.join(swarmDir, "plan.json");
49388
+ const planJsonPath = path39.join(swarmDir, "plan.json");
48234
49389
  if (fs21.existsSync(planJsonPath)) {
48235
49390
  const planRaw = fs21.readFileSync(planJsonPath, "utf-8");
48236
49391
  const plan = PlanSchema.parse(JSON.parse(planRaw));
@@ -48291,7 +49446,7 @@ async function handleSimulateCommand(directory, args) {
48291
49446
  }
48292
49447
  let darkMatterPairs;
48293
49448
  try {
48294
- darkMatterPairs = await _internals9.detectDarkMatter(directory, options);
49449
+ darkMatterPairs = await _internals10.detectDarkMatter(directory, options);
48295
49450
  } catch (err) {
48296
49451
  const errMsg = err instanceof Error ? err.message : String(err);
48297
49452
  return `## Simulate Report
@@ -48321,9 +49476,9 @@ Ensure this is a git repository with commit history.`;
48321
49476
  `);
48322
49477
  try {
48323
49478
  const fs22 = await import("fs/promises");
48324
- const path37 = await import("path");
48325
- const reportPath = path37.join(directory, ".swarm", "simulate-report.md");
48326
- await fs22.mkdir(path37.dirname(reportPath), { recursive: true });
49479
+ const path40 = await import("path");
49480
+ const reportPath = path40.join(directory, ".swarm", "simulate-report.md");
49481
+ await fs22.mkdir(path40.dirname(reportPath), { recursive: true });
48327
49482
  await fs22.writeFile(reportPath, report, "utf-8");
48328
49483
  } catch (err) {
48329
49484
  const writeErr = err instanceof Error ? err.message : String(err);
@@ -48345,6 +49500,158 @@ async function handleSpecifyCommand(_directory, args) {
48345
49500
  return "[MODE: SPECIFY] Please enter MODE: SPECIFY and generate a spec for this project.";
48346
49501
  }
48347
49502
 
49503
+ // src/turbo/lean/state.ts
49504
+ import * as fs22 from "fs";
49505
+ import * as path40 from "path";
49506
+ function nowISO2() {
49507
+ return new Date().toISOString();
49508
+ }
49509
+ function ensureSwarmDir2(directory) {
49510
+ const swarmDir = path40.resolve(directory, ".swarm");
49511
+ if (!fs22.existsSync(swarmDir)) {
49512
+ fs22.mkdirSync(swarmDir, { recursive: true });
49513
+ }
49514
+ return swarmDir;
49515
+ }
49516
+ function emptyCounters2() {
49517
+ return {
49518
+ lanesPlanned: 0,
49519
+ lanesStarted: 0,
49520
+ lanesCompleted: 0,
49521
+ lanesFailed: 0,
49522
+ tasksSerialized: 0,
49523
+ tasksDegraded: 0
49524
+ };
49525
+ }
49526
+ function emptyRunState(sessionID, maxParallelCoders) {
49527
+ return {
49528
+ status: "idle",
49529
+ sessionID,
49530
+ strategy: "lean",
49531
+ maxParallelCoders,
49532
+ lanes: [],
49533
+ degradedTasks: [],
49534
+ serializedTasks: [],
49535
+ counters: emptyCounters2()
49536
+ };
49537
+ }
49538
+ function emptyPersisted2() {
49539
+ return {
49540
+ version: 1,
49541
+ updatedAt: nowISO2(),
49542
+ sessions: {}
49543
+ };
49544
+ }
49545
+ function isStateUnreadable(directory) {
49546
+ return stateUnreadableMap.get(directory) ?? false;
49547
+ }
49548
+ function markStateUnreadable2(directory, reason) {
49549
+ stateUnreadableMap.set(directory, true);
49550
+ error(`[turbo/lean/state] state file unreadable for ${directory}: ${reason} \u2014 failing closed`);
49551
+ }
49552
+ function readPersisted2(directory) {
49553
+ try {
49554
+ const filePath = path40.join(directory, ".swarm", STATE_FILE2);
49555
+ if (!fs22.existsSync(filePath)) {
49556
+ const seed = emptyPersisted2();
49557
+ try {
49558
+ ensureSwarmDir2(directory);
49559
+ fs22.writeFileSync(filePath, `${JSON.stringify(seed, null, 2)}
49560
+ `, "utf-8");
49561
+ } catch {}
49562
+ return seed;
49563
+ }
49564
+ const raw = fs22.readFileSync(filePath, "utf-8");
49565
+ const parsed = JSON.parse(raw);
49566
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
49567
+ markStateUnreadable2(directory, `malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
49568
+ return null;
49569
+ }
49570
+ return {
49571
+ version: 1,
49572
+ updatedAt: parsed.updatedAt ?? nowISO2(),
49573
+ sessions: parsed.sessions
49574
+ };
49575
+ } catch (error93) {
49576
+ const reason = error93 instanceof Error ? error93.message : String(error93);
49577
+ markStateUnreadable2(directory, reason);
49578
+ return null;
49579
+ }
49580
+ }
49581
+ function writePersisted2(directory, persisted) {
49582
+ if (stateUnreadableMap.get(directory)) {
49583
+ throw new Error(`Lean Turbo state is unreadable. Please repair .swarm/${STATE_FILE2} before continuing.`);
49584
+ }
49585
+ let filePath;
49586
+ let tmpPath;
49587
+ let payload;
49588
+ try {
49589
+ ensureSwarmDir2(directory);
49590
+ filePath = path40.join(directory, ".swarm", STATE_FILE2);
49591
+ tmpPath = `${filePath}.tmp.${Date.now()}`;
49592
+ persisted.updatedAt = nowISO2();
49593
+ payload = `${JSON.stringify(persisted, null, 2)}
49594
+ `;
49595
+ } catch (error93) {
49596
+ const msg = error93 instanceof Error ? error93.message : String(error93);
49597
+ error(`[turbo/lean/state] Failed to prepare ${STATE_FILE2} write: ${msg}`);
49598
+ throw new Error(`Lean Turbo state persistence prepare failed: ${msg}`);
49599
+ }
49600
+ try {
49601
+ fs22.writeFileSync(tmpPath, payload, "utf-8");
49602
+ fs22.renameSync(tmpPath, filePath);
49603
+ } catch (error93) {
49604
+ const msg = error93 instanceof Error ? error93.message : String(error93);
49605
+ error(`[turbo/lean/state] Failed to persist ${STATE_FILE2} atomically: ${msg}`);
49606
+ try {
49607
+ if (fs22.existsSync(tmpPath)) {
49608
+ fs22.unlinkSync(tmpPath);
49609
+ }
49610
+ } catch {}
49611
+ throw new Error(`Lean Turbo state persistence failed: ${msg}`);
49612
+ }
49613
+ }
49614
+ function loadLeanTurboRunState(directory, sessionID) {
49615
+ if (stateUnreadableMap.get(directory))
49616
+ return null;
49617
+ const persisted = readPersisted2(directory);
49618
+ if (!persisted)
49619
+ return null;
49620
+ return persisted.sessions[sessionID] ?? null;
49621
+ }
49622
+ function saveLeanTurboRunState(directory, runState) {
49623
+ if (stateUnreadableMap.get(directory)) {
49624
+ throw new Error(`Lean Turbo state is unreadable for ${directory}. Please repair .swarm/${STATE_FILE2} before continuing.`);
49625
+ }
49626
+ const persisted = readPersisted2(directory);
49627
+ if (!persisted) {
49628
+ throw new Error(`Lean Turbo state is unreadable for ${directory}. Please repair .swarm/${STATE_FILE2} before continuing.`);
49629
+ }
49630
+ persisted.sessions[runState.sessionID] = runState;
49631
+ writePersisted2(directory, persisted);
49632
+ }
49633
+ function pauseLeanTurboRun(directory, sessionID, reason) {
49634
+ if (stateUnreadableMap.get(directory)) {
49635
+ throw new Error(`Lean Turbo state is unreadable for ${directory}. Please repair .swarm/${STATE_FILE2} before continuing.`);
49636
+ }
49637
+ const persisted = readPersisted2(directory);
49638
+ if (!persisted) {
49639
+ throw new Error(`Lean Turbo state is unreadable for ${directory}. Please repair .swarm/${STATE_FILE2} before continuing.`);
49640
+ }
49641
+ const state = persisted.sessions[sessionID];
49642
+ if (!state)
49643
+ return;
49644
+ state.status = "paused";
49645
+ state.pauseReason = reason;
49646
+ persisted.sessions[sessionID] = state;
49647
+ writePersisted2(directory, persisted);
49648
+ }
49649
+ var STATE_FILE2 = "turbo-state.json", stateUnreadableMap;
49650
+ var init_state3 = __esm(() => {
49651
+ init_logger();
49652
+ stateUnreadableMap = new Map;
49653
+ });
49654
+
48348
49655
  // src/services/compaction-service.ts
48349
49656
  function makeInitialState() {
48350
49657
  return {
@@ -48408,66 +49715,126 @@ var init_context_budget_service = __esm(() => {
48408
49715
  // src/services/status-service.ts
48409
49716
  async function getStatusData(directory, agents) {
48410
49717
  const plan = await loadPlan(directory);
49718
+ let status;
48411
49719
  if (plan && plan.migration_status !== "migration_failed") {
48412
- const currentPhase2 = extractCurrentPhaseFromPlan(plan) || "Unknown";
48413
- let completedTasks2 = 0;
48414
- let totalTasks2 = 0;
49720
+ const currentPhase = extractCurrentPhaseFromPlan(plan) || "Unknown";
49721
+ let completedTasks = 0;
49722
+ let totalTasks = 0;
48415
49723
  for (const phase of plan.phases) {
48416
49724
  for (const task of phase.tasks) {
48417
- totalTasks2++;
49725
+ totalTasks++;
48418
49726
  if (task.status === "completed")
48419
- completedTasks2++;
49727
+ completedTasks++;
48420
49728
  }
48421
49729
  }
48422
- const agentCount2 = Object.keys(agents).length;
48423
- const metrics2 = getCompactionMetrics();
48424
- return {
49730
+ const agentCount = Object.keys(agents).length;
49731
+ const metrics = getCompactionMetrics();
49732
+ status = {
48425
49733
  hasPlan: true,
48426
- currentPhase: currentPhase2,
48427
- completedTasks: completedTasks2,
48428
- totalTasks: totalTasks2,
48429
- agentCount: agentCount2,
49734
+ currentPhase,
49735
+ completedTasks,
49736
+ totalTasks,
49737
+ agentCount,
48430
49738
  isLegacy: false,
48431
49739
  turboMode: hasActiveTurboMode(),
48432
49740
  contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
48433
- compactionCount: metrics2.compactionCount,
48434
- lastSnapshotAt: metrics2.lastSnapshotAt
49741
+ compactionCount: metrics.compactionCount,
49742
+ lastSnapshotAt: metrics.lastSnapshotAt
48435
49743
  };
49744
+ } else {
49745
+ const planContent = await readSwarmFileAsync(directory, "plan.md");
49746
+ if (!planContent) {
49747
+ const metrics = getCompactionMetrics();
49748
+ status = {
49749
+ hasPlan: false,
49750
+ currentPhase: "Unknown",
49751
+ completedTasks: 0,
49752
+ totalTasks: 0,
49753
+ agentCount: Object.keys(agents).length,
49754
+ isLegacy: true,
49755
+ turboMode: hasActiveTurboMode(),
49756
+ contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
49757
+ compactionCount: metrics.compactionCount,
49758
+ lastSnapshotAt: metrics.lastSnapshotAt
49759
+ };
49760
+ } else {
49761
+ const currentPhase = extractCurrentPhase(planContent) || "Unknown";
49762
+ const completedTasks = (planContent.match(/^- \[x\]/gm) || []).length;
49763
+ const incompleteTasks = (planContent.match(/^- \[ \]/gm) || []).length;
49764
+ const totalTasks = completedTasks + incompleteTasks;
49765
+ const agentCount = Object.keys(agents).length;
49766
+ const metrics = getCompactionMetrics();
49767
+ status = {
49768
+ hasPlan: true,
49769
+ currentPhase,
49770
+ completedTasks,
49771
+ totalTasks,
49772
+ agentCount,
49773
+ isLegacy: true,
49774
+ turboMode: hasActiveTurboMode(),
49775
+ contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
49776
+ compactionCount: metrics.compactionCount,
49777
+ lastSnapshotAt: metrics.lastSnapshotAt
49778
+ };
49779
+ }
48436
49780
  }
48437
- const planContent = await readSwarmFileAsync(directory, "plan.md");
48438
- if (!planContent) {
48439
- const metrics2 = getCompactionMetrics();
48440
- return {
48441
- hasPlan: false,
48442
- currentPhase: "Unknown",
48443
- completedTasks: 0,
48444
- totalTasks: 0,
48445
- agentCount: Object.keys(agents).length,
48446
- isLegacy: true,
48447
- turboMode: hasActiveTurboMode(),
48448
- contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
48449
- compactionCount: metrics2.compactionCount,
48450
- lastSnapshotAt: metrics2.lastSnapshotAt
48451
- };
49781
+ return enrichWithLeanTurbo(status, directory);
49782
+ }
49783
+ function enrichWithLeanTurbo(status, directory) {
49784
+ const turboMode = hasActiveTurboMode();
49785
+ const leanActive = _internals20.hasActiveLeanTurbo();
49786
+ let turboStrategy = "off";
49787
+ if (leanActive) {
49788
+ turboStrategy = "lean";
49789
+ } else if (turboMode) {
49790
+ turboStrategy = "standard";
48452
49791
  }
48453
- const currentPhase = extractCurrentPhase(planContent) || "Unknown";
48454
- const completedTasks = (planContent.match(/^- \[x\]/gm) || []).length;
48455
- const incompleteTasks = (planContent.match(/^- \[ \]/gm) || []).length;
48456
- const totalTasks = completedTasks + incompleteTasks;
48457
- const agentCount = Object.keys(agents).length;
48458
- const metrics = getCompactionMetrics();
48459
- return {
48460
- hasPlan: true,
48461
- currentPhase,
48462
- completedTasks,
48463
- totalTasks,
48464
- agentCount,
48465
- isLegacy: true,
48466
- turboMode: hasActiveTurboMode(),
48467
- contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
48468
- compactionCount: metrics.compactionCount,
48469
- lastSnapshotAt: metrics.lastSnapshotAt
48470
- };
49792
+ status.turboStrategy = turboStrategy;
49793
+ if (!leanActive) {
49794
+ return status;
49795
+ }
49796
+ let leanSessionID = null;
49797
+ for (const [sessionId, session] of swarmState.agentSessions) {
49798
+ if (session.turboStrategy === "lean" && session.leanTurboActive === true) {
49799
+ leanSessionID = sessionId;
49800
+ break;
49801
+ }
49802
+ }
49803
+ if (leanSessionID) {
49804
+ const runState = _internals20.loadLeanTurboRunState(directory, leanSessionID);
49805
+ if (runState) {
49806
+ status.leanTurboPhase = runState.phase;
49807
+ status.leanMaxParallelCoders = runState.maxParallelCoders;
49808
+ status.leanPauseReason = runState.pauseReason;
49809
+ if (!Array.isArray(runState.lanes)) {
49810
+ runState.lanes = [];
49811
+ }
49812
+ let activeLanes = 0;
49813
+ let completedLanes = 0;
49814
+ for (const lane of runState.lanes) {
49815
+ if (lane.status === "running")
49816
+ activeLanes++;
49817
+ if (lane.status === "completed")
49818
+ completedLanes++;
49819
+ }
49820
+ status.leanActiveLaneCount = activeLanes;
49821
+ status.leanCompletedLanes = completedLanes;
49822
+ if (!Array.isArray(runState.degradedTasks)) {
49823
+ runState.degradedTasks = [];
49824
+ status.leanDegradedTasks = 0;
49825
+ }
49826
+ if (runState.degradedTasks.length > 0) {
49827
+ status.leanDegradedTasks = runState.degradedTasks.length;
49828
+ const summaryParts = [];
49829
+ for (const dt of runState.degradedTasks) {
49830
+ summaryParts.push(`${dt.taskId} (${dt.reason})`);
49831
+ }
49832
+ status.leanDegradationSummary = summaryParts.join("; ");
49833
+ }
49834
+ }
49835
+ }
49836
+ status.fullAutoActive = _internals20.hasActiveFullAuto();
49837
+ return status;
48471
49838
  }
48472
49839
  function formatStatusMarkdown(status) {
48473
49840
  const lines = [
@@ -48477,8 +49844,36 @@ function formatStatusMarkdown(status) {
48477
49844
  `**Tasks**: ${status.completedTasks}/${status.totalTasks} complete`,
48478
49845
  `**Agents**: ${status.agentCount} registered`
48479
49846
  ];
48480
- if (status.turboMode) {
48481
- lines.push("", `**TURBO MODE**: active`);
49847
+ if (status.turboStrategy && status.turboStrategy !== "off") {
49848
+ lines.push("");
49849
+ if (status.turboStrategy === "lean") {
49850
+ const parts = ["lean"];
49851
+ if (status.leanTurboPhase !== undefined) {
49852
+ parts.push(`Phase ${status.leanTurboPhase}`);
49853
+ }
49854
+ if (status.leanActiveLaneCount !== undefined) {
49855
+ const totalLanes = (status.leanActiveLaneCount ?? 0) + (status.leanCompletedLanes ?? 0);
49856
+ parts.push(`${status.leanActiveLaneCount}/${totalLanes} lanes active`);
49857
+ }
49858
+ if (status.leanDegradedTasks !== undefined && status.leanDegradedTasks > 0) {
49859
+ parts.push(`${status.leanDegradedTasks} degraded`);
49860
+ }
49861
+ lines.push(`**Turbo**: ${parts.join(", ")}`);
49862
+ if (status.leanDegradationSummary) {
49863
+ lines.push(` - ${status.leanDegradationSummary}`);
49864
+ }
49865
+ if (status.leanPauseReason) {
49866
+ lines.push(`**Lean paused**: ${status.leanPauseReason}`);
49867
+ }
49868
+ } else {
49869
+ lines.push(`**Turbo**: standard`);
49870
+ }
49871
+ if (status.fullAutoActive) {
49872
+ lines.push(`**Full-Auto**: active`);
49873
+ }
49874
+ } else if (status.turboStrategy === undefined && status.turboMode === true) {
49875
+ lines.push("");
49876
+ lines.push("**TURBO MODE**: active");
48482
49877
  }
48483
49878
  if (status.contextBudgetPct !== null && status.contextBudgetPct > 0) {
48484
49879
  const pct = status.contextBudgetPct.toFixed(1);
@@ -48502,13 +49897,20 @@ async function handleStatusCommand(directory, agents) {
48502
49897
  }
48503
49898
  return formatStatusMarkdown(statusData);
48504
49899
  }
49900
+ var _internals20;
48505
49901
  var init_status_service = __esm(() => {
48506
49902
  init_extractors();
48507
49903
  init_utils2();
48508
49904
  init_manager();
48509
49905
  init_state();
49906
+ init_state3();
48510
49907
  init_compaction_service();
48511
49908
  init_context_budget_service();
49909
+ _internals20 = {
49910
+ loadLeanTurboRunState,
49911
+ hasActiveLeanTurbo,
49912
+ hasActiveFullAuto
49913
+ };
48512
49914
  });
48513
49915
 
48514
49916
  // src/commands/status.ts
@@ -48544,7 +49946,7 @@ var init_sync_plan = __esm(() => {
48544
49946
  });
48545
49947
 
48546
49948
  // src/commands/turbo.ts
48547
- async function handleTurboCommand(_directory, args, sessionID) {
49949
+ async function handleTurboCommand(directory, args, sessionID) {
48548
49950
  if (!sessionID || sessionID.trim() === "") {
48549
49951
  return "Error: No active session context. Turbo Mode requires an active session. Use /swarm turbo from within an OpenCode session, or start a session first.";
48550
49952
  }
@@ -48552,24 +49954,179 @@ async function handleTurboCommand(_directory, args, sessionID) {
48552
49954
  if (!session) {
48553
49955
  return "Error: No active session. Turbo Mode requires an active session to operate.";
48554
49956
  }
48555
- const arg = args[0]?.toLowerCase();
48556
- let newTurboMode;
48557
- let feedback;
48558
- if (arg === "on") {
48559
- newTurboMode = true;
48560
- feedback = "Turbo Mode enabled";
48561
- } else if (arg === "off") {
48562
- newTurboMode = false;
48563
- feedback = "Turbo Mode disabled";
49957
+ const arg0 = args[0]?.toLowerCase();
49958
+ const arg1 = args[1]?.toLowerCase();
49959
+ if (arg0 === "status") {
49960
+ return buildStatusMessage(session, directory, sessionID);
49961
+ }
49962
+ const isTurboOn = session.turboMode;
49963
+ const isLeanActive = session.leanTurboActive === true;
49964
+ const disableTurbo = (reason) => {
49965
+ if (isLeanActive) {
49966
+ try {
49967
+ pauseLeanTurboRun(directory, sessionID, reason);
49968
+ } catch (error93) {
49969
+ error(`[turbo] pauseLeanTurboRun failed: ${error93 instanceof Error ? error93.message : String(error93)}`);
49970
+ }
49971
+ }
49972
+ session.turboMode = false;
49973
+ session.turboStrategy = undefined;
49974
+ session.leanTurboActive = false;
49975
+ session.leanTurboCurrentPhase = undefined;
49976
+ };
49977
+ if (arg0 === "off" || arg0 === "lean" && arg1 === "off") {
49978
+ disableTurbo("/swarm turbo off");
49979
+ return "Turbo Mode disabled";
49980
+ }
49981
+ if (arg0 === "standard" && arg1 === "off") {
49982
+ disableTurbo("/swarm turbo standard off");
49983
+ return "Turbo Mode disabled";
49984
+ }
49985
+ if (arg0 === undefined) {
49986
+ if (isTurboOn) {
49987
+ disableTurbo("/swarm turbo (toggle off)");
49988
+ return "Turbo Mode disabled";
49989
+ } else {
49990
+ session.turboMode = true;
49991
+ session.turboStrategy = "standard";
49992
+ session.leanTurboActive = false;
49993
+ session.leanTurboCurrentPhase = undefined;
49994
+ return "Turbo Mode enabled";
49995
+ }
49996
+ }
49997
+ if (arg0 === "on") {
49998
+ let strategy = "standard";
49999
+ try {
50000
+ const { config: config3 } = loadPluginConfigWithMeta(directory);
50001
+ if (config3.turbo?.strategy === "lean") {
50002
+ strategy = "lean";
50003
+ }
50004
+ } catch (error93) {
50005
+ warn(`[turbo] could not read config for strategy default: ${error93 instanceof Error ? error93.message : String(error93)}`);
50006
+ }
50007
+ if (strategy === "lean") {
50008
+ return enableLeanTurbo(session, directory, sessionID);
50009
+ }
50010
+ if (isLeanActive) {
50011
+ disableTurbo("/swarm turbo on (switching from lean)");
50012
+ }
50013
+ session.turboMode = true;
50014
+ session.turboStrategy = "standard";
50015
+ session.leanTurboActive = false;
50016
+ session.leanTurboCurrentPhase = undefined;
50017
+ return "Turbo Mode enabled";
50018
+ }
50019
+ if (arg0 === "standard" && arg1 === "on") {
50020
+ if (isLeanActive) {
50021
+ disableTurbo("/swarm turbo standard on (switching from lean)");
50022
+ }
50023
+ session.turboMode = true;
50024
+ session.turboStrategy = "standard";
50025
+ session.leanTurboActive = false;
50026
+ session.leanTurboCurrentPhase = undefined;
50027
+ return "Turbo Mode enabled (standard)";
50028
+ }
50029
+ if (arg0 === "lean" && arg1 === "on") {
50030
+ return enableLeanTurbo(session, directory, sessionID);
50031
+ }
50032
+ if (arg0 === "lean" && arg1 === undefined) {
50033
+ if (isLeanActive) {
50034
+ disableTurbo("/swarm turbo lean (toggle off)");
50035
+ return "Turbo Mode disabled";
50036
+ } else {
50037
+ return enableLeanTurbo(session, directory, sessionID);
50038
+ }
50039
+ }
50040
+ if (isTurboOn) {
50041
+ disableTurbo("/swarm turbo (toggle off via unknown arg)");
50042
+ return "Turbo Mode disabled";
48564
50043
  } else {
48565
- newTurboMode = !session.turboMode;
48566
- feedback = newTurboMode ? "Turbo Mode enabled" : "Turbo Mode disabled";
50044
+ session.turboMode = true;
50045
+ session.turboStrategy = "standard";
50046
+ session.leanTurboActive = false;
50047
+ session.leanTurboCurrentPhase = undefined;
50048
+ return "Turbo Mode enabled";
48567
50049
  }
48568
- session.turboMode = newTurboMode;
48569
- return feedback;
50050
+ }
50051
+ function enableLeanTurbo(session, directory, sessionID) {
50052
+ let maxParallelCoders = 4;
50053
+ let conflictPolicy = "serialize";
50054
+ try {
50055
+ const { config: config3 } = loadPluginConfigWithMeta(directory);
50056
+ const leanConfig = config3.turbo?.lean;
50057
+ if (leanConfig) {
50058
+ maxParallelCoders = leanConfig.max_parallel_coders ?? 4;
50059
+ conflictPolicy = leanConfig.conflict_policy ?? "serialize";
50060
+ }
50061
+ } catch (error93) {
50062
+ warn(`[turbo] could not read lean config: ${error93 instanceof Error ? error93.message : String(error93)}`);
50063
+ }
50064
+ let durableError;
50065
+ try {
50066
+ const state = emptyRunState(sessionID, maxParallelCoders);
50067
+ state.status = "running";
50068
+ saveLeanTurboRunState(directory, state);
50069
+ } catch (error93) {
50070
+ durableError = error93 instanceof Error ? error93.message : String(error93);
50071
+ error(`[turbo] durable run-state write failed: ${durableError}`);
50072
+ }
50073
+ if (durableError) {
50074
+ return [
50075
+ "Error: Lean Turbo could NOT be enabled \u2014 durable run-state write failed.",
50076
+ `Reason: ${durableError}.`,
50077
+ "Inspect .swarm/ permissions and disk space, then retry."
50078
+ ].join(" ");
50079
+ }
50080
+ const fullAutoActive = session.fullAutoMode;
50081
+ session.turboMode = true;
50082
+ session.turboStrategy = "lean";
50083
+ session.leanTurboActive = true;
50084
+ session.leanTurboCurrentPhase = undefined;
50085
+ return [
50086
+ "Lean Turbo enabled",
50087
+ `(maxParallelCoders=${maxParallelCoders}, conflict_policy=${conflictPolicy},`,
50088
+ `Full-Auto: ${fullAutoActive ? "active" : "inactive"})`
50089
+ ].join(" ");
50090
+ }
50091
+ function buildStatusMessage(session, directory, sessionID) {
50092
+ if (!session.turboMode) {
50093
+ return "Turbo: off";
50094
+ }
50095
+ if (session.turboStrategy === "standard" || !session.leanTurboActive) {
50096
+ return "Turbo: standard (turboMode=true)";
50097
+ }
50098
+ if (isStateUnreadable(directory)) {
50099
+ return [
50100
+ "Turbo: lean (turboMode=true, leanTurboActive=true)",
50101
+ "WARNING: Durable state is unreadable \u2014 cannot report full status."
50102
+ ].join(`
50103
+ `);
50104
+ }
50105
+ const state = loadLeanTurboRunState(directory, sessionID);
50106
+ if (!state) {
50107
+ return [
50108
+ "Turbo: lean (turboMode=true, leanTurboActive=true)",
50109
+ "WARNING: Durable state not found."
50110
+ ].join(`
50111
+ `);
50112
+ }
50113
+ const phase = state.phase !== undefined ? `phase=${state.phase}` : "phase=unset";
50114
+ const laneCount = state.lanes.length;
50115
+ const degradedCount = state.degradedTasks.length;
50116
+ const maxParallel = state.maxParallelCoders;
50117
+ const fullAutoActive = session.fullAutoMode;
50118
+ return [
50119
+ `Turbo: lean (turboMode=true, leanTurboActive=true)`,
50120
+ `Status: ${state.status}, ${phase}, lanes=${laneCount}, degraded=${degradedCount}`,
50121
+ `maxParallelCoders=${maxParallel}, Full-Auto: ${fullAutoActive ? "active" : "inactive"}`
50122
+ ].join(`
50123
+ `);
48570
50124
  }
48571
50125
  var init_turbo = __esm(() => {
50126
+ init_config();
48572
50127
  init_state();
50128
+ init_state3();
50129
+ init_logger();
48573
50130
  });
48574
50131
 
48575
50132
  // src/commands/write-retro.ts
@@ -48693,8 +50250,8 @@ __export(exports_commands, {
48693
50250
  COMMAND_NAME_SET: () => COMMAND_NAME_SET,
48694
50251
  COMMAND_NAMES: () => COMMAND_NAMES
48695
50252
  });
48696
- import fs22 from "fs";
48697
- import path37 from "path";
50253
+ import fs23 from "fs";
50254
+ import path41 from "path";
48698
50255
  function buildHelpText() {
48699
50256
  const lines = ["## Swarm Commands", ""];
48700
50257
  const CATEGORIES = [
@@ -48797,11 +50354,11 @@ function createSwarmCommandHandler(directory, agents) {
48797
50354
  return;
48798
50355
  }
48799
50356
  let isFirstRun = false;
48800
- const sentinelPath = path37.join(directory, ".swarm", ".first-run-complete");
50357
+ const sentinelPath = path41.join(directory, ".swarm", ".first-run-complete");
48801
50358
  try {
48802
- const swarmDir = path37.join(directory, ".swarm");
48803
- fs22.mkdirSync(swarmDir, { recursive: true });
48804
- fs22.writeFileSync(sentinelPath, `first-run-complete: ${new Date().toISOString()}
50359
+ const swarmDir = path41.join(directory, ".swarm");
50360
+ fs23.mkdirSync(swarmDir, { recursive: true });
50361
+ fs23.writeFileSync(sentinelPath, `first-run-complete: ${new Date().toISOString()}
48805
50362
  `, { flag: "wx" });
48806
50363
  isFirstRun = true;
48807
50364
  } catch (_err) {}
@@ -48822,7 +50379,7 @@ function createSwarmCommandHandler(directory, agents) {
48822
50379
  const attemptedCommand = tokens[0] || "";
48823
50380
  const MAX_DISPLAY = 100;
48824
50381
  const displayCommand = attemptedCommand.length > MAX_DISPLAY ? `${attemptedCommand.slice(0, MAX_DISPLAY)}...` : attemptedCommand;
48825
- const similar = _internals19.findSimilarCommands(attemptedCommand);
50382
+ const similar = _internals21.findSimilarCommands(attemptedCommand);
48826
50383
  const header = `Command \`/swarm ${displayCommand}\` not found.`;
48827
50384
  const suggestions = similar.length > 0 ? `Did you mean:
48828
50385
  ${similar.map((cmd) => ` \u2022 /swarm ${cmd}`).join(`
@@ -48929,7 +50486,7 @@ function findSimilarCommands(query) {
48929
50486
  }
48930
50487
  const scored = VALID_COMMANDS.map((cmd) => {
48931
50488
  const cmdLower = cmd.toLowerCase();
48932
- const fullScore = _internals19.levenshteinDistance(q, cmdLower);
50489
+ const fullScore = _internals21.levenshteinDistance(q, cmdLower);
48933
50490
  let tokenScore = Infinity;
48934
50491
  if (cmd.includes(" ") || cmd.includes("-")) {
48935
50492
  const qTokens = q.split(/[\s-]+/);
@@ -48942,7 +50499,7 @@ function findSimilarCommands(query) {
48942
50499
  for (const ct of cmdTokens) {
48943
50500
  if (ct.length === 0)
48944
50501
  continue;
48945
- const dist = _internals19.levenshteinDistance(qt, ct);
50502
+ const dist = _internals21.levenshteinDistance(qt, ct);
48946
50503
  if (dist < minDist)
48947
50504
  minDist = dist;
48948
50505
  }
@@ -48952,7 +50509,7 @@ function findSimilarCommands(query) {
48952
50509
  }
48953
50510
  const dashStrippedQ = q.replace(/-/g, "");
48954
50511
  const dashStrippedCmd = cmdLower.replace(/-/g, "");
48955
- const dashScore = _internals19.levenshteinDistance(dashStrippedQ, dashStrippedCmd);
50512
+ const dashScore = _internals21.levenshteinDistance(dashStrippedQ, dashStrippedCmd);
48956
50513
  const score = Math.min(fullScore, tokenScore, dashScore);
48957
50514
  return { cmd, score };
48958
50515
  });
@@ -48984,11 +50541,11 @@ async function handleHelpCommand(ctx) {
48984
50541
  return buildHelpText2();
48985
50542
  }
48986
50543
  const tokens = targetCommand.split(/\s+/);
48987
- const resolved = _internals19.resolveCommand(tokens);
50544
+ const resolved = _internals21.resolveCommand(tokens);
48988
50545
  if (resolved) {
48989
- return _internals19.buildDetailedHelp(resolved.key, resolved.entry);
50546
+ return _internals21.buildDetailedHelp(resolved.key, resolved.entry);
48990
50547
  }
48991
- const similar = _internals19.findSimilarCommands(targetCommand);
50548
+ const similar = _internals21.findSimilarCommands(targetCommand);
48992
50549
  const { buildHelpText: fullHelp } = await Promise.resolve().then(() => (init_commands(), exports_commands));
48993
50550
  if (similar.length > 0) {
48994
50551
  return `Command '/swarm ${targetCommand}' not found.
@@ -49024,24 +50581,24 @@ function validateAliases() {
49024
50581
  }
49025
50582
  aliasTargets.get(target).push(name);
49026
50583
  const visited = new Set;
49027
- const path38 = [];
50584
+ const path42 = [];
49028
50585
  let current = target;
49029
50586
  while (current) {
49030
50587
  const currentEntry = COMMAND_REGISTRY[current];
49031
50588
  if (!currentEntry)
49032
50589
  break;
49033
50590
  if (visited.has(current)) {
49034
- const cycleStart = path38.indexOf(current);
50591
+ const cycleStart = path42.indexOf(current);
49035
50592
  const fullChain = [
49036
50593
  name,
49037
- ...path38.slice(0, cycleStart > 0 ? cycleStart : path38.length),
50594
+ ...path42.slice(0, cycleStart > 0 ? cycleStart : path42.length),
49038
50595
  current
49039
50596
  ].join(" \u2192 ");
49040
50597
  errors5.push(`Circular alias detected: ${fullChain}`);
49041
50598
  break;
49042
50599
  }
49043
50600
  visited.add(current);
49044
- path38.push(current);
50601
+ path42.push(current);
49045
50602
  current = currentEntry.aliasOf || "";
49046
50603
  }
49047
50604
  }
@@ -49082,7 +50639,7 @@ function resolveCommand(tokens) {
49082
50639
  }
49083
50640
  return null;
49084
50641
  }
49085
- var COMMAND_REGISTRY, VALID_COMMANDS, _internals19, validation;
50642
+ var COMMAND_REGISTRY, VALID_COMMANDS, _internals21, validation;
49086
50643
  var init_registry = __esm(() => {
49087
50644
  init_acknowledge_spec_drift();
49088
50645
  init_agents();
@@ -49152,7 +50709,7 @@ var init_registry = __esm(() => {
49152
50709
  clashesWithNativeCcCommand: "/agents"
49153
50710
  },
49154
50711
  help: {
49155
- handler: (ctx) => _internals19.handleHelpCommand(ctx),
50712
+ handler: (ctx) => _internals21.handleHelpCommand(ctx),
49156
50713
  description: "Show help for swarm commands",
49157
50714
  category: "core",
49158
50715
  args: "[command]",
@@ -49314,17 +50871,21 @@ var init_registry = __esm(() => {
49314
50871
  category: "diagnostics"
49315
50872
  },
49316
50873
  finalize: {
49317
- handler: (ctx) => handleCloseCommand(ctx.directory, ctx.args),
50874
+ handler: (ctx) => handleCloseCommand(ctx.directory, ctx.args, {
50875
+ sessionID: ctx.sessionID
50876
+ }),
49318
50877
  description: "Use /swarm finalize to finalize the swarm project and archive evidence",
49319
- details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs safe git ff-only to main. Resets agent sessions and delegation chains. Reads .swarm/close-lessons.md for explicit lessons and runs curation.",
49320
- args: "--prune-branches",
50878
+ details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs safe git ff-only to main. Resets agent sessions and delegation chains. Reads .swarm/close-lessons.md for explicit lessons and runs curation. Use --skill-review to run the quota-bounded skill_improver in proposal mode.",
50879
+ args: "--prune-branches, --skill-review",
49321
50880
  category: "core"
49322
50881
  },
49323
50882
  close: {
49324
- handler: (ctx) => handleCloseCommand(ctx.directory, ctx.args),
50883
+ handler: (ctx) => handleCloseCommand(ctx.directory, ctx.args, {
50884
+ sessionID: ctx.sessionID
50885
+ }),
49325
50886
  description: "Use /swarm close (deprecated alias) to finalize and archive swarm state",
49326
50887
  details: "Deprecated alias for /swarm finalize. Preserved for backward compatibility.",
49327
- args: "--prune-branches",
50888
+ args: "--prune-branches, --skill-review",
49328
50889
  category: "core",
49329
50890
  aliasOf: "finalize",
49330
50891
  deprecated: true
@@ -49505,7 +51066,7 @@ var init_registry = __esm(() => {
49505
51066
  }
49506
51067
  };
49507
51068
  VALID_COMMANDS = Object.keys(COMMAND_REGISTRY);
49508
- _internals19 = {
51069
+ _internals21 = {
49509
51070
  handleHelpCommand,
49510
51071
  validateAliases,
49511
51072
  resolveCommand,
@@ -49513,7 +51074,7 @@ var init_registry = __esm(() => {
49513
51074
  findSimilarCommands,
49514
51075
  buildDetailedHelp
49515
51076
  };
49516
- validation = _internals19.validateAliases();
51077
+ validation = _internals21.validateAliases();
49517
51078
  if (!validation.valid) {
49518
51079
  throw new Error(`COMMAND_REGISTRY alias validation failed:
49519
51080
  ${validation.errors.join(`
@@ -49531,68 +51092,68 @@ init_package();
49531
51092
  init_registry();
49532
51093
  init_cache_paths();
49533
51094
  init_constants();
49534
- import * as fs23 from "fs";
51095
+ import * as fs24 from "fs";
49535
51096
  import * as os7 from "os";
49536
- import * as path38 from "path";
51097
+ import * as path42 from "path";
49537
51098
  var { version: version4 } = package_default;
49538
51099
  var CONFIG_DIR = getPluginConfigDir();
49539
- var OPENCODE_CONFIG_PATH = path38.join(CONFIG_DIR, "opencode.json");
49540
- var PLUGIN_CONFIG_PATH = path38.join(CONFIG_DIR, "opencode-swarm.json");
49541
- var PROMPTS_DIR = path38.join(CONFIG_DIR, "opencode-swarm");
51100
+ var OPENCODE_CONFIG_PATH = path42.join(CONFIG_DIR, "opencode.json");
51101
+ var PLUGIN_CONFIG_PATH = path42.join(CONFIG_DIR, "opencode-swarm.json");
51102
+ var PROMPTS_DIR = path42.join(CONFIG_DIR, "opencode-swarm");
49542
51103
  var OPENCODE_PLUGIN_CACHE_PATHS = getPluginCachePaths();
49543
51104
  var OPENCODE_PLUGIN_LOCK_FILE_PATHS = getPluginLockFilePaths();
49544
51105
  function isSafeCachePath(p) {
49545
- const resolved = path38.resolve(p);
49546
- const home = path38.resolve(os7.homedir());
51106
+ const resolved = path42.resolve(p);
51107
+ const home = path42.resolve(os7.homedir());
49547
51108
  if (resolved === "/" || resolved === home || resolved.length <= home.length) {
49548
51109
  return false;
49549
51110
  }
49550
- const segments = resolved.split(path38.sep).filter((s) => s.length > 0);
51111
+ const segments = resolved.split(path42.sep).filter((s) => s.length > 0);
49551
51112
  if (segments.length < 4) {
49552
51113
  return false;
49553
51114
  }
49554
- const leaf = path38.basename(resolved);
51115
+ const leaf = path42.basename(resolved);
49555
51116
  if (leaf !== "opencode-swarm@latest" && leaf !== "opencode-swarm") {
49556
51117
  return false;
49557
51118
  }
49558
- const parent = path38.basename(path38.dirname(resolved));
51119
+ const parent = path42.basename(path42.dirname(resolved));
49559
51120
  if (parent !== "packages" && parent !== "node_modules") {
49560
51121
  return false;
49561
51122
  }
49562
- const grandparent = path38.basename(path38.dirname(path38.dirname(resolved)));
51123
+ const grandparent = path42.basename(path42.dirname(path42.dirname(resolved)));
49563
51124
  if (grandparent !== "opencode") {
49564
51125
  return false;
49565
51126
  }
49566
51127
  return true;
49567
51128
  }
49568
51129
  function isSafeLockFilePath(p) {
49569
- const resolved = path38.resolve(p);
49570
- const home = path38.resolve(os7.homedir());
51130
+ const resolved = path42.resolve(p);
51131
+ const home = path42.resolve(os7.homedir());
49571
51132
  if (resolved === "/" || resolved === home || resolved.length <= home.length) {
49572
51133
  return false;
49573
51134
  }
49574
- const segments = resolved.split(path38.sep).filter((s) => s.length > 0);
51135
+ const segments = resolved.split(path42.sep).filter((s) => s.length > 0);
49575
51136
  if (segments.length < 4) {
49576
51137
  return false;
49577
51138
  }
49578
- const leaf = path38.basename(resolved);
51139
+ const leaf = path42.basename(resolved);
49579
51140
  if (leaf !== "bun.lock" && leaf !== "bun.lockb" && leaf !== "package-lock.json") {
49580
51141
  return false;
49581
51142
  }
49582
- const parent = path38.basename(path38.dirname(resolved));
51143
+ const parent = path42.basename(path42.dirname(resolved));
49583
51144
  if (parent !== "opencode") {
49584
51145
  return false;
49585
51146
  }
49586
51147
  return true;
49587
51148
  }
49588
51149
  function ensureDir(dir) {
49589
- if (!fs23.existsSync(dir)) {
49590
- fs23.mkdirSync(dir, { recursive: true });
51150
+ if (!fs24.existsSync(dir)) {
51151
+ fs24.mkdirSync(dir, { recursive: true });
49591
51152
  }
49592
51153
  }
49593
51154
  function loadJson(filepath) {
49594
51155
  try {
49595
- const content = fs23.readFileSync(filepath, "utf-8");
51156
+ const content = fs24.readFileSync(filepath, "utf-8");
49596
51157
  const stripped = content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (match, comment) => comment ? "" : match).replace(/,(\s*[}\]])/g, "$1");
49597
51158
  return JSON.parse(stripped);
49598
51159
  } catch {
@@ -49600,14 +51161,14 @@ function loadJson(filepath) {
49600
51161
  }
49601
51162
  }
49602
51163
  function saveJson(filepath, data) {
49603
- fs23.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
51164
+ fs24.writeFileSync(filepath, `${JSON.stringify(data, null, 2)}
49604
51165
  `, "utf-8");
49605
51166
  }
49606
51167
  function writeProjectConfigIfMissing(cwd) {
49607
51168
  try {
49608
- const opencodeDir = path38.join(cwd, ".opencode");
49609
- const projectConfigPath = path38.join(opencodeDir, "opencode-swarm.json");
49610
- if (fs23.existsSync(projectConfigPath)) {
51169
+ const opencodeDir = path42.join(cwd, ".opencode");
51170
+ const projectConfigPath = path42.join(opencodeDir, "opencode-swarm.json");
51171
+ if (fs24.existsSync(projectConfigPath)) {
49611
51172
  return;
49612
51173
  }
49613
51174
  ensureDir(opencodeDir);
@@ -49623,7 +51184,7 @@ async function install() {
49623
51184
  `);
49624
51185
  ensureDir(CONFIG_DIR);
49625
51186
  ensureDir(PROMPTS_DIR);
49626
- const LEGACY_CONFIG_PATH = path38.join(CONFIG_DIR, "config.json");
51187
+ const LEGACY_CONFIG_PATH = path42.join(CONFIG_DIR, "config.json");
49627
51188
  let opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
49628
51189
  if (!opencodeConfig) {
49629
51190
  const legacyConfig = loadJson(LEGACY_CONFIG_PATH);
@@ -49670,7 +51231,7 @@ async function install() {
49670
51231
  console.warn(`\u26A0 Could not clear opencode lock file \u2014 you may need to delete it manually:
49671
51232
  ${failed}`);
49672
51233
  }
49673
- if (!fs23.existsSync(PLUGIN_CONFIG_PATH)) {
51234
+ if (!fs24.existsSync(PLUGIN_CONFIG_PATH)) {
49674
51235
  const defaultConfig = {
49675
51236
  agents: { ...DEFAULT_AGENT_CONFIGS },
49676
51237
  max_iterations: 5
@@ -49749,14 +51310,14 @@ function evictPluginCaches() {
49749
51310
  const cleared = [];
49750
51311
  const failed = [];
49751
51312
  for (const cachePath of OPENCODE_PLUGIN_CACHE_PATHS) {
49752
- if (!fs23.existsSync(cachePath))
51313
+ if (!fs24.existsSync(cachePath))
49753
51314
  continue;
49754
51315
  if (!isSafeCachePath(cachePath)) {
49755
51316
  failed.push(`${cachePath} (refused: failed safety check)`);
49756
51317
  continue;
49757
51318
  }
49758
51319
  try {
49759
- fs23.rmSync(cachePath, { recursive: true, force: true });
51320
+ fs24.rmSync(cachePath, { recursive: true, force: true });
49760
51321
  cleared.push(cachePath);
49761
51322
  } catch (err) {
49762
51323
  failed.push(`${cachePath} (${err instanceof Error ? err.message : String(err)})`);
@@ -49768,14 +51329,14 @@ function evictLockFiles() {
49768
51329
  const cleared = [];
49769
51330
  const failed = [];
49770
51331
  for (const lockPath of OPENCODE_PLUGIN_LOCK_FILE_PATHS) {
49771
- if (!fs23.existsSync(lockPath))
51332
+ if (!fs24.existsSync(lockPath))
49772
51333
  continue;
49773
51334
  if (!isSafeLockFilePath(lockPath)) {
49774
51335
  failed.push(`${lockPath} (refused: failed safety check)`);
49775
51336
  continue;
49776
51337
  }
49777
51338
  try {
49778
- fs23.unlinkSync(lockPath);
51339
+ fs24.unlinkSync(lockPath);
49779
51340
  cleared.push(lockPath);
49780
51341
  } catch (err) {
49781
51342
  const code = err?.code;
@@ -49794,7 +51355,7 @@ async function uninstall() {
49794
51355
  `);
49795
51356
  const opencodeConfig = loadJson(OPENCODE_CONFIG_PATH);
49796
51357
  if (!opencodeConfig) {
49797
- if (fs23.existsSync(OPENCODE_CONFIG_PATH)) {
51358
+ if (fs24.existsSync(OPENCODE_CONFIG_PATH)) {
49798
51359
  console.log(`\u2717 Could not parse opencode config at: ${OPENCODE_CONFIG_PATH}`);
49799
51360
  return 1;
49800
51361
  } else {
@@ -49826,13 +51387,13 @@ async function uninstall() {
49826
51387
  console.log("\u2713 Re-enabled default OpenCode agents (explore, general)");
49827
51388
  if (process.argv.includes("--clean")) {
49828
51389
  let cleaned = false;
49829
- if (fs23.existsSync(PLUGIN_CONFIG_PATH)) {
49830
- fs23.unlinkSync(PLUGIN_CONFIG_PATH);
51390
+ if (fs24.existsSync(PLUGIN_CONFIG_PATH)) {
51391
+ fs24.unlinkSync(PLUGIN_CONFIG_PATH);
49831
51392
  console.log(`\u2713 Removed plugin config: ${PLUGIN_CONFIG_PATH}`);
49832
51393
  cleaned = true;
49833
51394
  }
49834
- if (fs23.existsSync(PROMPTS_DIR)) {
49835
- fs23.rmSync(PROMPTS_DIR, { recursive: true });
51395
+ if (fs24.existsSync(PROMPTS_DIR)) {
51396
+ fs24.rmSync(PROMPTS_DIR, { recursive: true });
49836
51397
  console.log(`\u2713 Removed custom prompts: ${PROMPTS_DIR}`);
49837
51398
  cleaned = true;
49838
51399
  }