opencode-swarm 6.14.0 → 6.14.11
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/LICENSE +21 -21
- package/dist/cli/index.js +0 -0
- package/dist/index.js +276 -99
- package/dist/lang/grammars/tree-sitter-bash.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-c-sharp.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-cpp.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-css.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-go.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-ini.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-java.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-php.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-powershell.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-python.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-regex.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-ruby.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-rust.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/lang/grammars/tree-sitter.wasm +0 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/save-plan.d.ts +54 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/package.json +2 -1
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/cli/index.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -1,27 +1,16 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
4
2
|
var __defProp = Object.defineProperty;
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
-
for (let key of __getOwnPropNames(mod))
|
|
11
|
-
if (!__hasOwnProp.call(to, key))
|
|
12
|
-
__defProp(to, key, {
|
|
13
|
-
get: () => mod[key],
|
|
14
|
-
enumerable: true
|
|
15
|
-
});
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name2, newValue) {
|
|
5
|
+
this[name2] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
18
7
|
var __export = (target, all) => {
|
|
19
8
|
for (var name2 in all)
|
|
20
9
|
__defProp(target, name2, {
|
|
21
10
|
get: all[name2],
|
|
22
11
|
enumerable: true,
|
|
23
12
|
configurable: true,
|
|
24
|
-
set: (
|
|
13
|
+
set: __exportSetter.bind(all, name2)
|
|
25
14
|
});
|
|
26
15
|
};
|
|
27
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -14816,7 +14805,7 @@ function migrateLegacyPlan(planContent, swarmId) {
|
|
|
14816
14805
|
}
|
|
14817
14806
|
continue;
|
|
14818
14807
|
}
|
|
14819
|
-
const phaseMatch = trimmed.match(
|
|
14808
|
+
const phaseMatch = trimmed.match(/^#{2,3}\s*Phase\s+(\d+)(?::\s*([^[]+))?\s*(?:\[([^\]]+)\])?/i);
|
|
14820
14809
|
if (phaseMatch) {
|
|
14821
14810
|
if (currentPhase !== null) {
|
|
14822
14811
|
phases.push(currentPhase);
|
|
@@ -14884,12 +14873,87 @@ function migrateLegacyPlan(planContent, swarmId) {
|
|
|
14884
14873
|
};
|
|
14885
14874
|
currentPhase.tasks.push(task);
|
|
14886
14875
|
}
|
|
14876
|
+
const numberedTaskMatch = trimmed.match(/^(\d+)\.\s+(.+?)(?:\s*\[(\w+)\])?$/);
|
|
14877
|
+
if (numberedTaskMatch && currentPhase !== null) {
|
|
14878
|
+
const taskId = `${currentPhase.id}.${currentPhase.tasks.length + 1}`;
|
|
14879
|
+
let description = numberedTaskMatch[2].trim();
|
|
14880
|
+
const sizeText = numberedTaskMatch[3]?.toLowerCase() || "small";
|
|
14881
|
+
const dependsMatch = description.match(/\s*\(depends:\s*([^)]+)\)$/i);
|
|
14882
|
+
const depends = [];
|
|
14883
|
+
if (dependsMatch) {
|
|
14884
|
+
const depsText = dependsMatch[1];
|
|
14885
|
+
depends.push(...depsText.split(",").map((d) => d.trim()));
|
|
14886
|
+
description = description.substring(0, dependsMatch.index).trim();
|
|
14887
|
+
}
|
|
14888
|
+
const sizeMap = {
|
|
14889
|
+
small: "small",
|
|
14890
|
+
medium: "medium",
|
|
14891
|
+
large: "large"
|
|
14892
|
+
};
|
|
14893
|
+
const task = {
|
|
14894
|
+
id: taskId,
|
|
14895
|
+
phase: currentPhase.id,
|
|
14896
|
+
status: "pending",
|
|
14897
|
+
size: sizeMap[sizeText] || "small",
|
|
14898
|
+
description,
|
|
14899
|
+
depends,
|
|
14900
|
+
acceptance: undefined,
|
|
14901
|
+
files_touched: [],
|
|
14902
|
+
evidence_path: undefined,
|
|
14903
|
+
blocked_reason: undefined
|
|
14904
|
+
};
|
|
14905
|
+
currentPhase.tasks.push(task);
|
|
14906
|
+
}
|
|
14907
|
+
const noPrefixTaskMatch = trimmed.match(/^-\s*\[([^\]]+)\]\s+(?!\d+\.\d+:)(.+?)(?:\s*\[(\w+)\])?(?:\s*-\s*(.+))?$/i);
|
|
14908
|
+
if (noPrefixTaskMatch && currentPhase !== null) {
|
|
14909
|
+
const checkbox = noPrefixTaskMatch[1].toLowerCase();
|
|
14910
|
+
const taskId = `${currentPhase.id}.${currentPhase.tasks.length + 1}`;
|
|
14911
|
+
let description = noPrefixTaskMatch[2].trim();
|
|
14912
|
+
const sizeText = noPrefixTaskMatch[3]?.toLowerCase() || "small";
|
|
14913
|
+
let blockedReason;
|
|
14914
|
+
const dependsMatch = description.match(/\s*\(depends:\s*([^)]+)\)$/i);
|
|
14915
|
+
const depends = [];
|
|
14916
|
+
if (dependsMatch) {
|
|
14917
|
+
const depsText = dependsMatch[1];
|
|
14918
|
+
depends.push(...depsText.split(",").map((d) => d.trim()));
|
|
14919
|
+
description = description.substring(0, dependsMatch.index).trim();
|
|
14920
|
+
}
|
|
14921
|
+
let status = "pending";
|
|
14922
|
+
if (checkbox === "x") {
|
|
14923
|
+
status = "completed";
|
|
14924
|
+
} else if (checkbox === "blocked") {
|
|
14925
|
+
status = "blocked";
|
|
14926
|
+
const blockedReasonMatch = noPrefixTaskMatch[4];
|
|
14927
|
+
if (blockedReasonMatch) {
|
|
14928
|
+
blockedReason = blockedReasonMatch.trim();
|
|
14929
|
+
}
|
|
14930
|
+
}
|
|
14931
|
+
const sizeMap = {
|
|
14932
|
+
small: "small",
|
|
14933
|
+
medium: "medium",
|
|
14934
|
+
large: "large"
|
|
14935
|
+
};
|
|
14936
|
+
const task = {
|
|
14937
|
+
id: taskId,
|
|
14938
|
+
phase: currentPhase.id,
|
|
14939
|
+
status,
|
|
14940
|
+
size: sizeMap[sizeText] || "small",
|
|
14941
|
+
description,
|
|
14942
|
+
depends,
|
|
14943
|
+
acceptance: undefined,
|
|
14944
|
+
files_touched: [],
|
|
14945
|
+
evidence_path: undefined,
|
|
14946
|
+
blocked_reason: blockedReason
|
|
14947
|
+
};
|
|
14948
|
+
currentPhase.tasks.push(task);
|
|
14949
|
+
}
|
|
14887
14950
|
}
|
|
14888
14951
|
if (currentPhase !== null) {
|
|
14889
14952
|
phases.push(currentPhase);
|
|
14890
14953
|
}
|
|
14891
14954
|
let migrationStatus = "migrated";
|
|
14892
14955
|
if (phases.length === 0) {
|
|
14956
|
+
console.warn(`migrateLegacyPlan: 0 phases parsed from ${lines.length} lines. First 3 lines: ${lines.slice(0, 3).join(" | ")}`);
|
|
14893
14957
|
migrationStatus = "migration_failed";
|
|
14894
14958
|
phases.push({
|
|
14895
14959
|
id: 1,
|
|
@@ -31440,7 +31504,7 @@ var init_preflight_integration = __esm(() => {
|
|
|
31440
31504
|
});
|
|
31441
31505
|
|
|
31442
31506
|
// src/index.ts
|
|
31443
|
-
import * as
|
|
31507
|
+
import * as path32 from "path";
|
|
31444
31508
|
|
|
31445
31509
|
// src/tools/tool-names.ts
|
|
31446
31510
|
var TOOL_NAMES = [
|
|
@@ -31467,7 +31531,8 @@ var TOOL_NAMES = [
|
|
|
31467
31531
|
"gitingest",
|
|
31468
31532
|
"retrieve_summary",
|
|
31469
31533
|
"extract_code_blocks",
|
|
31470
|
-
"phase_complete"
|
|
31534
|
+
"phase_complete",
|
|
31535
|
+
"save_plan"
|
|
31471
31536
|
];
|
|
31472
31537
|
var TOOL_NAME_SET = new Set(TOOL_NAMES);
|
|
31473
31538
|
|
|
@@ -31500,6 +31565,7 @@ var AGENT_TOOL_MAP = {
|
|
|
31500
31565
|
"pkg_audit",
|
|
31501
31566
|
"pre_check_batch",
|
|
31502
31567
|
"retrieve_summary",
|
|
31568
|
+
"save_plan",
|
|
31503
31569
|
"schema_drift",
|
|
31504
31570
|
"secretscan",
|
|
31505
31571
|
"symbols",
|
|
@@ -32531,10 +32597,21 @@ This briefing is a HARD REQUIREMENT for ALL phases. Skipping it is a process vio
|
|
|
32531
32597
|
|
|
32532
32598
|
### MODE: PLAN
|
|
32533
32599
|
|
|
32534
|
-
|
|
32535
|
-
-
|
|
32536
|
-
-
|
|
32537
|
-
-
|
|
32600
|
+
Use the \`save_plan\` tool to create the implementation plan. Required parameters:
|
|
32601
|
+
- \`title\`: The real project name from the spec (NOT a placeholder like [Project])
|
|
32602
|
+
- \`swarm_id\`: The swarm identifier (e.g. "mega", "local", "paid")
|
|
32603
|
+
- \`phases\`: Array of phases, each with \`id\` (number), \`name\` (string), and \`tasks\` (array)
|
|
32604
|
+
- Each task needs: \`id\` (e.g. "1.1"), \`description\` (real content from spec \u2014 bracket placeholders like [task] will be REJECTED)
|
|
32605
|
+
- Optional task fields: \`size\` (small/medium/large), \`depends\` (array of task IDs), \`acceptance\` (string)
|
|
32606
|
+
|
|
32607
|
+
Example call:
|
|
32608
|
+
save_plan({ title: "My Real Project", swarm_id: "mega", phases: [{ id: 1, name: "Setup", tasks: [{ id: "1.1", description: "Install dependencies and configure TypeScript", size: "small" }] }] })
|
|
32609
|
+
|
|
32610
|
+
\u26A0\uFE0F If \`save_plan\` is unavailable, delegate plan writing to {{AGENT_PREFIX}}coder:
|
|
32611
|
+
TASK: Write the implementation plan to .swarm/plan.md
|
|
32612
|
+
FILE: .swarm/plan.md
|
|
32613
|
+
INPUT: [provide the complete plan content below]
|
|
32614
|
+
CONSTRAINT: Write EXACTLY the content provided. Do not modify, summarize, or interpret.
|
|
32538
32615
|
|
|
32539
32616
|
TASK GRANULARITY RULES:
|
|
32540
32617
|
- SMALL task: 1 file, 1 function/class/component, 1 logical concern. Delegate as-is.
|
|
@@ -32553,8 +32630,7 @@ PHASE COUNT GUIDANCE:
|
|
|
32553
32630
|
- Rationale: Retrospectives at phase boundaries capture lessons that improve subsequent
|
|
32554
32631
|
phases. A single-phase plan gets zero iterative learning benefit.
|
|
32555
32632
|
|
|
32556
|
-
|
|
32557
|
-
- Decisions, patterns, SME cache, file map
|
|
32633
|
+
Also create .swarm/context.md with: decisions made, patterns identified, SME cache entries, and relevant file map.
|
|
32558
32634
|
|
|
32559
32635
|
### MODE: CRITIC-GATE
|
|
32560
32636
|
Delegate plan to {{AGENT_PREFIX}}critic for review BEFORE any implementation begins.
|
|
@@ -32765,19 +32841,21 @@ Mark [BLOCKED] in plan.md, skip to next unblocked task, inform user.
|
|
|
32765
32841
|
|
|
32766
32842
|
## FILES
|
|
32767
32843
|
|
|
32844
|
+
\u26A0\uFE0F FILE FORMAT RULES: Every value in angle brackets below MUST be real content derived from the spec or codebase analysis. NEVER write literal bracket-placeholder text like "[task]", "[Project]", "[date]", "[reason]" \u2014 those are template slots in this example, NOT values to reproduce. Status tags like [COMPLETE], [IN PROGRESS], [BLOCKED], [SMALL], [MEDIUM], [LARGE], and checkboxes [x]/[ ] are valid format elements and must be reproduced exactly.
|
|
32845
|
+
|
|
32768
32846
|
.swarm/plan.md:
|
|
32769
32847
|
\`\`\`
|
|
32770
|
-
#
|
|
32848
|
+
# <real project name derived from the spec>
|
|
32771
32849
|
Swarm: {{SWARM_ID}}
|
|
32772
|
-
Phase:
|
|
32850
|
+
Phase: <current phase number> | Updated: <today's date in ISO format>
|
|
32773
32851
|
|
|
32774
|
-
## Phase 1 [COMPLETE]
|
|
32775
|
-
- [x] 1.1:
|
|
32852
|
+
## Phase 1: <descriptive phase name> [COMPLETE]
|
|
32853
|
+
- [x] 1.1: <specific completed task description from spec> [SMALL]
|
|
32776
32854
|
|
|
32777
|
-
## Phase 2 [IN PROGRESS]
|
|
32778
|
-
- [x] 2.1:
|
|
32779
|
-
- [ ] 2.2:
|
|
32780
|
-
- [BLOCKED] 2.3:
|
|
32855
|
+
## Phase 2: <descriptive phase name> [IN PROGRESS]
|
|
32856
|
+
- [x] 2.1: <specific task description from spec> [MEDIUM]
|
|
32857
|
+
- [ ] 2.2: <specific task description from spec> (depends: 2.1) \u2190 CURRENT
|
|
32858
|
+
- [BLOCKED] 2.3: <specific task description from spec> - <reason for blockage>
|
|
32781
32859
|
\`\`\`
|
|
32782
32860
|
|
|
32783
32861
|
.swarm/context.md:
|
|
@@ -32786,14 +32864,14 @@ Phase: [N] | Updated: [date]
|
|
|
32786
32864
|
Swarm: {{SWARM_ID}}
|
|
32787
32865
|
|
|
32788
32866
|
## Decisions
|
|
32789
|
-
-
|
|
32867
|
+
- <specific technical decision made>: <rationale for the decision>
|
|
32790
32868
|
|
|
32791
32869
|
## SME Cache
|
|
32792
|
-
###
|
|
32793
|
-
-
|
|
32870
|
+
### <domain name e.g. security, cross-platform>
|
|
32871
|
+
- <specific guidance from the SME consultation>
|
|
32794
32872
|
|
|
32795
32873
|
## Patterns
|
|
32796
|
-
-
|
|
32874
|
+
- <pattern name>: <how and when to use it in this codebase>
|
|
32797
32875
|
\`\`\``;
|
|
32798
32876
|
function createArchitectAgent(model, customPrompt, customAppendPrompt) {
|
|
32799
32877
|
let prompt = ARCHITECT_PROMPT;
|
|
@@ -41017,10 +41095,108 @@ var phase_complete = tool({
|
|
|
41017
41095
|
return executePhaseComplete(phaseCompleteArgs);
|
|
41018
41096
|
}
|
|
41019
41097
|
});
|
|
41098
|
+
// src/tools/save-plan.ts
|
|
41099
|
+
init_tool();
|
|
41100
|
+
init_manager2();
|
|
41101
|
+
import * as path23 from "path";
|
|
41102
|
+
function detectPlaceholderContent(args2) {
|
|
41103
|
+
const issues = [];
|
|
41104
|
+
const placeholderPattern = /^\[\w[\w\s]*\]$/;
|
|
41105
|
+
if (placeholderPattern.test(args2.title.trim())) {
|
|
41106
|
+
issues.push(`Plan title appears to be a template placeholder: "${args2.title}"`);
|
|
41107
|
+
}
|
|
41108
|
+
for (const phase of args2.phases) {
|
|
41109
|
+
if (placeholderPattern.test(phase.name.trim())) {
|
|
41110
|
+
issues.push(`Phase ${phase.id} name appears to be a template placeholder: "${phase.name}"`);
|
|
41111
|
+
}
|
|
41112
|
+
for (const task of phase.tasks) {
|
|
41113
|
+
if (placeholderPattern.test(task.description.trim())) {
|
|
41114
|
+
issues.push(`Task ${task.id} description appears to be a template placeholder: "${task.description}"`);
|
|
41115
|
+
}
|
|
41116
|
+
}
|
|
41117
|
+
}
|
|
41118
|
+
return issues;
|
|
41119
|
+
}
|
|
41120
|
+
async function executeSavePlan(args2) {
|
|
41121
|
+
const placeholderIssues = detectPlaceholderContent(args2);
|
|
41122
|
+
if (placeholderIssues.length > 0) {
|
|
41123
|
+
return {
|
|
41124
|
+
success: false,
|
|
41125
|
+
message: "Plan rejected: contains template placeholder content",
|
|
41126
|
+
errors: placeholderIssues
|
|
41127
|
+
};
|
|
41128
|
+
}
|
|
41129
|
+
const plan = {
|
|
41130
|
+
schema_version: "1.0.0",
|
|
41131
|
+
title: args2.title,
|
|
41132
|
+
swarm: args2.swarm_id,
|
|
41133
|
+
migration_status: "native",
|
|
41134
|
+
current_phase: args2.phases[0]?.id,
|
|
41135
|
+
phases: args2.phases.map((phase) => {
|
|
41136
|
+
return {
|
|
41137
|
+
id: phase.id,
|
|
41138
|
+
name: phase.name,
|
|
41139
|
+
status: "pending",
|
|
41140
|
+
tasks: phase.tasks.map((task) => {
|
|
41141
|
+
return {
|
|
41142
|
+
id: task.id,
|
|
41143
|
+
phase: phase.id,
|
|
41144
|
+
status: "pending",
|
|
41145
|
+
size: task.size ?? "small",
|
|
41146
|
+
description: task.description,
|
|
41147
|
+
depends: task.depends ?? [],
|
|
41148
|
+
acceptance: task.acceptance,
|
|
41149
|
+
files_touched: []
|
|
41150
|
+
};
|
|
41151
|
+
})
|
|
41152
|
+
};
|
|
41153
|
+
})
|
|
41154
|
+
};
|
|
41155
|
+
const tasksCount = plan.phases.reduce((acc, phase) => acc + phase.tasks.length, 0);
|
|
41156
|
+
const dir = args2.working_directory ?? process.cwd();
|
|
41157
|
+
try {
|
|
41158
|
+
await savePlan(dir, plan);
|
|
41159
|
+
return {
|
|
41160
|
+
success: true,
|
|
41161
|
+
message: "Plan saved successfully",
|
|
41162
|
+
plan_path: path23.join(dir, ".swarm", "plan.json"),
|
|
41163
|
+
phases_count: plan.phases.length,
|
|
41164
|
+
tasks_count: tasksCount
|
|
41165
|
+
};
|
|
41166
|
+
} catch (error93) {
|
|
41167
|
+
return {
|
|
41168
|
+
success: false,
|
|
41169
|
+
message: "Failed to save plan",
|
|
41170
|
+
errors: [String(error93)]
|
|
41171
|
+
};
|
|
41172
|
+
}
|
|
41173
|
+
}
|
|
41174
|
+
var save_plan = tool({
|
|
41175
|
+
description: "Save a structured implementation plan to .swarm/plan.json and .swarm/plan.md. " + "Task descriptions and phase names MUST contain real content from the spec \u2014 " + "bracket placeholders like [task] or [Project] will be rejected.",
|
|
41176
|
+
args: {
|
|
41177
|
+
title: tool.schema.string().min(1).describe("Plan title \u2014 the REAL project name from the spec. NOT a placeholder like [Project]."),
|
|
41178
|
+
swarm_id: tool.schema.string().min(1).describe('Swarm identifier (e.g. "mega")'),
|
|
41179
|
+
phases: tool.schema.array(tool.schema.object({
|
|
41180
|
+
id: tool.schema.number().int().positive().describe("Phase number, starting at 1"),
|
|
41181
|
+
name: tool.schema.string().min(1).describe("Descriptive phase name derived from the spec"),
|
|
41182
|
+
tasks: tool.schema.array(tool.schema.object({
|
|
41183
|
+
id: tool.schema.string().min(1).regex(/^\d+\.\d+(\.\d+)*$/, 'Task ID must be in N.M format, e.g. "1.1"').describe('Task ID in N.M format, e.g. "1.1", "2.3"'),
|
|
41184
|
+
description: tool.schema.string().min(1).describe("Specific task description from the spec. NOT a placeholder like [task]."),
|
|
41185
|
+
size: tool.schema.enum(["small", "medium", "large"]).optional().describe("Task size estimate (default: small)"),
|
|
41186
|
+
depends: tool.schema.array(tool.schema.string()).optional().describe('Task IDs this task depends on, e.g. ["1.1", "1.2"]'),
|
|
41187
|
+
acceptance: tool.schema.string().optional().describe("Acceptance criteria for this task")
|
|
41188
|
+
})).min(1).describe("Tasks in this phase")
|
|
41189
|
+
})).min(1).describe("Implementation phases"),
|
|
41190
|
+
working_directory: tool.schema.string().optional().describe("Working directory (defaults to process.cwd())")
|
|
41191
|
+
},
|
|
41192
|
+
execute: async (args2) => {
|
|
41193
|
+
return JSON.stringify(await executeSavePlan(args2), null, 2);
|
|
41194
|
+
}
|
|
41195
|
+
});
|
|
41020
41196
|
// src/tools/pkg-audit.ts
|
|
41021
41197
|
init_dist();
|
|
41022
41198
|
import * as fs18 from "fs";
|
|
41023
|
-
import * as
|
|
41199
|
+
import * as path24 from "path";
|
|
41024
41200
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
41025
41201
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
41026
41202
|
function isValidEcosystem(value) {
|
|
@@ -41038,13 +41214,13 @@ function validateArgs3(args2) {
|
|
|
41038
41214
|
function detectEcosystems() {
|
|
41039
41215
|
const ecosystems = [];
|
|
41040
41216
|
const cwd = process.cwd();
|
|
41041
|
-
if (fs18.existsSync(
|
|
41217
|
+
if (fs18.existsSync(path24.join(cwd, "package.json"))) {
|
|
41042
41218
|
ecosystems.push("npm");
|
|
41043
41219
|
}
|
|
41044
|
-
if (fs18.existsSync(
|
|
41220
|
+
if (fs18.existsSync(path24.join(cwd, "pyproject.toml")) || fs18.existsSync(path24.join(cwd, "requirements.txt"))) {
|
|
41045
41221
|
ecosystems.push("pip");
|
|
41046
41222
|
}
|
|
41047
|
-
if (fs18.existsSync(
|
|
41223
|
+
if (fs18.existsSync(path24.join(cwd, "Cargo.toml"))) {
|
|
41048
41224
|
ecosystems.push("cargo");
|
|
41049
41225
|
}
|
|
41050
41226
|
return ecosystems;
|
|
@@ -42952,11 +43128,11 @@ var Module2 = (() => {
|
|
|
42952
43128
|
throw toThrow;
|
|
42953
43129
|
}, "quit_");
|
|
42954
43130
|
var scriptDirectory = "";
|
|
42955
|
-
function locateFile(
|
|
43131
|
+
function locateFile(path25) {
|
|
42956
43132
|
if (Module["locateFile"]) {
|
|
42957
|
-
return Module["locateFile"](
|
|
43133
|
+
return Module["locateFile"](path25, scriptDirectory);
|
|
42958
43134
|
}
|
|
42959
|
-
return scriptDirectory +
|
|
43135
|
+
return scriptDirectory + path25;
|
|
42960
43136
|
}
|
|
42961
43137
|
__name(locateFile, "locateFile");
|
|
42962
43138
|
var readAsync, readBinary;
|
|
@@ -44770,7 +44946,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
44770
44946
|
]);
|
|
44771
44947
|
// src/tools/pre-check-batch.ts
|
|
44772
44948
|
init_dist();
|
|
44773
|
-
import * as
|
|
44949
|
+
import * as path27 from "path";
|
|
44774
44950
|
|
|
44775
44951
|
// node_modules/yocto-queue/index.js
|
|
44776
44952
|
class Node2 {
|
|
@@ -44937,7 +45113,7 @@ init_manager();
|
|
|
44937
45113
|
|
|
44938
45114
|
// src/quality/metrics.ts
|
|
44939
45115
|
import * as fs19 from "fs";
|
|
44940
|
-
import * as
|
|
45116
|
+
import * as path25 from "path";
|
|
44941
45117
|
var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
|
|
44942
45118
|
var MIN_DUPLICATION_LINES = 10;
|
|
44943
45119
|
function estimateCyclomaticComplexity(content) {
|
|
@@ -44989,7 +45165,7 @@ async function computeComplexityDelta(files, workingDir) {
|
|
|
44989
45165
|
let totalComplexity = 0;
|
|
44990
45166
|
const analyzedFiles = [];
|
|
44991
45167
|
for (const file3 of files) {
|
|
44992
|
-
const fullPath =
|
|
45168
|
+
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
44993
45169
|
if (!fs19.existsSync(fullPath)) {
|
|
44994
45170
|
continue;
|
|
44995
45171
|
}
|
|
@@ -45112,7 +45288,7 @@ function countGoExports(content) {
|
|
|
45112
45288
|
function getExportCountForFile(filePath) {
|
|
45113
45289
|
try {
|
|
45114
45290
|
const content = fs19.readFileSync(filePath, "utf-8");
|
|
45115
|
-
const ext =
|
|
45291
|
+
const ext = path25.extname(filePath).toLowerCase();
|
|
45116
45292
|
switch (ext) {
|
|
45117
45293
|
case ".ts":
|
|
45118
45294
|
case ".tsx":
|
|
@@ -45138,7 +45314,7 @@ async function computePublicApiDelta(files, workingDir) {
|
|
|
45138
45314
|
let totalExports = 0;
|
|
45139
45315
|
const analyzedFiles = [];
|
|
45140
45316
|
for (const file3 of files) {
|
|
45141
|
-
const fullPath =
|
|
45317
|
+
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
45142
45318
|
if (!fs19.existsSync(fullPath)) {
|
|
45143
45319
|
continue;
|
|
45144
45320
|
}
|
|
@@ -45172,7 +45348,7 @@ async function computeDuplicationRatio(files, workingDir) {
|
|
|
45172
45348
|
let duplicateLines = 0;
|
|
45173
45349
|
const analyzedFiles = [];
|
|
45174
45350
|
for (const file3 of files) {
|
|
45175
|
-
const fullPath =
|
|
45351
|
+
const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
|
|
45176
45352
|
if (!fs19.existsSync(fullPath)) {
|
|
45177
45353
|
continue;
|
|
45178
45354
|
}
|
|
@@ -45205,8 +45381,8 @@ function countCodeLines(content) {
|
|
|
45205
45381
|
return lines.length;
|
|
45206
45382
|
}
|
|
45207
45383
|
function isTestFile(filePath) {
|
|
45208
|
-
const basename5 =
|
|
45209
|
-
const _ext =
|
|
45384
|
+
const basename5 = path25.basename(filePath);
|
|
45385
|
+
const _ext = path25.extname(filePath).toLowerCase();
|
|
45210
45386
|
const testPatterns = [
|
|
45211
45387
|
".test.",
|
|
45212
45388
|
".spec.",
|
|
@@ -45248,7 +45424,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
|
|
|
45248
45424
|
async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
45249
45425
|
let testLines = 0;
|
|
45250
45426
|
let codeLines = 0;
|
|
45251
|
-
const srcDir =
|
|
45427
|
+
const srcDir = path25.join(workingDir, "src");
|
|
45252
45428
|
if (fs19.existsSync(srcDir)) {
|
|
45253
45429
|
await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
45254
45430
|
codeLines += lines;
|
|
@@ -45256,14 +45432,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
45256
45432
|
}
|
|
45257
45433
|
const possibleSrcDirs = ["lib", "app", "source", "core"];
|
|
45258
45434
|
for (const dir of possibleSrcDirs) {
|
|
45259
|
-
const dirPath =
|
|
45435
|
+
const dirPath = path25.join(workingDir, dir);
|
|
45260
45436
|
if (fs19.existsSync(dirPath)) {
|
|
45261
45437
|
await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
45262
45438
|
codeLines += lines;
|
|
45263
45439
|
});
|
|
45264
45440
|
}
|
|
45265
45441
|
}
|
|
45266
|
-
const testsDir =
|
|
45442
|
+
const testsDir = path25.join(workingDir, "tests");
|
|
45267
45443
|
if (fs19.existsSync(testsDir)) {
|
|
45268
45444
|
await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
45269
45445
|
testLines += lines;
|
|
@@ -45271,7 +45447,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
45271
45447
|
}
|
|
45272
45448
|
const possibleTestDirs = ["test", "__tests__", "specs"];
|
|
45273
45449
|
for (const dir of possibleTestDirs) {
|
|
45274
|
-
const dirPath =
|
|
45450
|
+
const dirPath = path25.join(workingDir, dir);
|
|
45275
45451
|
if (fs19.existsSync(dirPath) && dirPath !== testsDir) {
|
|
45276
45452
|
await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
45277
45453
|
testLines += lines;
|
|
@@ -45286,7 +45462,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
45286
45462
|
try {
|
|
45287
45463
|
const entries = fs19.readdirSync(dirPath, { withFileTypes: true });
|
|
45288
45464
|
for (const entry of entries) {
|
|
45289
|
-
const fullPath =
|
|
45465
|
+
const fullPath = path25.join(dirPath, entry.name);
|
|
45290
45466
|
if (entry.isDirectory()) {
|
|
45291
45467
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
|
|
45292
45468
|
continue;
|
|
@@ -45294,7 +45470,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
45294
45470
|
await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
|
|
45295
45471
|
} else if (entry.isFile()) {
|
|
45296
45472
|
const relativePath = fullPath.replace(`${process.cwd()}/`, "");
|
|
45297
|
-
const ext =
|
|
45473
|
+
const ext = path25.extname(entry.name).toLowerCase();
|
|
45298
45474
|
const validExts = [
|
|
45299
45475
|
".ts",
|
|
45300
45476
|
".tsx",
|
|
@@ -45549,7 +45725,7 @@ async function qualityBudget(input, directory) {
|
|
|
45549
45725
|
// src/tools/sast-scan.ts
|
|
45550
45726
|
init_manager();
|
|
45551
45727
|
import * as fs20 from "fs";
|
|
45552
|
-
import * as
|
|
45728
|
+
import * as path26 from "path";
|
|
45553
45729
|
import { extname as extname7 } from "path";
|
|
45554
45730
|
|
|
45555
45731
|
// src/sast/rules/c.ts
|
|
@@ -46504,7 +46680,7 @@ async function sastScan(input, directory, config3) {
|
|
|
46504
46680
|
const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
|
|
46505
46681
|
const filesByLanguage = new Map;
|
|
46506
46682
|
for (const filePath of changed_files) {
|
|
46507
|
-
const resolvedPath =
|
|
46683
|
+
const resolvedPath = path26.isAbsolute(filePath) ? filePath : path26.resolve(directory, filePath);
|
|
46508
46684
|
if (!fs20.existsSync(resolvedPath)) {
|
|
46509
46685
|
_filesSkipped++;
|
|
46510
46686
|
continue;
|
|
@@ -46613,10 +46789,10 @@ function validatePath(inputPath, baseDir) {
|
|
|
46613
46789
|
if (!inputPath || inputPath.length === 0) {
|
|
46614
46790
|
return "path is required";
|
|
46615
46791
|
}
|
|
46616
|
-
const resolved =
|
|
46617
|
-
const baseResolved =
|
|
46618
|
-
const relative3 =
|
|
46619
|
-
if (relative3.startsWith("..") ||
|
|
46792
|
+
const resolved = path27.resolve(baseDir, inputPath);
|
|
46793
|
+
const baseResolved = path27.resolve(baseDir);
|
|
46794
|
+
const relative3 = path27.relative(baseResolved, resolved);
|
|
46795
|
+
if (relative3.startsWith("..") || path27.isAbsolute(relative3)) {
|
|
46620
46796
|
return "path traversal detected";
|
|
46621
46797
|
}
|
|
46622
46798
|
return null;
|
|
@@ -46738,7 +46914,7 @@ async function runPreCheckBatch(input) {
|
|
|
46738
46914
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
46739
46915
|
continue;
|
|
46740
46916
|
}
|
|
46741
|
-
changedFiles.push(
|
|
46917
|
+
changedFiles.push(path27.resolve(directory, file3));
|
|
46742
46918
|
}
|
|
46743
46919
|
} else {
|
|
46744
46920
|
changedFiles = [];
|
|
@@ -46929,7 +47105,7 @@ var retrieve_summary = tool({
|
|
|
46929
47105
|
init_dist();
|
|
46930
47106
|
init_manager();
|
|
46931
47107
|
import * as fs21 from "fs";
|
|
46932
|
-
import * as
|
|
47108
|
+
import * as path28 from "path";
|
|
46933
47109
|
|
|
46934
47110
|
// src/sbom/detectors/dart.ts
|
|
46935
47111
|
function parsePubspecLock(content) {
|
|
@@ -47776,7 +47952,7 @@ function findManifestFiles(rootDir) {
|
|
|
47776
47952
|
try {
|
|
47777
47953
|
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
47778
47954
|
for (const entry of entries) {
|
|
47779
|
-
const fullPath =
|
|
47955
|
+
const fullPath = path28.join(dir, entry.name);
|
|
47780
47956
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
47781
47957
|
continue;
|
|
47782
47958
|
}
|
|
@@ -47786,7 +47962,7 @@ function findManifestFiles(rootDir) {
|
|
|
47786
47962
|
for (const pattern of patterns) {
|
|
47787
47963
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
47788
47964
|
if (new RegExp(regex, "i").test(entry.name)) {
|
|
47789
|
-
manifestFiles.push(
|
|
47965
|
+
manifestFiles.push(path28.relative(cwd, fullPath));
|
|
47790
47966
|
break;
|
|
47791
47967
|
}
|
|
47792
47968
|
}
|
|
@@ -47805,12 +47981,12 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
47805
47981
|
try {
|
|
47806
47982
|
const entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
47807
47983
|
for (const entry of entries) {
|
|
47808
|
-
const fullPath =
|
|
47984
|
+
const fullPath = path28.join(dir, entry.name);
|
|
47809
47985
|
if (entry.isFile()) {
|
|
47810
47986
|
for (const pattern of patterns) {
|
|
47811
47987
|
const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
47812
47988
|
if (new RegExp(regex, "i").test(entry.name)) {
|
|
47813
|
-
found.push(
|
|
47989
|
+
found.push(path28.relative(workingDir, fullPath));
|
|
47814
47990
|
break;
|
|
47815
47991
|
}
|
|
47816
47992
|
}
|
|
@@ -47823,11 +47999,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
47823
47999
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
47824
48000
|
const dirs = new Set;
|
|
47825
48001
|
for (const file3 of changedFiles) {
|
|
47826
|
-
let currentDir =
|
|
48002
|
+
let currentDir = path28.dirname(file3);
|
|
47827
48003
|
while (true) {
|
|
47828
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
47829
|
-
dirs.add(
|
|
47830
|
-
const parent =
|
|
48004
|
+
if (currentDir && currentDir !== "." && currentDir !== path28.sep) {
|
|
48005
|
+
dirs.add(path28.join(workingDir, currentDir));
|
|
48006
|
+
const parent = path28.dirname(currentDir);
|
|
47831
48007
|
if (parent === currentDir)
|
|
47832
48008
|
break;
|
|
47833
48009
|
currentDir = parent;
|
|
@@ -47934,7 +48110,7 @@ var sbom_generate = tool({
|
|
|
47934
48110
|
const processedFiles = [];
|
|
47935
48111
|
for (const manifestFile of manifestFiles) {
|
|
47936
48112
|
try {
|
|
47937
|
-
const fullPath =
|
|
48113
|
+
const fullPath = path28.isAbsolute(manifestFile) ? manifestFile : path28.join(workingDir, manifestFile);
|
|
47938
48114
|
if (!fs21.existsSync(fullPath)) {
|
|
47939
48115
|
continue;
|
|
47940
48116
|
}
|
|
@@ -47951,7 +48127,7 @@ var sbom_generate = tool({
|
|
|
47951
48127
|
const bom = generateCycloneDX(allComponents);
|
|
47952
48128
|
const bomJson = serializeCycloneDX(bom);
|
|
47953
48129
|
const filename = generateSbomFilename();
|
|
47954
|
-
const outputPath =
|
|
48130
|
+
const outputPath = path28.join(outputDir, filename);
|
|
47955
48131
|
fs21.writeFileSync(outputPath, bomJson, "utf-8");
|
|
47956
48132
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
47957
48133
|
try {
|
|
@@ -47994,7 +48170,7 @@ var sbom_generate = tool({
|
|
|
47994
48170
|
// src/tools/schema-drift.ts
|
|
47995
48171
|
init_dist();
|
|
47996
48172
|
import * as fs22 from "fs";
|
|
47997
|
-
import * as
|
|
48173
|
+
import * as path29 from "path";
|
|
47998
48174
|
var SPEC_CANDIDATES = [
|
|
47999
48175
|
"openapi.json",
|
|
48000
48176
|
"openapi.yaml",
|
|
@@ -48026,12 +48202,12 @@ function normalizePath(p) {
|
|
|
48026
48202
|
}
|
|
48027
48203
|
function discoverSpecFile(cwd, specFileArg) {
|
|
48028
48204
|
if (specFileArg) {
|
|
48029
|
-
const resolvedPath =
|
|
48030
|
-
const normalizedCwd = cwd.endsWith(
|
|
48205
|
+
const resolvedPath = path29.resolve(cwd, specFileArg);
|
|
48206
|
+
const normalizedCwd = cwd.endsWith(path29.sep) ? cwd : cwd + path29.sep;
|
|
48031
48207
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
48032
48208
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
48033
48209
|
}
|
|
48034
|
-
const ext =
|
|
48210
|
+
const ext = path29.extname(resolvedPath).toLowerCase();
|
|
48035
48211
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
48036
48212
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
48037
48213
|
}
|
|
@@ -48045,7 +48221,7 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
48045
48221
|
return resolvedPath;
|
|
48046
48222
|
}
|
|
48047
48223
|
for (const candidate of SPEC_CANDIDATES) {
|
|
48048
|
-
const candidatePath =
|
|
48224
|
+
const candidatePath = path29.resolve(cwd, candidate);
|
|
48049
48225
|
if (fs22.existsSync(candidatePath)) {
|
|
48050
48226
|
const stats = fs22.statSync(candidatePath);
|
|
48051
48227
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
@@ -48057,7 +48233,7 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
48057
48233
|
}
|
|
48058
48234
|
function parseSpec(specFile) {
|
|
48059
48235
|
const content = fs22.readFileSync(specFile, "utf-8");
|
|
48060
|
-
const ext =
|
|
48236
|
+
const ext = path29.extname(specFile).toLowerCase();
|
|
48061
48237
|
if (ext === ".json") {
|
|
48062
48238
|
return parseJsonSpec(content);
|
|
48063
48239
|
}
|
|
@@ -48128,7 +48304,7 @@ function extractRoutes(cwd) {
|
|
|
48128
48304
|
return;
|
|
48129
48305
|
}
|
|
48130
48306
|
for (const entry of entries) {
|
|
48131
|
-
const fullPath =
|
|
48307
|
+
const fullPath = path29.join(dir, entry.name);
|
|
48132
48308
|
if (entry.isSymbolicLink()) {
|
|
48133
48309
|
continue;
|
|
48134
48310
|
}
|
|
@@ -48138,7 +48314,7 @@ function extractRoutes(cwd) {
|
|
|
48138
48314
|
}
|
|
48139
48315
|
walkDir(fullPath);
|
|
48140
48316
|
} else if (entry.isFile()) {
|
|
48141
|
-
const ext =
|
|
48317
|
+
const ext = path29.extname(entry.name).toLowerCase();
|
|
48142
48318
|
const baseName = entry.name.toLowerCase();
|
|
48143
48319
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
48144
48320
|
continue;
|
|
@@ -48307,7 +48483,7 @@ init_secretscan();
|
|
|
48307
48483
|
// src/tools/symbols.ts
|
|
48308
48484
|
init_tool();
|
|
48309
48485
|
import * as fs23 from "fs";
|
|
48310
|
-
import * as
|
|
48486
|
+
import * as path30 from "path";
|
|
48311
48487
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
48312
48488
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
48313
48489
|
function containsControlCharacters(str) {
|
|
@@ -48336,11 +48512,11 @@ function containsWindowsAttacks(str) {
|
|
|
48336
48512
|
}
|
|
48337
48513
|
function isPathInWorkspace(filePath, workspace) {
|
|
48338
48514
|
try {
|
|
48339
|
-
const resolvedPath =
|
|
48515
|
+
const resolvedPath = path30.resolve(workspace, filePath);
|
|
48340
48516
|
const realWorkspace = fs23.realpathSync(workspace);
|
|
48341
48517
|
const realResolvedPath = fs23.realpathSync(resolvedPath);
|
|
48342
|
-
const relativePath =
|
|
48343
|
-
if (relativePath.startsWith("..") ||
|
|
48518
|
+
const relativePath = path30.relative(realWorkspace, realResolvedPath);
|
|
48519
|
+
if (relativePath.startsWith("..") || path30.isAbsolute(relativePath)) {
|
|
48344
48520
|
return false;
|
|
48345
48521
|
}
|
|
48346
48522
|
return true;
|
|
@@ -48352,7 +48528,7 @@ function validatePathForRead(filePath, workspace) {
|
|
|
48352
48528
|
return isPathInWorkspace(filePath, workspace);
|
|
48353
48529
|
}
|
|
48354
48530
|
function extractTSSymbols(filePath, cwd) {
|
|
48355
|
-
const fullPath =
|
|
48531
|
+
const fullPath = path30.join(cwd, filePath);
|
|
48356
48532
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
48357
48533
|
return [];
|
|
48358
48534
|
}
|
|
@@ -48504,7 +48680,7 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
48504
48680
|
});
|
|
48505
48681
|
}
|
|
48506
48682
|
function extractPythonSymbols(filePath, cwd) {
|
|
48507
|
-
const fullPath =
|
|
48683
|
+
const fullPath = path30.join(cwd, filePath);
|
|
48508
48684
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
48509
48685
|
return [];
|
|
48510
48686
|
}
|
|
@@ -48586,7 +48762,7 @@ var symbols = tool({
|
|
|
48586
48762
|
}, null, 2);
|
|
48587
48763
|
}
|
|
48588
48764
|
const cwd = process.cwd();
|
|
48589
|
-
const ext =
|
|
48765
|
+
const ext = path30.extname(file3);
|
|
48590
48766
|
if (containsControlCharacters(file3)) {
|
|
48591
48767
|
return JSON.stringify({
|
|
48592
48768
|
file: file3,
|
|
@@ -48655,7 +48831,7 @@ init_test_runner();
|
|
|
48655
48831
|
// src/tools/todo-extract.ts
|
|
48656
48832
|
init_dist();
|
|
48657
48833
|
import * as fs24 from "fs";
|
|
48658
|
-
import * as
|
|
48834
|
+
import * as path31 from "path";
|
|
48659
48835
|
var MAX_TEXT_LENGTH = 200;
|
|
48660
48836
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
48661
48837
|
var SUPPORTED_EXTENSIONS2 = new Set([
|
|
@@ -48726,9 +48902,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
48726
48902
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
48727
48903
|
}
|
|
48728
48904
|
try {
|
|
48729
|
-
const resolvedPath =
|
|
48730
|
-
const normalizedCwd =
|
|
48731
|
-
const normalizedResolved =
|
|
48905
|
+
const resolvedPath = path31.resolve(paths);
|
|
48906
|
+
const normalizedCwd = path31.resolve(cwd);
|
|
48907
|
+
const normalizedResolved = path31.resolve(resolvedPath);
|
|
48732
48908
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
48733
48909
|
return {
|
|
48734
48910
|
error: "paths must be within the current working directory",
|
|
@@ -48744,7 +48920,7 @@ function validatePathsInput(paths, cwd) {
|
|
|
48744
48920
|
}
|
|
48745
48921
|
}
|
|
48746
48922
|
function isSupportedExtension(filePath) {
|
|
48747
|
-
const ext =
|
|
48923
|
+
const ext = path31.extname(filePath).toLowerCase();
|
|
48748
48924
|
return SUPPORTED_EXTENSIONS2.has(ext);
|
|
48749
48925
|
}
|
|
48750
48926
|
function findSourceFiles3(dir, files = []) {
|
|
@@ -48759,7 +48935,7 @@ function findSourceFiles3(dir, files = []) {
|
|
|
48759
48935
|
if (SKIP_DIRECTORIES3.has(entry)) {
|
|
48760
48936
|
continue;
|
|
48761
48937
|
}
|
|
48762
|
-
const fullPath =
|
|
48938
|
+
const fullPath = path31.join(dir, entry);
|
|
48763
48939
|
let stat;
|
|
48764
48940
|
try {
|
|
48765
48941
|
stat = fs24.statSync(fullPath);
|
|
@@ -48871,7 +49047,7 @@ var todo_extract = tool({
|
|
|
48871
49047
|
filesToScan.push(scanPath);
|
|
48872
49048
|
} else {
|
|
48873
49049
|
const errorResult = {
|
|
48874
|
-
error: `unsupported file extension: ${
|
|
49050
|
+
error: `unsupported file extension: ${path31.extname(scanPath)}`,
|
|
48875
49051
|
total: 0,
|
|
48876
49052
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
48877
49053
|
entries: []
|
|
@@ -48986,7 +49162,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
48986
49162
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
48987
49163
|
preflightTriggerManager = new PTM(automationConfig);
|
|
48988
49164
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
48989
|
-
const swarmDir =
|
|
49165
|
+
const swarmDir = path32.resolve(ctx.directory, ".swarm");
|
|
48990
49166
|
statusArtifact = new ASA(swarmDir);
|
|
48991
49167
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
48992
49168
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|
|
@@ -49092,6 +49268,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
49092
49268
|
phase_complete,
|
|
49093
49269
|
pre_check_batch,
|
|
49094
49270
|
retrieve_summary,
|
|
49271
|
+
save_plan,
|
|
49095
49272
|
schema_drift,
|
|
49096
49273
|
secretscan,
|
|
49097
49274
|
symbols,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export { fetchGitingest, type GitingestArgs, gitingest } from './gitingest';
|
|
|
9
9
|
export { imports } from './imports';
|
|
10
10
|
export { lint } from './lint';
|
|
11
11
|
export { phase_complete } from './phase-complete';
|
|
12
|
+
export { save_plan } from './save-plan';
|
|
13
|
+
export type { SavePlanArgs, SavePlanResult } from './save-plan';
|
|
12
14
|
export { pkg_audit } from './pkg-audit';
|
|
13
15
|
export { type PlaceholderFinding, type PlaceholderScanInput, type PlaceholderScanResult, placeholderScan, } from './placeholder-scan';
|
|
14
16
|
export { type PreCheckBatchInput, type PreCheckBatchResult, pre_check_batch, runPreCheckBatch, type ToolResult, } from './pre-check-batch';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Save plan tool for persisting validated implementation plans.
|
|
3
|
+
* Allows the Architect agent to save structured plans to .swarm/plan.json and .swarm/plan.md.
|
|
4
|
+
*/
|
|
5
|
+
import { type ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
6
|
+
/**
|
|
7
|
+
* Arguments for the save_plan tool
|
|
8
|
+
*/
|
|
9
|
+
export interface SavePlanArgs {
|
|
10
|
+
title: string;
|
|
11
|
+
swarm_id: string;
|
|
12
|
+
phases: Array<{
|
|
13
|
+
id: number;
|
|
14
|
+
name: string;
|
|
15
|
+
tasks: Array<{
|
|
16
|
+
id: string;
|
|
17
|
+
description: string;
|
|
18
|
+
size?: 'small' | 'medium' | 'large';
|
|
19
|
+
depends?: string[];
|
|
20
|
+
acceptance?: string;
|
|
21
|
+
}>;
|
|
22
|
+
}>;
|
|
23
|
+
working_directory?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Result from executing save_plan
|
|
27
|
+
*/
|
|
28
|
+
export interface SavePlanResult {
|
|
29
|
+
success: boolean;
|
|
30
|
+
message: string;
|
|
31
|
+
plan_path?: string;
|
|
32
|
+
phases_count?: number;
|
|
33
|
+
tasks_count?: number;
|
|
34
|
+
errors?: string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Detect template placeholder content (e.g., [task], [Project], [description], [N]).
|
|
38
|
+
* These patterns indicate the LLM reproduced template examples literally rather than
|
|
39
|
+
* filling in real content from the specification.
|
|
40
|
+
* @param args - The save plan arguments to validate
|
|
41
|
+
* @returns Array of issue strings describing found placeholders
|
|
42
|
+
*/
|
|
43
|
+
export declare function detectPlaceholderContent(args: SavePlanArgs): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Execute the save_plan tool.
|
|
46
|
+
* Validates for placeholder content, builds a Plan object, and saves to disk.
|
|
47
|
+
* @param args - The save plan arguments
|
|
48
|
+
* @returns SavePlanResult with success status and details
|
|
49
|
+
*/
|
|
50
|
+
export declare function executeSavePlan(args: SavePlanArgs): Promise<SavePlanResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Tool definition for save_plan
|
|
53
|
+
*/
|
|
54
|
+
export declare const save_plan: ToolDefinition;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Used for constants and agent setup references.
|
|
4
4
|
*/
|
|
5
5
|
/** Union type of all valid tool names */
|
|
6
|
-
export type ToolName = 'diff' | 'syntax_check' | 'placeholder_scan' | 'imports' | 'lint' | 'secretscan' | 'sast_scan' | 'build_check' | 'pre_check_batch' | 'quality_budget' | 'symbols' | 'complexity_hotspots' | 'schema_drift' | 'todo_extract' | 'evidence_check' | 'sbom_generate' | 'checkpoint' | 'pkg_audit' | 'test_runner' | 'detect_domains' | 'gitingest' | 'retrieve_summary' | 'extract_code_blocks' | 'phase_complete';
|
|
6
|
+
export type ToolName = 'diff' | 'syntax_check' | 'placeholder_scan' | 'imports' | 'lint' | 'secretscan' | 'sast_scan' | 'build_check' | 'pre_check_batch' | 'quality_budget' | 'symbols' | 'complexity_hotspots' | 'schema_drift' | 'todo_extract' | 'evidence_check' | 'sbom_generate' | 'checkpoint' | 'pkg_audit' | 'test_runner' | 'detect_domains' | 'gitingest' | 'retrieve_summary' | 'extract_code_blocks' | 'phase_complete' | 'save_plan';
|
|
7
7
|
/** Readonly array of all tool names */
|
|
8
8
|
export declare const TOOL_NAMES: readonly ToolName[];
|
|
9
9
|
/** Set for O(1) tool name validation */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.14.
|
|
3
|
+
"version": "6.14.11",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@biomejs/biome": "2.3.14",
|
|
57
57
|
"bun-types": "latest",
|
|
58
|
+
"js-yaml": "^4.1.1",
|
|
58
59
|
"typescript": "^5.7.3"
|
|
59
60
|
}
|
|
60
61
|
}
|