opencode-swarm 6.32.1 → 6.32.2

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.
@@ -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>;
@@ -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];
@@ -49749,8 +49750,8 @@ import * as path31 from "path";
49749
49750
  // src/hooks/guardrails.ts
49750
49751
  init_constants();
49751
49752
  init_schema();
49752
- init_manager2();
49753
49753
  import * as path29 from "path";
49754
+ init_manager2();
49754
49755
  init_utils();
49755
49756
 
49756
49757
  // src/hooks/loop-detector.ts
@@ -49932,6 +49933,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
49932
49933
  } else {
49933
49934
  guardrailsConfig = config3;
49934
49935
  }
49936
+ const effectiveDirectory = typeof directory === "string" ? directory : process.cwd();
49935
49937
  if (guardrailsConfig?.enabled === false) {
49936
49938
  return {
49937
49939
  toolBefore: async () => {},
@@ -50025,9 +50027,9 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
50025
50027
  const args2 = output.args;
50026
50028
  const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
50027
50029
  if (typeof targetPath === "string" && targetPath.length > 0) {
50028
- const resolvedTarget = path29.resolve(directory, targetPath).toLowerCase();
50029
- const planMdPath = path29.resolve(directory, ".swarm", "plan.md").toLowerCase();
50030
- const planJsonPath = path29.resolve(directory, ".swarm", "plan.json").toLowerCase();
50030
+ const resolvedTarget = path29.resolve(effectiveDirectory, targetPath).toLowerCase();
50031
+ const planMdPath = path29.resolve(effectiveDirectory, ".swarm", "plan.md").toLowerCase();
50032
+ const planJsonPath = path29.resolve(effectiveDirectory, ".swarm", "plan.json").toLowerCase();
50031
50033
  if (resolvedTarget === planMdPath || resolvedTarget === planJsonPath) {
50032
50034
  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
50035
  }
@@ -50076,13 +50078,13 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
50076
50078
  }
50077
50079
  }
50078
50080
  for (const p of paths) {
50079
- const resolvedP = path29.resolve(directory, p);
50080
- const planMdPath = path29.resolve(directory, ".swarm", "plan.md").toLowerCase();
50081
- const planJsonPath = path29.resolve(directory, ".swarm", "plan.json").toLowerCase();
50081
+ const resolvedP = path29.resolve(effectiveDirectory, p);
50082
+ const planMdPath = path29.resolve(effectiveDirectory, ".swarm", "plan.md").toLowerCase();
50083
+ const planJsonPath = path29.resolve(effectiveDirectory, ".swarm", "plan.json").toLowerCase();
50082
50084
  if (resolvedP.toLowerCase() === planMdPath || resolvedP.toLowerCase() === planJsonPath) {
50083
50085
  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
50086
  }
50085
- if (isOutsideSwarmDir(p, directory) && (isSourceCodePath(p) || hasTraversalSegments(p))) {
50087
+ if (isOutsideSwarmDir(p, effectiveDirectory) && (isSourceCodePath(p) || hasTraversalSegments(p))) {
50086
50088
  const session2 = swarmState.agentSessions.get(input.sessionID);
50087
50089
  if (session2) {
50088
50090
  session2.architectWriteCount++;
@@ -50098,7 +50100,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
50098
50100
  }
50099
50101
  }
50100
50102
  }
50101
- if (typeof targetPath === "string" && targetPath.length > 0 && isOutsideSwarmDir(targetPath, directory) && isSourceCodePath(path29.relative(directory, path29.resolve(directory, targetPath)))) {
50103
+ if (typeof targetPath === "string" && targetPath.length > 0 && isOutsideSwarmDir(targetPath, effectiveDirectory) && isSourceCodePath(path29.relative(effectiveDirectory, path29.resolve(effectiveDirectory, targetPath)))) {
50102
50104
  const session2 = swarmState.agentSessions.get(input.sessionID);
50103
50105
  if (session2) {
50104
50106
  session2.architectWriteCount++;
@@ -50297,7 +50299,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
50297
50299
  if (delegation.isDelegation && (delegation.targetAgent === "reviewer" || delegation.targetAgent === "test_engineer")) {
50298
50300
  let currentPhase = 1;
50299
50301
  try {
50300
- const plan = await loadPlan(directory);
50302
+ const plan = await loadPlan(effectiveDirectory);
50301
50303
  if (plan) {
50302
50304
  const phaseString = extractCurrentPhaseFromPlan2(plan);
50303
50305
  currentPhase = extractPhaseNumber(phaseString);
@@ -50470,7 +50472,7 @@ ${textPart2.text}`;
50470
50472
  }
50471
50473
  let currentPhaseForCheck = 1;
50472
50474
  try {
50473
- const plan = await loadPlan(directory);
50475
+ const plan = await loadPlan(effectiveDirectory);
50474
50476
  if (plan) {
50475
50477
  const phaseString = extractCurrentPhaseFromPlan2(plan);
50476
50478
  currentPhaseForCheck = extractPhaseNumber(phaseString);
@@ -50534,7 +50536,7 @@ ${textPart2.text}`;
50534
50536
  }
50535
50537
  if (isArchitectSessionForGates && session && session.catastrophicPhaseWarnings && requireReviewerAndTestEngineer) {
50536
50538
  try {
50537
- const plan = await loadPlan(directory);
50539
+ const plan = await loadPlan(effectiveDirectory);
50538
50540
  if (plan?.phases) {
50539
50541
  for (const phase of plan.phases) {
50540
50542
  if (phase.status === "complete") {
@@ -55131,6 +55133,10 @@ var check_gate_status = createSwarmTool({
55131
55133
  return JSON.stringify(result, null, 2);
55132
55134
  }
55133
55135
  });
55136
+
55137
+ // src/tools/index.ts
55138
+ init_checkpoint();
55139
+
55134
55140
  // src/tools/complexity-hotspots.ts
55135
55141
  init_dist();
55136
55142
  init_create_tool();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "6.32.1",
3
+ "version": "6.32.2",
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",