opencode-swarm 6.32.1 → 6.32.3
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/dist/cli/index.js
CHANGED
|
@@ -31619,8 +31619,11 @@ async function rewriteKnowledge(filePath, entries) {
|
|
|
31619
31619
|
` : "");
|
|
31620
31620
|
await writeFile(filePath, content, "utf-8");
|
|
31621
31621
|
} finally {
|
|
31622
|
-
if (release)
|
|
31623
|
-
|
|
31622
|
+
if (release) {
|
|
31623
|
+
try {
|
|
31624
|
+
await release();
|
|
31625
|
+
} catch {}
|
|
31626
|
+
}
|
|
31624
31627
|
}
|
|
31625
31628
|
}
|
|
31626
31629
|
function normalize2(text) {
|
|
@@ -33624,6 +33627,7 @@ async function handleExportCommand(directory, _args) {
|
|
|
33624
33627
|
}
|
|
33625
33628
|
// src/commands/handoff.ts
|
|
33626
33629
|
init_utils2();
|
|
33630
|
+
import crypto3 from "crypto";
|
|
33627
33631
|
import { renameSync as renameSync5 } from "fs";
|
|
33628
33632
|
|
|
33629
33633
|
// src/services/handoff-service.ts
|
|
@@ -34019,7 +34023,7 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
34019
34023
|
const handoffData = await getHandoffData(directory);
|
|
34020
34024
|
const markdown = formatHandoffMarkdown(handoffData);
|
|
34021
34025
|
const resolvedPath = validateSwarmPath(directory, "handoff.md");
|
|
34022
|
-
const tempPath = `${resolvedPath}.tmp.${
|
|
34026
|
+
const tempPath = `${resolvedPath}.tmp.${crypto3.randomUUID()}`;
|
|
34023
34027
|
await Bun.write(tempPath, markdown);
|
|
34024
34028
|
renameSync5(tempPath, resolvedPath);
|
|
34025
34029
|
await writeSnapshot(directory, swarmState);
|
|
@@ -32,3 +32,10 @@ export declare function detectAdversarialPatterns(text: string): AdversarialPatt
|
|
|
32
32
|
* Format a precedent manipulation detection event for JSONL emission.
|
|
33
33
|
*/
|
|
34
34
|
export declare function formatPrecedentManipulationEvent(match: AdversarialPatternMatch, agentName: string, phase: number): string;
|
|
35
|
+
export declare function formatDebuggingSpiralEvent(match: AdversarialPatternMatch, taskId: string): string;
|
|
36
|
+
export declare function handleDebuggingSpiral(match: AdversarialPatternMatch, taskId: string, directory: string): Promise<{
|
|
37
|
+
eventLogged: boolean;
|
|
38
|
+
checkpointCreated: boolean;
|
|
39
|
+
message: string;
|
|
40
|
+
}>;
|
|
41
|
+
export declare function detectDebuggingSpiral(_directory: string): Promise<AdversarialPatternMatch | null>;
|
|
@@ -11,7 +11,7 @@ import type { DelegationEnvelope, EnvelopeValidationResult } from '../types/dele
|
|
|
11
11
|
* Returns null if no valid envelope is found.
|
|
12
12
|
* Never throws - all errors are caught and result in null.
|
|
13
13
|
*/
|
|
14
|
-
export declare function parseDelegationEnvelope(content: string): DelegationEnvelope | null;
|
|
14
|
+
export declare function parseDelegationEnvelope(content: string, directory?: string): DelegationEnvelope | null;
|
|
15
15
|
interface ValidationContext {
|
|
16
16
|
planTasks: string[];
|
|
17
17
|
validAgents: string[];
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - Layer 2 (Hard Block @ 100%): Throws error in toolBefore to block further calls, injects STOP message
|
|
8
8
|
*/
|
|
9
9
|
import { type GuardrailsConfig } from '../config/schema';
|
|
10
|
+
import { type FileZone } from '../context/zone-classifier';
|
|
10
11
|
/**
|
|
11
12
|
* Retrieves stored input args for a given callID.
|
|
12
13
|
* Used by other hooks (e.g., delegation-gate) to access tool input args.
|
|
@@ -72,3 +73,43 @@ export declare function createGuardrailsHooks(directory: string, directoryOrConf
|
|
|
72
73
|
* @returns Numeric hash (0 if hashing fails)
|
|
73
74
|
*/
|
|
74
75
|
export declare function hashArgs(args: unknown): number;
|
|
76
|
+
/** A record of an agent attesting to (resolving/suppressing/deferring) a finding. */
|
|
77
|
+
export interface AttestationRecord {
|
|
78
|
+
findingId: string;
|
|
79
|
+
agent: string;
|
|
80
|
+
attestation: string;
|
|
81
|
+
action: 'resolve' | 'suppress' | 'defer';
|
|
82
|
+
timestamp: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validates that an attestation string meets the minimum length requirement.
|
|
86
|
+
*/
|
|
87
|
+
export declare function validateAttestation(attestation: string, _findingId: string, _agent: string, _action: 'resolve' | 'suppress' | 'defer'): {
|
|
88
|
+
valid: true;
|
|
89
|
+
} | {
|
|
90
|
+
valid: false;
|
|
91
|
+
reason: string;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Appends an attestation record to `.swarm/evidence/attestations.jsonl`.
|
|
95
|
+
*/
|
|
96
|
+
export declare function recordAttestation(dir: string, record: AttestationRecord): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Validates an attestation and, on success, records it; on failure, logs a rejection event.
|
|
99
|
+
*/
|
|
100
|
+
export declare function validateAndRecordAttestation(dir: string, findingId: string, agent: string, attestation: string, action: 'resolve' | 'suppress' | 'defer'): Promise<{
|
|
101
|
+
valid: true;
|
|
102
|
+
} | {
|
|
103
|
+
valid: false;
|
|
104
|
+
reason: string;
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Checks whether the given agent is authorised to write to the given file path.
|
|
108
|
+
*/
|
|
109
|
+
export declare function checkFileAuthority(agentName: string, filePath: string, _cwd: string): {
|
|
110
|
+
allowed: true;
|
|
111
|
+
} | {
|
|
112
|
+
allowed: false;
|
|
113
|
+
reason: string;
|
|
114
|
+
zone?: FileZone;
|
|
115
|
+
};
|
|
@@ -5,6 +5,7 @@ export interface MigrationResult {
|
|
|
5
5
|
entriesMigrated: number;
|
|
6
6
|
entriesDropped: number;
|
|
7
7
|
entriesTotal: number;
|
|
8
|
-
skippedReason?: 'sentinel-exists' | 'no-context-file' | 'empty-context';
|
|
8
|
+
skippedReason?: 'sentinel-exists' | 'no-context-file' | 'empty-context' | 'external-sentinel-exists';
|
|
9
9
|
}
|
|
10
|
+
export declare function migrateKnowledgeToExternal(_directory: string, _config: KnowledgeConfig): Promise<MigrationResult>;
|
|
10
11
|
export declare function migrateContextToKnowledge(directory: string, config: KnowledgeConfig): Promise<MigrationResult>;
|
package/dist/index.js
CHANGED
|
@@ -29876,6 +29876,285 @@ var init_create_tool = __esm(() => {
|
|
|
29876
29876
|
init_dist();
|
|
29877
29877
|
});
|
|
29878
29878
|
|
|
29879
|
+
// src/tools/checkpoint.ts
|
|
29880
|
+
import { spawnSync } from "child_process";
|
|
29881
|
+
import * as fs6 from "fs";
|
|
29882
|
+
import * as path9 from "path";
|
|
29883
|
+
function containsNonAsciiChars(label) {
|
|
29884
|
+
for (let i2 = 0;i2 < label.length; i2++) {
|
|
29885
|
+
const charCode = label.charCodeAt(i2);
|
|
29886
|
+
if (charCode < 32 || charCode > 126) {
|
|
29887
|
+
return true;
|
|
29888
|
+
}
|
|
29889
|
+
}
|
|
29890
|
+
return false;
|
|
29891
|
+
}
|
|
29892
|
+
function validateLabel(label) {
|
|
29893
|
+
if (!label || label.length === 0) {
|
|
29894
|
+
return "label is required";
|
|
29895
|
+
}
|
|
29896
|
+
if (label.length > MAX_LABEL_LENGTH) {
|
|
29897
|
+
return `label exceeds maximum length of ${MAX_LABEL_LENGTH}`;
|
|
29898
|
+
}
|
|
29899
|
+
if (label.startsWith("--")) {
|
|
29900
|
+
return 'label cannot start with "--" (git flag pattern)';
|
|
29901
|
+
}
|
|
29902
|
+
if (CONTROL_CHAR_PATTERN.test(label)) {
|
|
29903
|
+
return "label contains control characters";
|
|
29904
|
+
}
|
|
29905
|
+
if (NON_ASCII_PATTERN.test(label)) {
|
|
29906
|
+
return "label contains non-ASCII or invalid characters";
|
|
29907
|
+
}
|
|
29908
|
+
if (containsNonAsciiChars(label)) {
|
|
29909
|
+
return "label contains non-ASCII characters (must be printable ASCII only)";
|
|
29910
|
+
}
|
|
29911
|
+
if (SHELL_METACHARACTERS.test(label)) {
|
|
29912
|
+
return "label contains shell metacharacters";
|
|
29913
|
+
}
|
|
29914
|
+
if (!SAFE_LABEL_PATTERN.test(label)) {
|
|
29915
|
+
return "label contains invalid characters (use alphanumeric, hyphen, underscore, space)";
|
|
29916
|
+
}
|
|
29917
|
+
if (!/[a-zA-Z0-9_]/.test(label)) {
|
|
29918
|
+
return "label cannot be whitespace-only";
|
|
29919
|
+
}
|
|
29920
|
+
if (label.includes("..") || label.includes("/") || label.includes("\\")) {
|
|
29921
|
+
return "label contains path traversal sequence";
|
|
29922
|
+
}
|
|
29923
|
+
return null;
|
|
29924
|
+
}
|
|
29925
|
+
function getCheckpointLogPath(directory) {
|
|
29926
|
+
return path9.join(directory, CHECKPOINT_LOG_PATH);
|
|
29927
|
+
}
|
|
29928
|
+
function readCheckpointLog(directory) {
|
|
29929
|
+
const logPath = getCheckpointLogPath(directory);
|
|
29930
|
+
try {
|
|
29931
|
+
if (fs6.existsSync(logPath)) {
|
|
29932
|
+
const content = fs6.readFileSync(logPath, "utf-8");
|
|
29933
|
+
const parsed = JSON.parse(content);
|
|
29934
|
+
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
29935
|
+
return { version: 1, checkpoints: [] };
|
|
29936
|
+
}
|
|
29937
|
+
return parsed;
|
|
29938
|
+
}
|
|
29939
|
+
} catch {}
|
|
29940
|
+
return { version: 1, checkpoints: [] };
|
|
29941
|
+
}
|
|
29942
|
+
function writeCheckpointLog(log2, directory) {
|
|
29943
|
+
const logPath = getCheckpointLogPath(directory);
|
|
29944
|
+
const dir = path9.dirname(logPath);
|
|
29945
|
+
if (!fs6.existsSync(dir)) {
|
|
29946
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
29947
|
+
}
|
|
29948
|
+
const tempPath = `${logPath}.tmp`;
|
|
29949
|
+
fs6.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
|
|
29950
|
+
fs6.renameSync(tempPath, logPath);
|
|
29951
|
+
}
|
|
29952
|
+
function gitExec(args2) {
|
|
29953
|
+
const result = spawnSync("git", args2, {
|
|
29954
|
+
encoding: "utf-8",
|
|
29955
|
+
timeout: GIT_TIMEOUT_MS,
|
|
29956
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
29957
|
+
});
|
|
29958
|
+
if (result.status !== 0) {
|
|
29959
|
+
const err2 = new Error(result.stderr?.trim() || `git exited with code ${result.status}`);
|
|
29960
|
+
throw err2;
|
|
29961
|
+
}
|
|
29962
|
+
return result.stdout;
|
|
29963
|
+
}
|
|
29964
|
+
function getCurrentSha() {
|
|
29965
|
+
const output = gitExec(["rev-parse", "HEAD"]);
|
|
29966
|
+
return output.trim();
|
|
29967
|
+
}
|
|
29968
|
+
function isGitRepo() {
|
|
29969
|
+
try {
|
|
29970
|
+
gitExec(["rev-parse", "--git-dir"]);
|
|
29971
|
+
return true;
|
|
29972
|
+
} catch {
|
|
29973
|
+
return false;
|
|
29974
|
+
}
|
|
29975
|
+
}
|
|
29976
|
+
function handleSave(label, directory) {
|
|
29977
|
+
try {
|
|
29978
|
+
const log2 = readCheckpointLog(directory);
|
|
29979
|
+
const existingCheckpoint = log2.checkpoints.find((c) => c.label === label);
|
|
29980
|
+
if (existingCheckpoint) {
|
|
29981
|
+
return JSON.stringify({
|
|
29982
|
+
action: "save",
|
|
29983
|
+
success: false,
|
|
29984
|
+
error: `duplicate label: "${label}" already exists. Use a different label or delete the existing checkpoint first.`
|
|
29985
|
+
}, null, 2);
|
|
29986
|
+
}
|
|
29987
|
+
const _sha = getCurrentSha();
|
|
29988
|
+
const timestamp = new Date().toISOString();
|
|
29989
|
+
gitExec(["commit", "--allow-empty", "-m", `checkpoint: ${label}`]);
|
|
29990
|
+
const newSha = getCurrentSha();
|
|
29991
|
+
log2.checkpoints.push({
|
|
29992
|
+
label,
|
|
29993
|
+
sha: newSha,
|
|
29994
|
+
timestamp
|
|
29995
|
+
});
|
|
29996
|
+
writeCheckpointLog(log2, directory);
|
|
29997
|
+
return JSON.stringify({
|
|
29998
|
+
action: "save",
|
|
29999
|
+
success: true,
|
|
30000
|
+
label,
|
|
30001
|
+
sha: newSha,
|
|
30002
|
+
message: `Checkpoint saved: "${label}"`
|
|
30003
|
+
}, null, 2);
|
|
30004
|
+
} catch (e) {
|
|
30005
|
+
const errorMessage = e instanceof Error ? `save failed: ${e.message}` : "save failed: unknown error";
|
|
30006
|
+
return JSON.stringify({
|
|
30007
|
+
action: "save",
|
|
30008
|
+
success: false,
|
|
30009
|
+
error: errorMessage
|
|
30010
|
+
}, null, 2);
|
|
30011
|
+
}
|
|
30012
|
+
}
|
|
30013
|
+
function handleRestore(label, directory) {
|
|
30014
|
+
try {
|
|
30015
|
+
const log2 = readCheckpointLog(directory);
|
|
30016
|
+
const checkpoint = log2.checkpoints.find((c) => c.label === label);
|
|
30017
|
+
if (!checkpoint) {
|
|
30018
|
+
return JSON.stringify({
|
|
30019
|
+
action: "restore",
|
|
30020
|
+
success: false,
|
|
30021
|
+
error: `checkpoint not found: "${label}"`
|
|
30022
|
+
}, null, 2);
|
|
30023
|
+
}
|
|
30024
|
+
gitExec(["reset", "--soft", checkpoint.sha]);
|
|
30025
|
+
return JSON.stringify({
|
|
30026
|
+
action: "restore",
|
|
30027
|
+
success: true,
|
|
30028
|
+
label,
|
|
30029
|
+
sha: checkpoint.sha,
|
|
30030
|
+
message: `Restored to checkpoint: "${label}" (soft reset)`
|
|
30031
|
+
}, null, 2);
|
|
30032
|
+
} catch (e) {
|
|
30033
|
+
const errorMessage = e instanceof Error ? `restore failed: ${e.message}` : "restore failed: unknown error";
|
|
30034
|
+
return JSON.stringify({
|
|
30035
|
+
action: "restore",
|
|
30036
|
+
success: false,
|
|
30037
|
+
error: errorMessage
|
|
30038
|
+
}, null, 2);
|
|
30039
|
+
}
|
|
30040
|
+
}
|
|
30041
|
+
function handleList(directory) {
|
|
30042
|
+
const log2 = readCheckpointLog(directory);
|
|
30043
|
+
const sorted = [...log2.checkpoints].sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
30044
|
+
return JSON.stringify({
|
|
30045
|
+
action: "list",
|
|
30046
|
+
success: true,
|
|
30047
|
+
count: sorted.length,
|
|
30048
|
+
checkpoints: sorted
|
|
30049
|
+
}, null, 2);
|
|
30050
|
+
}
|
|
30051
|
+
function handleDelete(label, directory) {
|
|
30052
|
+
try {
|
|
30053
|
+
const log2 = readCheckpointLog(directory);
|
|
30054
|
+
const initialLength = log2.checkpoints.length;
|
|
30055
|
+
log2.checkpoints = log2.checkpoints.filter((c) => c.label !== label);
|
|
30056
|
+
if (log2.checkpoints.length === initialLength) {
|
|
30057
|
+
return JSON.stringify({
|
|
30058
|
+
action: "delete",
|
|
30059
|
+
success: false,
|
|
30060
|
+
error: `checkpoint not found: "${label}"`
|
|
30061
|
+
}, null, 2);
|
|
30062
|
+
}
|
|
30063
|
+
writeCheckpointLog(log2, directory);
|
|
30064
|
+
return JSON.stringify({
|
|
30065
|
+
action: "delete",
|
|
30066
|
+
success: true,
|
|
30067
|
+
label,
|
|
30068
|
+
message: `Checkpoint deleted: "${label}" (git commit preserved)`
|
|
30069
|
+
}, null, 2);
|
|
30070
|
+
} catch (e) {
|
|
30071
|
+
const errorMessage = e instanceof Error ? `delete failed: ${e.message}` : "delete failed: unknown error";
|
|
30072
|
+
return JSON.stringify({
|
|
30073
|
+
action: "delete",
|
|
30074
|
+
success: false,
|
|
30075
|
+
error: errorMessage
|
|
30076
|
+
}, null, 2);
|
|
30077
|
+
}
|
|
30078
|
+
}
|
|
30079
|
+
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json", MAX_LABEL_LENGTH = 100, GIT_TIMEOUT_MS = 30000, SHELL_METACHARACTERS, SAFE_LABEL_PATTERN, CONTROL_CHAR_PATTERN, NON_ASCII_PATTERN, checkpoint;
|
|
30080
|
+
var init_checkpoint = __esm(() => {
|
|
30081
|
+
init_tool();
|
|
30082
|
+
init_create_tool();
|
|
30083
|
+
SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
30084
|
+
SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
|
|
30085
|
+
CONTROL_CHAR_PATTERN = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/;
|
|
30086
|
+
NON_ASCII_PATTERN = /[^\x20-\x7E]/;
|
|
30087
|
+
checkpoint = createSwarmTool({
|
|
30088
|
+
description: "Save, restore, list, and delete git checkpoints. " + "Use save to create a named snapshot, restore to return to a checkpoint (soft reset), " + "list to see all checkpoints, and delete to remove a checkpoint from the log. " + "Git commits are preserved on delete.",
|
|
30089
|
+
args: {
|
|
30090
|
+
action: tool.schema.string().describe("Action to perform: save, restore, list, or delete"),
|
|
30091
|
+
label: tool.schema.string().optional().describe("Checkpoint label (required for save, restore, delete)")
|
|
30092
|
+
},
|
|
30093
|
+
execute: async (args2, directory) => {
|
|
30094
|
+
if (!isGitRepo()) {
|
|
30095
|
+
return JSON.stringify({
|
|
30096
|
+
action: "unknown",
|
|
30097
|
+
success: false,
|
|
30098
|
+
error: "not a git repository"
|
|
30099
|
+
}, null, 2);
|
|
30100
|
+
}
|
|
30101
|
+
let action;
|
|
30102
|
+
let label;
|
|
30103
|
+
try {
|
|
30104
|
+
action = String(args2.action);
|
|
30105
|
+
label = args2.label !== undefined ? String(args2.label) : undefined;
|
|
30106
|
+
} catch {
|
|
30107
|
+
return JSON.stringify({
|
|
30108
|
+
action: "unknown",
|
|
30109
|
+
success: false,
|
|
30110
|
+
error: "invalid arguments"
|
|
30111
|
+
}, null, 2);
|
|
30112
|
+
}
|
|
30113
|
+
const validActions = ["save", "restore", "list", "delete"];
|
|
30114
|
+
if (!validActions.includes(action)) {
|
|
30115
|
+
return JSON.stringify({
|
|
30116
|
+
action,
|
|
30117
|
+
success: false,
|
|
30118
|
+
error: `invalid action: "${action}". Valid actions: ${validActions.join(", ")}`
|
|
30119
|
+
}, null, 2);
|
|
30120
|
+
}
|
|
30121
|
+
if (["save", "restore", "delete"].includes(action)) {
|
|
30122
|
+
if (!label) {
|
|
30123
|
+
return JSON.stringify({
|
|
30124
|
+
action,
|
|
30125
|
+
success: false,
|
|
30126
|
+
error: `label is required for ${action} action`
|
|
30127
|
+
}, null, 2);
|
|
30128
|
+
}
|
|
30129
|
+
const labelError = validateLabel(label);
|
|
30130
|
+
if (labelError) {
|
|
30131
|
+
return JSON.stringify({
|
|
30132
|
+
action,
|
|
30133
|
+
success: false,
|
|
30134
|
+
error: `invalid label: ${labelError}`
|
|
30135
|
+
}, null, 2);
|
|
30136
|
+
}
|
|
30137
|
+
}
|
|
30138
|
+
switch (action) {
|
|
30139
|
+
case "save":
|
|
30140
|
+
return handleSave(label, directory);
|
|
30141
|
+
case "restore":
|
|
30142
|
+
return handleRestore(label, directory);
|
|
30143
|
+
case "list":
|
|
30144
|
+
return handleList(directory);
|
|
30145
|
+
case "delete":
|
|
30146
|
+
return handleDelete(label, directory);
|
|
30147
|
+
default:
|
|
30148
|
+
return JSON.stringify({
|
|
30149
|
+
action,
|
|
30150
|
+
success: false,
|
|
30151
|
+
error: "unreachable"
|
|
30152
|
+
}, null, 2);
|
|
30153
|
+
}
|
|
30154
|
+
}
|
|
30155
|
+
});
|
|
30156
|
+
});
|
|
30157
|
+
|
|
29879
30158
|
// node_modules/graceful-fs/polyfills.js
|
|
29880
30159
|
var require_polyfills = __commonJS((exports, module2) => {
|
|
29881
30160
|
var constants = __require("constants");
|
|
@@ -43511,286 +43790,8 @@ async function handleBenchmarkCommand(directory, args2) {
|
|
|
43511
43790
|
`);
|
|
43512
43791
|
}
|
|
43513
43792
|
|
|
43514
|
-
// src/tools/checkpoint.ts
|
|
43515
|
-
init_tool();
|
|
43516
|
-
init_create_tool();
|
|
43517
|
-
import { spawnSync } from "child_process";
|
|
43518
|
-
import * as fs6 from "fs";
|
|
43519
|
-
import * as path9 from "path";
|
|
43520
|
-
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
43521
|
-
var MAX_LABEL_LENGTH = 100;
|
|
43522
|
-
var GIT_TIMEOUT_MS = 30000;
|
|
43523
|
-
var SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
43524
|
-
var SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
|
|
43525
|
-
var CONTROL_CHAR_PATTERN = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/;
|
|
43526
|
-
var NON_ASCII_PATTERN = /[^\x20-\x7E]/;
|
|
43527
|
-
function containsNonAsciiChars(label) {
|
|
43528
|
-
for (let i2 = 0;i2 < label.length; i2++) {
|
|
43529
|
-
const charCode = label.charCodeAt(i2);
|
|
43530
|
-
if (charCode < 32 || charCode > 126) {
|
|
43531
|
-
return true;
|
|
43532
|
-
}
|
|
43533
|
-
}
|
|
43534
|
-
return false;
|
|
43535
|
-
}
|
|
43536
|
-
function validateLabel(label) {
|
|
43537
|
-
if (!label || label.length === 0) {
|
|
43538
|
-
return "label is required";
|
|
43539
|
-
}
|
|
43540
|
-
if (label.length > MAX_LABEL_LENGTH) {
|
|
43541
|
-
return `label exceeds maximum length of ${MAX_LABEL_LENGTH}`;
|
|
43542
|
-
}
|
|
43543
|
-
if (label.startsWith("--")) {
|
|
43544
|
-
return 'label cannot start with "--" (git flag pattern)';
|
|
43545
|
-
}
|
|
43546
|
-
if (CONTROL_CHAR_PATTERN.test(label)) {
|
|
43547
|
-
return "label contains control characters";
|
|
43548
|
-
}
|
|
43549
|
-
if (NON_ASCII_PATTERN.test(label)) {
|
|
43550
|
-
return "label contains non-ASCII or invalid characters";
|
|
43551
|
-
}
|
|
43552
|
-
if (containsNonAsciiChars(label)) {
|
|
43553
|
-
return "label contains non-ASCII characters (must be printable ASCII only)";
|
|
43554
|
-
}
|
|
43555
|
-
if (SHELL_METACHARACTERS.test(label)) {
|
|
43556
|
-
return "label contains shell metacharacters";
|
|
43557
|
-
}
|
|
43558
|
-
if (!SAFE_LABEL_PATTERN.test(label)) {
|
|
43559
|
-
return "label contains invalid characters (use alphanumeric, hyphen, underscore, space)";
|
|
43560
|
-
}
|
|
43561
|
-
if (!/[a-zA-Z0-9_]/.test(label)) {
|
|
43562
|
-
return "label cannot be whitespace-only";
|
|
43563
|
-
}
|
|
43564
|
-
if (label.includes("..") || label.includes("/") || label.includes("\\")) {
|
|
43565
|
-
return "label contains path traversal sequence";
|
|
43566
|
-
}
|
|
43567
|
-
return null;
|
|
43568
|
-
}
|
|
43569
|
-
function getCheckpointLogPath(directory) {
|
|
43570
|
-
return path9.join(directory, CHECKPOINT_LOG_PATH);
|
|
43571
|
-
}
|
|
43572
|
-
function readCheckpointLog(directory) {
|
|
43573
|
-
const logPath = getCheckpointLogPath(directory);
|
|
43574
|
-
try {
|
|
43575
|
-
if (fs6.existsSync(logPath)) {
|
|
43576
|
-
const content = fs6.readFileSync(logPath, "utf-8");
|
|
43577
|
-
const parsed = JSON.parse(content);
|
|
43578
|
-
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
43579
|
-
return { version: 1, checkpoints: [] };
|
|
43580
|
-
}
|
|
43581
|
-
return parsed;
|
|
43582
|
-
}
|
|
43583
|
-
} catch {}
|
|
43584
|
-
return { version: 1, checkpoints: [] };
|
|
43585
|
-
}
|
|
43586
|
-
function writeCheckpointLog(log2, directory) {
|
|
43587
|
-
const logPath = getCheckpointLogPath(directory);
|
|
43588
|
-
const dir = path9.dirname(logPath);
|
|
43589
|
-
if (!fs6.existsSync(dir)) {
|
|
43590
|
-
fs6.mkdirSync(dir, { recursive: true });
|
|
43591
|
-
}
|
|
43592
|
-
const tempPath = `${logPath}.tmp`;
|
|
43593
|
-
fs6.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
|
|
43594
|
-
fs6.renameSync(tempPath, logPath);
|
|
43595
|
-
}
|
|
43596
|
-
function gitExec(args2) {
|
|
43597
|
-
const result = spawnSync("git", args2, {
|
|
43598
|
-
encoding: "utf-8",
|
|
43599
|
-
timeout: GIT_TIMEOUT_MS,
|
|
43600
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
43601
|
-
});
|
|
43602
|
-
if (result.status !== 0) {
|
|
43603
|
-
const err2 = new Error(result.stderr?.trim() || `git exited with code ${result.status}`);
|
|
43604
|
-
throw err2;
|
|
43605
|
-
}
|
|
43606
|
-
return result.stdout;
|
|
43607
|
-
}
|
|
43608
|
-
function getCurrentSha() {
|
|
43609
|
-
const output = gitExec(["rev-parse", "HEAD"]);
|
|
43610
|
-
return output.trim();
|
|
43611
|
-
}
|
|
43612
|
-
function isGitRepo() {
|
|
43613
|
-
try {
|
|
43614
|
-
gitExec(["rev-parse", "--git-dir"]);
|
|
43615
|
-
return true;
|
|
43616
|
-
} catch {
|
|
43617
|
-
return false;
|
|
43618
|
-
}
|
|
43619
|
-
}
|
|
43620
|
-
function handleSave(label, directory) {
|
|
43621
|
-
try {
|
|
43622
|
-
const log2 = readCheckpointLog(directory);
|
|
43623
|
-
const existingCheckpoint = log2.checkpoints.find((c) => c.label === label);
|
|
43624
|
-
if (existingCheckpoint) {
|
|
43625
|
-
return JSON.stringify({
|
|
43626
|
-
action: "save",
|
|
43627
|
-
success: false,
|
|
43628
|
-
error: `duplicate label: "${label}" already exists. Use a different label or delete the existing checkpoint first.`
|
|
43629
|
-
}, null, 2);
|
|
43630
|
-
}
|
|
43631
|
-
const _sha = getCurrentSha();
|
|
43632
|
-
const timestamp = new Date().toISOString();
|
|
43633
|
-
gitExec(["commit", "--allow-empty", "-m", `checkpoint: ${label}`]);
|
|
43634
|
-
const newSha = getCurrentSha();
|
|
43635
|
-
log2.checkpoints.push({
|
|
43636
|
-
label,
|
|
43637
|
-
sha: newSha,
|
|
43638
|
-
timestamp
|
|
43639
|
-
});
|
|
43640
|
-
writeCheckpointLog(log2, directory);
|
|
43641
|
-
return JSON.stringify({
|
|
43642
|
-
action: "save",
|
|
43643
|
-
success: true,
|
|
43644
|
-
label,
|
|
43645
|
-
sha: newSha,
|
|
43646
|
-
message: `Checkpoint saved: "${label}"`
|
|
43647
|
-
}, null, 2);
|
|
43648
|
-
} catch (e) {
|
|
43649
|
-
const errorMessage = e instanceof Error ? `save failed: ${e.message}` : "save failed: unknown error";
|
|
43650
|
-
return JSON.stringify({
|
|
43651
|
-
action: "save",
|
|
43652
|
-
success: false,
|
|
43653
|
-
error: errorMessage
|
|
43654
|
-
}, null, 2);
|
|
43655
|
-
}
|
|
43656
|
-
}
|
|
43657
|
-
function handleRestore(label, directory) {
|
|
43658
|
-
try {
|
|
43659
|
-
const log2 = readCheckpointLog(directory);
|
|
43660
|
-
const checkpoint = log2.checkpoints.find((c) => c.label === label);
|
|
43661
|
-
if (!checkpoint) {
|
|
43662
|
-
return JSON.stringify({
|
|
43663
|
-
action: "restore",
|
|
43664
|
-
success: false,
|
|
43665
|
-
error: `checkpoint not found: "${label}"`
|
|
43666
|
-
}, null, 2);
|
|
43667
|
-
}
|
|
43668
|
-
gitExec(["reset", "--soft", checkpoint.sha]);
|
|
43669
|
-
return JSON.stringify({
|
|
43670
|
-
action: "restore",
|
|
43671
|
-
success: true,
|
|
43672
|
-
label,
|
|
43673
|
-
sha: checkpoint.sha,
|
|
43674
|
-
message: `Restored to checkpoint: "${label}" (soft reset)`
|
|
43675
|
-
}, null, 2);
|
|
43676
|
-
} catch (e) {
|
|
43677
|
-
const errorMessage = e instanceof Error ? `restore failed: ${e.message}` : "restore failed: unknown error";
|
|
43678
|
-
return JSON.stringify({
|
|
43679
|
-
action: "restore",
|
|
43680
|
-
success: false,
|
|
43681
|
-
error: errorMessage
|
|
43682
|
-
}, null, 2);
|
|
43683
|
-
}
|
|
43684
|
-
}
|
|
43685
|
-
function handleList(directory) {
|
|
43686
|
-
const log2 = readCheckpointLog(directory);
|
|
43687
|
-
const sorted = [...log2.checkpoints].sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
43688
|
-
return JSON.stringify({
|
|
43689
|
-
action: "list",
|
|
43690
|
-
success: true,
|
|
43691
|
-
count: sorted.length,
|
|
43692
|
-
checkpoints: sorted
|
|
43693
|
-
}, null, 2);
|
|
43694
|
-
}
|
|
43695
|
-
function handleDelete(label, directory) {
|
|
43696
|
-
try {
|
|
43697
|
-
const log2 = readCheckpointLog(directory);
|
|
43698
|
-
const initialLength = log2.checkpoints.length;
|
|
43699
|
-
log2.checkpoints = log2.checkpoints.filter((c) => c.label !== label);
|
|
43700
|
-
if (log2.checkpoints.length === initialLength) {
|
|
43701
|
-
return JSON.stringify({
|
|
43702
|
-
action: "delete",
|
|
43703
|
-
success: false,
|
|
43704
|
-
error: `checkpoint not found: "${label}"`
|
|
43705
|
-
}, null, 2);
|
|
43706
|
-
}
|
|
43707
|
-
writeCheckpointLog(log2, directory);
|
|
43708
|
-
return JSON.stringify({
|
|
43709
|
-
action: "delete",
|
|
43710
|
-
success: true,
|
|
43711
|
-
label,
|
|
43712
|
-
message: `Checkpoint deleted: "${label}" (git commit preserved)`
|
|
43713
|
-
}, null, 2);
|
|
43714
|
-
} catch (e) {
|
|
43715
|
-
const errorMessage = e instanceof Error ? `delete failed: ${e.message}` : "delete failed: unknown error";
|
|
43716
|
-
return JSON.stringify({
|
|
43717
|
-
action: "delete",
|
|
43718
|
-
success: false,
|
|
43719
|
-
error: errorMessage
|
|
43720
|
-
}, null, 2);
|
|
43721
|
-
}
|
|
43722
|
-
}
|
|
43723
|
-
var checkpoint = createSwarmTool({
|
|
43724
|
-
description: "Save, restore, list, and delete git checkpoints. " + "Use save to create a named snapshot, restore to return to a checkpoint (soft reset), " + "list to see all checkpoints, and delete to remove a checkpoint from the log. " + "Git commits are preserved on delete.",
|
|
43725
|
-
args: {
|
|
43726
|
-
action: tool.schema.string().describe("Action to perform: save, restore, list, or delete"),
|
|
43727
|
-
label: tool.schema.string().optional().describe("Checkpoint label (required for save, restore, delete)")
|
|
43728
|
-
},
|
|
43729
|
-
execute: async (args2, directory) => {
|
|
43730
|
-
if (!isGitRepo()) {
|
|
43731
|
-
return JSON.stringify({
|
|
43732
|
-
action: "unknown",
|
|
43733
|
-
success: false,
|
|
43734
|
-
error: "not a git repository"
|
|
43735
|
-
}, null, 2);
|
|
43736
|
-
}
|
|
43737
|
-
let action;
|
|
43738
|
-
let label;
|
|
43739
|
-
try {
|
|
43740
|
-
action = String(args2.action);
|
|
43741
|
-
label = args2.label !== undefined ? String(args2.label) : undefined;
|
|
43742
|
-
} catch {
|
|
43743
|
-
return JSON.stringify({
|
|
43744
|
-
action: "unknown",
|
|
43745
|
-
success: false,
|
|
43746
|
-
error: "invalid arguments"
|
|
43747
|
-
}, null, 2);
|
|
43748
|
-
}
|
|
43749
|
-
const validActions = ["save", "restore", "list", "delete"];
|
|
43750
|
-
if (!validActions.includes(action)) {
|
|
43751
|
-
return JSON.stringify({
|
|
43752
|
-
action,
|
|
43753
|
-
success: false,
|
|
43754
|
-
error: `invalid action: "${action}". Valid actions: ${validActions.join(", ")}`
|
|
43755
|
-
}, null, 2);
|
|
43756
|
-
}
|
|
43757
|
-
if (["save", "restore", "delete"].includes(action)) {
|
|
43758
|
-
if (!label) {
|
|
43759
|
-
return JSON.stringify({
|
|
43760
|
-
action,
|
|
43761
|
-
success: false,
|
|
43762
|
-
error: `label is required for ${action} action`
|
|
43763
|
-
}, null, 2);
|
|
43764
|
-
}
|
|
43765
|
-
const labelError = validateLabel(label);
|
|
43766
|
-
if (labelError) {
|
|
43767
|
-
return JSON.stringify({
|
|
43768
|
-
action,
|
|
43769
|
-
success: false,
|
|
43770
|
-
error: `invalid label: ${labelError}`
|
|
43771
|
-
}, null, 2);
|
|
43772
|
-
}
|
|
43773
|
-
}
|
|
43774
|
-
switch (action) {
|
|
43775
|
-
case "save":
|
|
43776
|
-
return handleSave(label, directory);
|
|
43777
|
-
case "restore":
|
|
43778
|
-
return handleRestore(label, directory);
|
|
43779
|
-
case "list":
|
|
43780
|
-
return handleList(directory);
|
|
43781
|
-
case "delete":
|
|
43782
|
-
return handleDelete(label, directory);
|
|
43783
|
-
default:
|
|
43784
|
-
return JSON.stringify({
|
|
43785
|
-
action,
|
|
43786
|
-
success: false,
|
|
43787
|
-
error: "unreachable"
|
|
43788
|
-
}, null, 2);
|
|
43789
|
-
}
|
|
43790
|
-
}
|
|
43791
|
-
});
|
|
43792
|
-
|
|
43793
43793
|
// src/commands/checkpoint.ts
|
|
43794
|
+
init_checkpoint();
|
|
43794
43795
|
async function handleCheckpointCommand(directory, args2) {
|
|
43795
43796
|
const subcommand = args2[0] || "list";
|
|
43796
43797
|
const label = args2[1];
|
|
@@ -44008,8 +44009,11 @@ async function rewriteKnowledge(filePath, entries) {
|
|
|
44008
44009
|
` : "");
|
|
44009
44010
|
await writeFile(filePath, content, "utf-8");
|
|
44010
44011
|
} finally {
|
|
44011
|
-
if (release)
|
|
44012
|
-
|
|
44012
|
+
if (release) {
|
|
44013
|
+
try {
|
|
44014
|
+
await release();
|
|
44015
|
+
} catch {}
|
|
44016
|
+
}
|
|
44013
44017
|
}
|
|
44014
44018
|
}
|
|
44015
44019
|
async function appendRejectedLesson(directory, lesson) {
|
|
@@ -46466,6 +46470,7 @@ async function handleExportCommand(directory, _args) {
|
|
|
46466
46470
|
}
|
|
46467
46471
|
// src/commands/handoff.ts
|
|
46468
46472
|
init_utils2();
|
|
46473
|
+
import crypto3 from "crypto";
|
|
46469
46474
|
import { renameSync as renameSync5 } from "fs";
|
|
46470
46475
|
|
|
46471
46476
|
// src/services/handoff-service.ts
|
|
@@ -46868,7 +46873,7 @@ async function handleHandoffCommand(directory, _args) {
|
|
|
46868
46873
|
const handoffData = await getHandoffData(directory);
|
|
46869
46874
|
const markdown = formatHandoffMarkdown(handoffData);
|
|
46870
46875
|
const resolvedPath = validateSwarmPath(directory, "handoff.md");
|
|
46871
|
-
const tempPath = `${resolvedPath}.tmp.${
|
|
46876
|
+
const tempPath = `${resolvedPath}.tmp.${crypto3.randomUUID()}`;
|
|
46872
46877
|
await Bun.write(tempPath, markdown);
|
|
46873
46878
|
renameSync5(tempPath, resolvedPath);
|
|
46874
46879
|
await writeSnapshot(directory, swarmState);
|
|
@@ -49749,8 +49754,8 @@ import * as path31 from "path";
|
|
|
49749
49754
|
// src/hooks/guardrails.ts
|
|
49750
49755
|
init_constants();
|
|
49751
49756
|
init_schema();
|
|
49752
|
-
init_manager2();
|
|
49753
49757
|
import * as path29 from "path";
|
|
49758
|
+
init_manager2();
|
|
49754
49759
|
init_utils();
|
|
49755
49760
|
|
|
49756
49761
|
// src/hooks/loop-detector.ts
|
|
@@ -49932,6 +49937,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
49932
49937
|
} else {
|
|
49933
49938
|
guardrailsConfig = config3;
|
|
49934
49939
|
}
|
|
49940
|
+
const effectiveDirectory = typeof directory === "string" ? directory : process.cwd();
|
|
49935
49941
|
if (guardrailsConfig?.enabled === false) {
|
|
49936
49942
|
return {
|
|
49937
49943
|
toolBefore: async () => {},
|
|
@@ -50025,9 +50031,9 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
50025
50031
|
const args2 = output.args;
|
|
50026
50032
|
const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
|
|
50027
50033
|
if (typeof targetPath === "string" && targetPath.length > 0) {
|
|
50028
|
-
const resolvedTarget = path29.resolve(
|
|
50029
|
-
const planMdPath = path29.resolve(
|
|
50030
|
-
const planJsonPath = path29.resolve(
|
|
50034
|
+
const resolvedTarget = path29.resolve(effectiveDirectory, targetPath).toLowerCase();
|
|
50035
|
+
const planMdPath = path29.resolve(effectiveDirectory, ".swarm", "plan.md").toLowerCase();
|
|
50036
|
+
const planJsonPath = path29.resolve(effectiveDirectory, ".swarm", "plan.json").toLowerCase();
|
|
50031
50037
|
if (resolvedTarget === planMdPath || resolvedTarget === planJsonPath) {
|
|
50032
50038
|
throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use update_task_status() to mark tasks complete, " + "phase_complete() for phase transitions, or " + "save_plan to create/restructure plans.");
|
|
50033
50039
|
}
|
|
@@ -50076,13 +50082,13 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
50076
50082
|
}
|
|
50077
50083
|
}
|
|
50078
50084
|
for (const p of paths) {
|
|
50079
|
-
const resolvedP = path29.resolve(
|
|
50080
|
-
const planMdPath = path29.resolve(
|
|
50081
|
-
const planJsonPath = path29.resolve(
|
|
50085
|
+
const resolvedP = path29.resolve(effectiveDirectory, p);
|
|
50086
|
+
const planMdPath = path29.resolve(effectiveDirectory, ".swarm", "plan.md").toLowerCase();
|
|
50087
|
+
const planJsonPath = path29.resolve(effectiveDirectory, ".swarm", "plan.json").toLowerCase();
|
|
50082
50088
|
if (resolvedP.toLowerCase() === planMdPath || resolvedP.toLowerCase() === planJsonPath) {
|
|
50083
50089
|
throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use update_task_status() to mark tasks complete, " + "phase_complete() for phase transitions, or " + "save_plan to create/restructure plans.");
|
|
50084
50090
|
}
|
|
50085
|
-
if (isOutsideSwarmDir(p,
|
|
50091
|
+
if (isOutsideSwarmDir(p, effectiveDirectory) && (isSourceCodePath(p) || hasTraversalSegments(p))) {
|
|
50086
50092
|
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
50087
50093
|
if (session2) {
|
|
50088
50094
|
session2.architectWriteCount++;
|
|
@@ -50098,7 +50104,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
50098
50104
|
}
|
|
50099
50105
|
}
|
|
50100
50106
|
}
|
|
50101
|
-
if (typeof targetPath === "string" && targetPath.length > 0 && isOutsideSwarmDir(targetPath,
|
|
50107
|
+
if (typeof targetPath === "string" && targetPath.length > 0 && isOutsideSwarmDir(targetPath, effectiveDirectory) && isSourceCodePath(path29.relative(effectiveDirectory, path29.resolve(effectiveDirectory, targetPath)))) {
|
|
50102
50108
|
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
50103
50109
|
if (session2) {
|
|
50104
50110
|
session2.architectWriteCount++;
|
|
@@ -50297,7 +50303,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
50297
50303
|
if (delegation.isDelegation && (delegation.targetAgent === "reviewer" || delegation.targetAgent === "test_engineer")) {
|
|
50298
50304
|
let currentPhase = 1;
|
|
50299
50305
|
try {
|
|
50300
|
-
const plan = await loadPlan(
|
|
50306
|
+
const plan = await loadPlan(effectiveDirectory);
|
|
50301
50307
|
if (plan) {
|
|
50302
50308
|
const phaseString = extractCurrentPhaseFromPlan2(plan);
|
|
50303
50309
|
currentPhase = extractPhaseNumber(phaseString);
|
|
@@ -50470,7 +50476,7 @@ ${textPart2.text}`;
|
|
|
50470
50476
|
}
|
|
50471
50477
|
let currentPhaseForCheck = 1;
|
|
50472
50478
|
try {
|
|
50473
|
-
const plan = await loadPlan(
|
|
50479
|
+
const plan = await loadPlan(effectiveDirectory);
|
|
50474
50480
|
if (plan) {
|
|
50475
50481
|
const phaseString = extractCurrentPhaseFromPlan2(plan);
|
|
50476
50482
|
currentPhaseForCheck = extractPhaseNumber(phaseString);
|
|
@@ -50534,7 +50540,7 @@ ${textPart2.text}`;
|
|
|
50534
50540
|
}
|
|
50535
50541
|
if (isArchitectSessionForGates && session && session.catastrophicPhaseWarnings && requireReviewerAndTestEngineer) {
|
|
50536
50542
|
try {
|
|
50537
|
-
const plan = await loadPlan(
|
|
50543
|
+
const plan = await loadPlan(effectiveDirectory);
|
|
50538
50544
|
if (plan?.phases) {
|
|
50539
50545
|
for (const phase of plan.phases) {
|
|
50540
50546
|
if (phase.status === "complete") {
|
|
@@ -50604,6 +50610,7 @@ function hashArgs(args2) {
|
|
|
50604
50610
|
}
|
|
50605
50611
|
|
|
50606
50612
|
// src/hooks/delegation-gate.ts
|
|
50613
|
+
init_utils2();
|
|
50607
50614
|
function extractTaskLine(text) {
|
|
50608
50615
|
const match = text.match(/TASK:\s*(.+?)(?:\n|$)/i);
|
|
50609
50616
|
return match ? match[1].trim() : null;
|
|
@@ -55131,6 +55138,10 @@ var check_gate_status = createSwarmTool({
|
|
|
55131
55138
|
return JSON.stringify(result, null, 2);
|
|
55132
55139
|
}
|
|
55133
55140
|
});
|
|
55141
|
+
|
|
55142
|
+
// src/tools/index.ts
|
|
55143
|
+
init_checkpoint();
|
|
55144
|
+
|
|
55134
55145
|
// src/tools/complexity-hotspots.ts
|
|
55135
55146
|
init_dist();
|
|
55136
55147
|
init_create_tool();
|
|
@@ -57378,10 +57389,11 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
57378
57389
|
const phase = Number(args2.phase);
|
|
57379
57390
|
const summary = args2.summary;
|
|
57380
57391
|
const sessionID = args2.sessionID;
|
|
57381
|
-
if (Number.isNaN(phase) || phase < 1) {
|
|
57392
|
+
if (Number.isNaN(phase) || phase < 1 || !Number.isFinite(phase) || !Number.isInteger(phase)) {
|
|
57382
57393
|
return JSON.stringify({
|
|
57383
57394
|
success: false,
|
|
57384
57395
|
phase,
|
|
57396
|
+
status: "blocked",
|
|
57385
57397
|
message: "Invalid phase number",
|
|
57386
57398
|
agentsDispatched: [],
|
|
57387
57399
|
warnings: ["Phase must be a positive number"]
|
|
@@ -57449,7 +57461,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
57449
57461
|
}
|
|
57450
57462
|
if (!retroFound) {
|
|
57451
57463
|
const allTaskIds = await listEvidenceTaskIds(dir);
|
|
57452
|
-
const retroTaskIds = allTaskIds.filter((id) => id.startsWith("retro-"));
|
|
57464
|
+
const retroTaskIds = allTaskIds.filter((id) => id.startsWith("retro-") && /^retro-\d+$/.test(id));
|
|
57453
57465
|
for (const taskId of retroTaskIds) {
|
|
57454
57466
|
const bundleResult = await loadEvidence(dir, taskId);
|
|
57455
57467
|
if (bundleResult.status !== "found") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.32.
|
|
3
|
+
"version": "6.32.3",
|
|
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",
|