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.
- package/README.md +4 -3
- package/dist/cli/index.js +2114 -553
- package/dist/commands/close.d.ts +14 -1
- package/dist/commands/registry.d.ts +3 -3
- package/dist/commands/turbo.d.ts +4 -4
- package/dist/config/constants.d.ts +12 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/schema.d.ts +116 -0
- package/dist/index.js +5288 -2128
- package/dist/parallel/file-locks.d.ts +50 -2
- package/dist/services/skill-improver.d.ts +1 -0
- package/dist/services/status-service.d.ts +29 -0
- package/dist/state.d.ts +17 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/lean-turbo-acquire-locks.d.ts +36 -0
- package/dist/tools/lean-turbo-plan-lanes.d.ts +35 -0
- package/dist/tools/lean-turbo-review.d.ts +34 -0
- package/dist/tools/lean-turbo-run-phase.d.ts +44 -0
- package/dist/tools/lean-turbo-runner-status.d.ts +36 -0
- package/dist/tools/lean-turbo-status.d.ts +44 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/dist/tools/update-task-status.d.ts +7 -4
- package/dist/turbo/lean/conflicts.d.ts +100 -0
- package/dist/turbo/lean/evidence.d.ts +91 -0
- package/dist/turbo/lean/index.d.ts +27 -0
- package/dist/turbo/lean/integration.d.ts +137 -0
- package/dist/turbo/lean/phase-ready.d.ts +105 -0
- package/dist/turbo/lean/planner.d.ts +115 -0
- package/dist/turbo/lean/reviewer.d.ts +124 -0
- package/dist/turbo/lean/risk.d.ts +47 -0
- package/dist/turbo/lean/runner.d.ts +322 -0
- package/dist/turbo/lean/state.d.ts +61 -0
- package/dist/turbo/lean/task-completion.d.ts +53 -0
- 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.
|
|
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
|
|
19376
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
20204
|
+
var _internals6, DEFAULT_QA_GATES;
|
|
20079
20205
|
var init_qa_gate_profile = __esm(() => {
|
|
20080
20206
|
init_project_db();
|
|
20081
|
-
|
|
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
|
|
21444
|
-
return Promise.all(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
34778
|
+
_internals7.gitExec(["rev-parse", "--abbrev-ref", `${currentBranch}@{upstream}`], cwd);
|
|
34629
34779
|
} catch {
|
|
34630
34780
|
try {
|
|
34631
|
-
const localSha =
|
|
34632
|
-
const remoteSha =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
34932
|
+
var GIT_TIMEOUT_MS2 = 30000, _internals7;
|
|
34783
34933
|
var init_branch = __esm(() => {
|
|
34784
34934
|
init_logger();
|
|
34785
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
37578
|
+
const swarmDir = path17.join(directory, ".swarm");
|
|
36480
37579
|
let planExists = false;
|
|
36481
37580
|
let planData = {
|
|
36482
|
-
title:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
36684
|
-
const destPath =
|
|
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 =
|
|
36695
|
-
const destDir =
|
|
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 =
|
|
36702
|
-
const destEntry =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
36779
|
-
|
|
36780
|
-
|
|
36781
|
-
|
|
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 ${
|
|
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 =
|
|
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
|
|
38153
|
+
import * as path18 from "path";
|
|
36999
38154
|
function getUserConfigDir2() {
|
|
37000
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
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 =
|
|
37005
|
-
const projectConfigPath =
|
|
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
|
|
37132
|
-
import * as
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
37265
|
-
const resolvedPath =
|
|
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 =
|
|
37291
|
-
const relTarget =
|
|
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 =
|
|
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 !==
|
|
38468
|
+
return baseA === baseB && baseA !== path19.basename(fileA) && baseA !== path19.basename(fileB);
|
|
37314
38469
|
}
|
|
37315
38470
|
function hasSharedPrefix(fileA, fileB) {
|
|
37316
|
-
const dirA =
|
|
37317
|
-
const dirB =
|
|
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 =
|
|
37322
|
-
const baseB =
|
|
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
|
|
37351
|
-
const matrix =
|
|
37352
|
-
const staticEdges = await
|
|
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 =
|
|
37377
|
-
const baseB =
|
|
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,
|
|
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
|
|
37465
|
-
return
|
|
38619
|
+
const pairs = await _internals10.detectDarkMatter(directory, options);
|
|
38620
|
+
return _internals10.formatDarkMatterOutput(pairs);
|
|
37466
38621
|
}
|
|
37467
38622
|
});
|
|
37468
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
38803
|
+
import * as path21 from "path";
|
|
37649
38804
|
function getPluginConfigDir() {
|
|
37650
|
-
return
|
|
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 ||
|
|
38808
|
+
const cacheBase = process.env.XDG_CACHE_HOME || path21.join(os5.homedir(), ".cache");
|
|
37654
38809
|
const configDir = getPluginConfigDir();
|
|
37655
38810
|
const paths = [
|
|
37656
|
-
|
|
37657
|
-
|
|
37658
|
-
|
|
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 =
|
|
37662
|
-
paths.push(
|
|
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 ||
|
|
37666
|
-
const appData = process.env.APPDATA ||
|
|
37667
|
-
paths.push(
|
|
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 ||
|
|
38827
|
+
const cacheBase = process.env.XDG_CACHE_HOME || path21.join(os5.homedir(), ".cache");
|
|
37673
38828
|
const configDir = getPluginConfigDir();
|
|
37674
38829
|
const paths = [
|
|
37675
|
-
|
|
37676
|
-
|
|
37677
|
-
|
|
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 =
|
|
37681
|
-
paths.push(
|
|
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 ||
|
|
37685
|
-
paths.push(
|
|
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
|
|
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
|
|
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 :
|
|
37698
|
-
return
|
|
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
|
|
38856
|
+
return join20(cacheDir(), "version-check.json");
|
|
37702
38857
|
}
|
|
37703
38858
|
function readVersionCache() {
|
|
37704
38859
|
try {
|
|
37705
|
-
const
|
|
37706
|
-
if (!
|
|
38860
|
+
const path22 = cacheFile();
|
|
38861
|
+
if (!existsSync12(path22))
|
|
37707
38862
|
return null;
|
|
37708
|
-
const raw =
|
|
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
|
|
37748
|
-
import
|
|
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 (!
|
|
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 =
|
|
38046
|
-
if (!
|
|
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 =
|
|
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 ?
|
|
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 =
|
|
39253
|
+
const thisDir = path22.dirname(fileURLToPath(import.meta.url));
|
|
38099
39254
|
const grammarDir = resolveGrammarDir(thisDir);
|
|
38100
39255
|
const missing = [];
|
|
38101
|
-
if (!
|
|
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 (!
|
|
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 =
|
|
38124
|
-
if (!
|
|
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 =
|
|
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 =
|
|
38176
|
-
if (!
|
|
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 =
|
|
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 =
|
|
38217
|
-
if (!
|
|
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 =
|
|
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 =
|
|
38273
|
-
if (!
|
|
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 =
|
|
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 =
|
|
38439
|
-
const snapshotFiles =
|
|
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 (!
|
|
39627
|
+
if (!existsSync13(cachePath)) {
|
|
38473
39628
|
cacheRows.push(`\u2B1C ${cachePath} \u2014 absent`);
|
|
38474
39629
|
continue;
|
|
38475
39630
|
}
|
|
38476
|
-
const pkgJsonPath =
|
|
39631
|
+
const pkgJsonPath = path22.join(cachePath, "package.json");
|
|
38477
39632
|
try {
|
|
38478
|
-
const raw =
|
|
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
|
|
39719
|
+
import * as path23 from "path";
|
|
38565
39720
|
function getUserConfigDir3() {
|
|
38566
|
-
return process.env.XDG_CONFIG_HOME ||
|
|
39721
|
+
return process.env.XDG_CONFIG_HOME || path23.join(os6.homedir(), ".config");
|
|
38567
39722
|
}
|
|
38568
39723
|
function getConfigPaths(directory) {
|
|
38569
|
-
const userConfigPath =
|
|
38570
|
-
const projectConfigPath =
|
|
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 =
|
|
38596
|
-
const resolvedUser =
|
|
38597
|
-
const resolvedProject =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
39858
|
+
function validateConfigKey(path24, value, _config) {
|
|
38704
39859
|
const findings = [];
|
|
38705
|
-
switch (
|
|
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,
|
|
40107
|
+
function walkConfigAndValidate(obj, path24, config3, findings) {
|
|
38953
40108
|
if (obj === null || obj === undefined) {
|
|
38954
40109
|
return;
|
|
38955
40110
|
}
|
|
38956
|
-
if (
|
|
38957
|
-
const keyFindings = validateConfigKey(
|
|
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(
|
|
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, `${
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
41397
|
+
return path24.join(dir, matches[0]);
|
|
40243
41398
|
}
|
|
40244
41399
|
} catch {}
|
|
40245
41400
|
} else {
|
|
40246
|
-
const filePath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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 ??
|
|
40628
|
-
const indexPath =
|
|
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: () =>
|
|
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 =
|
|
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 =
|
|
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 =
|
|
40880
|
-
const evidenceCheck =
|
|
40881
|
-
const blockers =
|
|
40882
|
-
const entries =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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",
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
41560
|
-
|
|
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 (!
|
|
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 =
|
|
42972
|
+
const sessionState = _internals13.parseSessionState(sessionContent);
|
|
41818
42973
|
const plan = await loadPlanJsonOnly(directory);
|
|
41819
|
-
const planInfo =
|
|
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 =
|
|
41839
|
-
const rawPhaseMetrics =
|
|
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,
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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(() =>
|
|
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(() =>
|
|
43288
|
+
_writeInFlight = _writeInFlight.then(() => _internals14.writeSnapshot(directory, swarmState), () => _internals14.writeSnapshot(directory, swarmState));
|
|
42134
43289
|
await _writeInFlight;
|
|
42135
43290
|
}
|
|
42136
|
-
var _writeInFlight,
|
|
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
|
-
|
|
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
|
|
42565
|
-
import { mkdir as
|
|
42566
|
-
import * as
|
|
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 =
|
|
42578
|
-
const contextPath =
|
|
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 (
|
|
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 (!
|
|
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
|
|
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 =
|
|
43763
|
+
const rawEntries = _internals15.parseContextMd(contextContent);
|
|
42609
43764
|
if (rawEntries.length === 0) {
|
|
42610
|
-
await
|
|
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 =
|
|
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 ??
|
|
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:
|
|
42645
|
-
category: raw.categoryHint ??
|
|
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
|
|
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 =
|
|
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 =
|
|
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:
|
|
43858
|
+
text: _internals15.truncateLesson(bullet),
|
|
42704
43859
|
sourceSection: match.sourceSection,
|
|
42705
|
-
categoryHint:
|
|
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 =
|
|
42775
|
-
if (
|
|
43929
|
+
const packageJsonPath = path28.join(directory, "package.json");
|
|
43930
|
+
if (existsSync18(packageJsonPath)) {
|
|
42776
43931
|
try {
|
|
42777
|
-
const pkg = JSON.parse(
|
|
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
|
|
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
|
|
42796
|
-
await
|
|
43950
|
+
await mkdir8(path28.dirname(sentinelPath), { recursive: true });
|
|
43951
|
+
await writeFile9(sentinelPath, JSON.stringify(sentinel, null, 2), "utf-8");
|
|
42797
43952
|
}
|
|
42798
|
-
var
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
43329
|
-
const biomeBin = isWindows ?
|
|
43330
|
-
const eslintBin = isWindows ?
|
|
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(
|
|
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(
|
|
44535
|
+
if (fs12.existsSync(path29.join(cwd, "ruff.toml")))
|
|
43381
44536
|
return isCommandAvailable("ruff");
|
|
43382
44537
|
try {
|
|
43383
|
-
const pyproject =
|
|
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(
|
|
44548
|
+
return fs12.existsSync(path29.join(cwd, "Cargo.toml")) && isCommandAvailable("cargo");
|
|
43394
44549
|
}
|
|
43395
44550
|
function detectGolangciLint(cwd) {
|
|
43396
|
-
return fs12.existsSync(
|
|
44551
|
+
return fs12.existsSync(path29.join(cwd, "go.mod")) && isCommandAvailable("golangci-lint");
|
|
43397
44552
|
}
|
|
43398
44553
|
function detectCheckstyle(cwd) {
|
|
43399
|
-
const hasMaven = fs12.existsSync(
|
|
43400
|
-
const hasGradle = fs12.existsSync(
|
|
43401
|
-
const hasBinary = hasMaven && isCommandAvailable("mvn") || hasGradle && (fs12.existsSync(
|
|
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(
|
|
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(
|
|
44579
|
+
if (fs12.existsSync(path29.join(cwd, "CMakeLists.txt"))) {
|
|
43425
44580
|
return isCommandAvailable("cppcheck");
|
|
43426
44581
|
}
|
|
43427
44582
|
try {
|
|
43428
|
-
const dirsToCheck = [cwd,
|
|
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(
|
|
44597
|
+
return fs12.existsSync(path29.join(cwd, "Package.swift")) && isCommandAvailable("swiftlint");
|
|
43443
44598
|
}
|
|
43444
44599
|
function detectDartAnalyze(cwd) {
|
|
43445
|
-
return fs12.existsSync(
|
|
44600
|
+
return fs12.existsSync(path29.join(cwd, "pubspec.yaml")) && (isCommandAvailable("dart") || isCommandAvailable("flutter"));
|
|
43446
44601
|
}
|
|
43447
44602
|
function detectRubocop(cwd) {
|
|
43448
|
-
return (fs12.existsSync(
|
|
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 =
|
|
44631
|
+
const candidate = path29.join(dir, "node_modules", ".bin", binName);
|
|
43477
44632
|
if (fs12.existsSync(candidate))
|
|
43478
44633
|
return candidate;
|
|
43479
|
-
const parent =
|
|
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(
|
|
44643
|
+
for (const dir of searchPath.split(path29.delimiter)) {
|
|
43489
44644
|
if (!dir)
|
|
43490
44645
|
continue;
|
|
43491
|
-
const candidate =
|
|
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 ?
|
|
43505
|
-
const eslintBin = isWindows ?
|
|
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(
|
|
43510
|
-
const eslintAncestor = findBinInAncestors(
|
|
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,
|
|
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
|
|
44856
|
+
const linter = await _internals16.detectAvailableLinter(directory);
|
|
43702
44857
|
if (linter) {
|
|
43703
|
-
const result = await
|
|
44858
|
+
const result = await _internals16.runLint(linter, mode, directory);
|
|
43704
44859
|
return JSON.stringify(result, null, 2);
|
|
43705
44860
|
}
|
|
43706
|
-
const additionalLinter =
|
|
44861
|
+
const additionalLinter = _internals16.detectAdditionalLinter(cwd);
|
|
43707
44862
|
if (additionalLinter) {
|
|
43708
44863
|
warn(`[lint] Using ${additionalLinter} linter for this project`);
|
|
43709
|
-
const result = await
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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 =
|
|
43962
|
-
const resolvedRealPath =
|
|
43963
|
-
return resolvedRealPath === 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 =
|
|
43990
|
-
const relPath =
|
|
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 =
|
|
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
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
44454
|
-
if (
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
44560
|
-
await
|
|
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 =
|
|
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 (!
|
|
45726
|
+
if (!_internals18.isCacheStale(map3, generatedAt)) {
|
|
44572
45727
|
return map3;
|
|
44573
45728
|
}
|
|
44574
45729
|
} catch {}
|
|
44575
45730
|
}
|
|
44576
|
-
return
|
|
45731
|
+
return _internals18.buildImpactMap(cwd);
|
|
44577
45732
|
}
|
|
44578
45733
|
async function saveImpactMap(cwd, impactMap) {
|
|
44579
|
-
const cacheDir2 =
|
|
44580
|
-
const cachePath =
|
|
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
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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
|
|
46025
|
+
import path32 from "path";
|
|
44871
46026
|
function getHistoryPath(workingDir) {
|
|
44872
|
-
return
|
|
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 =
|
|
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
|
|
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 =
|
|
45030
|
-
const pathParts = normalizedDir.split(
|
|
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 =
|
|
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 =
|
|
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 +
|
|
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
|
|
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(
|
|
46305
|
+
return fs17.existsSync(path34.join(cwd, "go.mod")) && isCommandAvailable("go");
|
|
45151
46306
|
}
|
|
45152
46307
|
function detectJavaMaven(cwd) {
|
|
45153
|
-
return fs17.existsSync(
|
|
46308
|
+
return fs17.existsSync(path34.join(cwd, "pom.xml")) && isCommandAvailable("mvn");
|
|
45154
46309
|
}
|
|
45155
46310
|
function detectGradle(cwd) {
|
|
45156
|
-
const hasBuildFile = fs17.existsSync(
|
|
45157
|
-
const hasGradlew = fs17.existsSync(
|
|
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(
|
|
45171
|
-
const hasBuildCache = fs17.existsSync(
|
|
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(
|
|
46330
|
+
return fs17.existsSync(path34.join(cwd, "Package.swift")) && isCommandAvailable("swift");
|
|
45176
46331
|
}
|
|
45177
46332
|
function detectDartTest(cwd) {
|
|
45178
|
-
return fs17.existsSync(
|
|
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(
|
|
45182
|
-
const hasGemfile = fs17.existsSync(
|
|
45183
|
-
const hasSpecDir = fs17.existsSync(
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
45222
|
-
const setupCfgPath =
|
|
45223
|
-
const requirementsTxtPath =
|
|
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 =
|
|
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 =
|
|
45255
|
-
const pesterConfigJsonPath =
|
|
45256
|
-
const pesterPs1Path =
|
|
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
|
|
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
|
|
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 =
|
|
46482
|
+
const relativeDir = path34.dirname(relativePath);
|
|
45328
46483
|
const nestedRelativeDir = relativeDir === "." ? "" : relativeDir;
|
|
45329
46484
|
const directories = TEST_DIRECTORY_NAMES.flatMap((dirName) => {
|
|
45330
|
-
const rootDir =
|
|
45331
|
-
return 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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
45374
|
-
const basename5 =
|
|
45375
|
-
const
|
|
45376
|
-
const preferRelativeOutput = !
|
|
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 =
|
|
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) =>
|
|
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) =>
|
|
45401
|
-
...repoLevelDirectories.flatMap((candidateDir) => testDirectoryNames.map((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 =
|
|
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 =
|
|
45431
|
-
const existingExt =
|
|
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 =
|
|
45452
|
-
const importDir =
|
|
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 =
|
|
45455
|
-
const sourceBasename =
|
|
45456
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
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 =
|
|
45470
|
-
const existingExt =
|
|
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 =
|
|
45488
|
-
const importBasename =
|
|
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 =
|
|
45491
|
-
const sourceBasename =
|
|
45492
|
-
const isRelatedDir = importDir === sourceDir || importDir ===
|
|
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(
|
|
45596
|
-
const hasGradlew = fs17.existsSync(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
46347
|
-
return
|
|
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
|
|
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 =
|
|
46432
|
-
const absolutePath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
46497
|
-
const changelogVersion =
|
|
46498
|
-
const versionFileVersion =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
47018
|
-
return
|
|
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,
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
48209
|
-
const dest =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
48325
|
-
const reportPath =
|
|
48326
|
-
await fs22.mkdir(
|
|
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
|
|
48413
|
-
let
|
|
48414
|
-
let
|
|
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
|
-
|
|
49725
|
+
totalTasks++;
|
|
48418
49726
|
if (task.status === "completed")
|
|
48419
|
-
|
|
49727
|
+
completedTasks++;
|
|
48420
49728
|
}
|
|
48421
49729
|
}
|
|
48422
|
-
const
|
|
48423
|
-
const
|
|
48424
|
-
|
|
49730
|
+
const agentCount = Object.keys(agents).length;
|
|
49731
|
+
const metrics = getCompactionMetrics();
|
|
49732
|
+
status = {
|
|
48425
49733
|
hasPlan: true,
|
|
48426
|
-
currentPhase
|
|
48427
|
-
completedTasks
|
|
48428
|
-
totalTasks
|
|
48429
|
-
agentCount
|
|
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:
|
|
48434
|
-
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
|
-
|
|
48438
|
-
|
|
48439
|
-
|
|
48440
|
-
|
|
48441
|
-
|
|
48442
|
-
|
|
48443
|
-
|
|
48444
|
-
|
|
48445
|
-
|
|
48446
|
-
|
|
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
|
-
|
|
48454
|
-
|
|
48455
|
-
|
|
48456
|
-
|
|
48457
|
-
|
|
48458
|
-
const
|
|
48459
|
-
|
|
48460
|
-
|
|
48461
|
-
|
|
48462
|
-
|
|
48463
|
-
|
|
48464
|
-
|
|
48465
|
-
|
|
48466
|
-
|
|
48467
|
-
|
|
48468
|
-
|
|
48469
|
-
|
|
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.
|
|
48481
|
-
lines.push(""
|
|
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(
|
|
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
|
|
48556
|
-
|
|
48557
|
-
|
|
48558
|
-
|
|
48559
|
-
|
|
48560
|
-
|
|
48561
|
-
|
|
48562
|
-
|
|
48563
|
-
|
|
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
|
-
|
|
48566
|
-
|
|
50044
|
+
session.turboMode = true;
|
|
50045
|
+
session.turboStrategy = "standard";
|
|
50046
|
+
session.leanTurboActive = false;
|
|
50047
|
+
session.leanTurboCurrentPhase = undefined;
|
|
50048
|
+
return "Turbo Mode enabled";
|
|
48567
50049
|
}
|
|
48568
|
-
|
|
48569
|
-
|
|
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
|
|
48697
|
-
import
|
|
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 =
|
|
50357
|
+
const sentinelPath = path41.join(directory, ".swarm", ".first-run-complete");
|
|
48801
50358
|
try {
|
|
48802
|
-
const swarmDir =
|
|
48803
|
-
|
|
48804
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
50544
|
+
const resolved = _internals21.resolveCommand(tokens);
|
|
48988
50545
|
if (resolved) {
|
|
48989
|
-
return
|
|
50546
|
+
return _internals21.buildDetailedHelp(resolved.key, resolved.entry);
|
|
48990
50547
|
}
|
|
48991
|
-
const similar =
|
|
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
|
|
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 =
|
|
50591
|
+
const cycleStart = path42.indexOf(current);
|
|
49035
50592
|
const fullChain = [
|
|
49036
50593
|
name,
|
|
49037
|
-
...
|
|
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
|
-
|
|
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,
|
|
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) =>
|
|
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
|
-
|
|
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 =
|
|
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
|
|
51095
|
+
import * as fs24 from "fs";
|
|
49535
51096
|
import * as os7 from "os";
|
|
49536
|
-
import * as
|
|
51097
|
+
import * as path42 from "path";
|
|
49537
51098
|
var { version: version4 } = package_default;
|
|
49538
51099
|
var CONFIG_DIR = getPluginConfigDir();
|
|
49539
|
-
var OPENCODE_CONFIG_PATH =
|
|
49540
|
-
var PLUGIN_CONFIG_PATH =
|
|
49541
|
-
var PROMPTS_DIR =
|
|
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 =
|
|
49546
|
-
const home =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
49570
|
-
const home =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
49590
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
49609
|
-
const projectConfigPath =
|
|
49610
|
-
if (
|
|
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 =
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
49830
|
-
|
|
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 (
|
|
49835
|
-
|
|
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
|
}
|