iterate-plugin 2.9.1 → 2.9.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/config-loader.js +61 -2
- package/dist/tools/checkpoint.js +10 -10
- package/dist/tools/config.js +7 -7
- package/dist/tools/context.js +3 -3
- package/dist/tools/decision-log.js +3 -3
- package/dist/tools/fix.js +7 -7
- package/dist/tools/history.js +3 -3
- package/dist/tools/prune.js +3 -3
- package/dist/tools/review.js +4 -4
- package/dist/tools/triage.js +5 -5
- package/dist/tools/validate.js +3 -3
- package/package.json +1 -1
- package/src/config-loader.ts +61 -2
- package/src/tools/checkpoint.ts +10 -10
- package/src/tools/config.ts +7 -7
- package/src/tools/context.ts +3 -3
- package/src/tools/decision-log.ts +3 -3
- package/src/tools/fix.ts +7 -7
- package/src/tools/history.ts +3 -3
- package/src/tools/prune.ts +3 -3
- package/src/tools/review.ts +4 -4
- package/src/tools/triage.ts +5 -5
- package/src/tools/validate.ts +3 -3
package/dist/config-loader.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
2
3
|
import { join, resolve, sep } from 'node:path';
|
|
3
4
|
import yaml from 'js-yaml';
|
|
4
5
|
/**
|
|
@@ -166,11 +167,69 @@ export function validateConfig(config) {
|
|
|
166
167
|
* path is unsafe; callers must short-circuit on the failure and return a
|
|
167
168
|
* structured error instead of proceeding.
|
|
168
169
|
*/
|
|
169
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Resolve a caller-supplied project root to a safe absolute path.
|
|
172
|
+
*
|
|
173
|
+
* Resolution order for the default (no explicit `path`) case:
|
|
174
|
+
* 1. `sessionCwd` — the absolute working directory the calling DSH session
|
|
175
|
+
* was created in (`exec.agent.session.header.cwd`). This is the
|
|
176
|
+
* authoritative workspace for the current conversation and is immune to
|
|
177
|
+
* where the web-service process happened to start.
|
|
178
|
+
* 2. the process cwd, when it is a usable directory (not `/` or the home
|
|
179
|
+
* dir — launchd/daemon-managed servers start with cwd=`/`);
|
|
180
|
+
* 3. the session workspace decoded from `DSH_SESSION_JSONL` (present when
|
|
181
|
+
* the DSH runtime injects per-session env into tool sub-processes).
|
|
182
|
+
*/
|
|
183
|
+
export function resolveProjectRoot(input, sessionCwd) {
|
|
170
184
|
const raw = (input ?? '').trim();
|
|
171
|
-
const root = raw ? resolve(raw) : resolve(
|
|
185
|
+
const root = raw ? resolve(raw) : resolve(effectiveCwd(sessionCwd));
|
|
172
186
|
if (!root || root === sep) {
|
|
173
187
|
return { ok: false, reason: 'Refusing filesystem root as project root.' };
|
|
174
188
|
}
|
|
175
189
|
return { ok: true, root };
|
|
176
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Thin adapter for tool `execute(args, exec)` bodies: pull the session cwd
|
|
193
|
+
* from the DSH run context and hand it to {@link resolveProjectRoot}.
|
|
194
|
+
*/
|
|
195
|
+
export function resolveProjectRootForExec(exec, input) {
|
|
196
|
+
return resolveProjectRoot(input, exec?.agent?.session?.header?.cwd);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Resolve the default working directory for tools invoked without an explicit
|
|
200
|
+
* `path`. Prefers the caller-provided session cwd, then the process cwd, then
|
|
201
|
+
* the session workspace encoded in `DSH_SESSION_JSONL`
|
|
202
|
+
* (`…/sessions/<encoded-workspace>/<session-id>/session.jsonl.zstd`), where
|
|
203
|
+
* the workspace directory is `--`-wrapped with `/` → `-` and percent-encoded
|
|
204
|
+
* bytes spelled as `~<hex>` (e.g. `/Volumes/Eng-Dev/iterate-skill` →
|
|
205
|
+
* `--Volumes-Eng-Dev-iterate-skill--`).
|
|
206
|
+
*/
|
|
207
|
+
function effectiveCwd(sessionCwd) {
|
|
208
|
+
if (sessionCwd && sessionCwd !== sep && sessionCwd !== homedir())
|
|
209
|
+
return sessionCwd;
|
|
210
|
+
let cwd = '';
|
|
211
|
+
try {
|
|
212
|
+
cwd = process.cwd();
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// cwd may be unreadable (deleted dir) — fall through to session workspace
|
|
216
|
+
}
|
|
217
|
+
if (cwd && cwd !== sep && cwd !== homedir())
|
|
218
|
+
return cwd;
|
|
219
|
+
const session = process.env.DSH_SESSION_JSONL;
|
|
220
|
+
if (session) {
|
|
221
|
+
const m = session.match(/\/sessions\/([^/]+)\//);
|
|
222
|
+
const encoded = m ? m[1] : undefined;
|
|
223
|
+
if (encoded && encoded.startsWith('--') && encoded.endsWith('--')) {
|
|
224
|
+
try {
|
|
225
|
+
const decoded = decodeURIComponent(encoded.slice(2, -2).replace(/~/g, '%'));
|
|
226
|
+
if (decoded && decoded.startsWith(sep))
|
|
227
|
+
return decoded;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// malformed encoding — fall through to cwd
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return cwd || sep;
|
|
235
|
+
}
|
package/dist/tools/checkpoint.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
12
12
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
13
|
-
import {
|
|
13
|
+
import { resolveProjectRootForExec } from "../config-loader.js";
|
|
14
14
|
import { checkpointPath, iterateDir } from "../paths.js";
|
|
15
15
|
import { readRegistry } from "./fix.js";
|
|
16
16
|
import { readDecisionEntries } from "./decision-log.js";
|
|
@@ -142,14 +142,14 @@ export function registerCheckpointTool(ctx) {
|
|
|
142
142
|
{ type: 'text', text: JSON.stringify(value, null, 2) },
|
|
143
143
|
],
|
|
144
144
|
},
|
|
145
|
-
async execute(args) {
|
|
146
|
-
const resolved =
|
|
145
|
+
async execute(args, exec) {
|
|
146
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
147
147
|
if (!resolved.ok)
|
|
148
148
|
return { operation: args.operation, ok: false, error: resolved.reason };
|
|
149
149
|
const projectRoot = resolved.root;
|
|
150
150
|
if (args.operation === 'load') {
|
|
151
151
|
const checkpoint = readCheckpoint(projectRoot);
|
|
152
|
-
return { operation: 'load', ok: true, checkpoint: checkpoint
|
|
152
|
+
return { operation: 'load', ok: true, checkpoint: checkpoint };
|
|
153
153
|
}
|
|
154
154
|
if (args.operation === 'clear') {
|
|
155
155
|
const existed = existsSync(checkpointPath(projectRoot));
|
|
@@ -217,7 +217,7 @@ export function registerStatusTool(ctx) {
|
|
|
217
217
|
additionalProperties: false,
|
|
218
218
|
properties: {
|
|
219
219
|
ok: { type: 'boolean', required: true },
|
|
220
|
-
mode: { type: 'string' },
|
|
220
|
+
mode: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
221
221
|
currentRound: { type: 'integer' },
|
|
222
222
|
totalRounds: { type: 'integer' },
|
|
223
223
|
fixedCount: { type: 'integer' },
|
|
@@ -227,7 +227,7 @@ export function registerStatusTool(ctx) {
|
|
|
227
227
|
hasCheckpoint: { type: 'boolean' },
|
|
228
228
|
interrupted: { type: 'boolean', description: 'True when a checkpoint exists, meaning the previous run was interrupted before finishing.' },
|
|
229
229
|
resumeCount: { type: 'integer', description: 'How many times the current checkpoint has already been resumed.' },
|
|
230
|
-
lastUpdated: { type: 'string' },
|
|
230
|
+
lastUpdated: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
231
231
|
error: { type: 'string' },
|
|
232
232
|
},
|
|
233
233
|
},
|
|
@@ -246,8 +246,8 @@ export function registerStatusTool(ctx) {
|
|
|
246
246
|
return [{ type: 'text', text: lines.filter(Boolean).join('\n') }];
|
|
247
247
|
},
|
|
248
248
|
},
|
|
249
|
-
async execute(args) {
|
|
250
|
-
const resolved =
|
|
249
|
+
async execute(args, exec) {
|
|
250
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
251
251
|
if (!resolved.ok)
|
|
252
252
|
return { ok: false, error: resolved.reason };
|
|
253
253
|
const projectRoot = resolved.root;
|
|
@@ -258,7 +258,7 @@ export function registerStatusTool(ctx) {
|
|
|
258
258
|
});
|
|
259
259
|
return {
|
|
260
260
|
ok: true,
|
|
261
|
-
mode: status.mode ??
|
|
261
|
+
mode: status.mode ?? null,
|
|
262
262
|
currentRound: status.currentRound,
|
|
263
263
|
totalRounds: status.totalRounds,
|
|
264
264
|
fixedCount: status.fixedCount,
|
|
@@ -268,7 +268,7 @@ export function registerStatusTool(ctx) {
|
|
|
268
268
|
hasCheckpoint: status.hasCheckpoint,
|
|
269
269
|
interrupted: status.interrupted,
|
|
270
270
|
resumeCount: status.resumeCount,
|
|
271
|
-
lastUpdated: status.lastUpdated ??
|
|
271
|
+
lastUpdated: status.lastUpdated ?? null,
|
|
272
272
|
};
|
|
273
273
|
},
|
|
274
274
|
}));
|
package/dist/tools/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
3
|
-
import { loadEffectiveConfig, validateConfig,
|
|
3
|
+
import { loadEffectiveConfig, validateConfig, resolveProjectRootForExec } from "../config-loader.js";
|
|
4
4
|
import { applyConfigUpdates, readRawConfig, validateConfigUpdates, writeConfigFile, } from "../config-write.js";
|
|
5
5
|
/**
|
|
6
6
|
* Register the `iterate_config` tool.
|
|
@@ -47,14 +47,14 @@ export function registerConfigTool(ctx) {
|
|
|
47
47
|
properties: {
|
|
48
48
|
found: { type: 'boolean', required: true },
|
|
49
49
|
valid: { type: 'boolean' },
|
|
50
|
-
errors: { type: 'array', items: { type: 'string' } },
|
|
50
|
+
errors: { oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'null' }] },
|
|
51
51
|
section: { type: 'string' },
|
|
52
52
|
data: { type: 'json' },
|
|
53
53
|
config: { type: 'json' },
|
|
54
54
|
availableSections: { type: 'array', items: { type: 'string' } },
|
|
55
55
|
operation: { type: 'string' },
|
|
56
56
|
ok: { type: 'boolean' },
|
|
57
|
-
backupPath: { type: 'string' },
|
|
57
|
+
backupPath: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
58
58
|
error: { type: 'string' },
|
|
59
59
|
},
|
|
60
60
|
},
|
|
@@ -62,8 +62,8 @@ export function registerConfigTool(ctx) {
|
|
|
62
62
|
{ type: 'text', text: JSON.stringify(value, null, 2) },
|
|
63
63
|
],
|
|
64
64
|
},
|
|
65
|
-
async execute(args) {
|
|
66
|
-
const resolved =
|
|
65
|
+
async execute(args, exec) {
|
|
66
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
67
67
|
if (!resolved.ok) {
|
|
68
68
|
return { found: false, error: resolved.reason };
|
|
69
69
|
}
|
|
@@ -100,7 +100,7 @@ export function registerConfigTool(ctx) {
|
|
|
100
100
|
operation: 'write',
|
|
101
101
|
ok: true,
|
|
102
102
|
found: true,
|
|
103
|
-
backupPath: result.backupPath ??
|
|
103
|
+
backupPath: result.backupPath ?? null,
|
|
104
104
|
config: config,
|
|
105
105
|
};
|
|
106
106
|
}
|
|
@@ -112,7 +112,7 @@ export function registerConfigTool(ctx) {
|
|
|
112
112
|
return {
|
|
113
113
|
found: hasOverride,
|
|
114
114
|
valid: errors.length === 0,
|
|
115
|
-
errors: errors.length > 0 ? errors :
|
|
115
|
+
errors: errors.length > 0 ? errors : null,
|
|
116
116
|
section: 'validation_report',
|
|
117
117
|
};
|
|
118
118
|
}
|
package/dist/tools/context.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
2
2
|
import { join, dirname, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
5
|
-
import {
|
|
5
|
+
import { resolveProjectRootForExec } from "../config-loader.js";
|
|
6
6
|
/** How many ancestor directories we walk up looking for a SKILL.md. */
|
|
7
7
|
const MAX_SKILL_DIR_LOOKUP_DEPTH = 12;
|
|
8
8
|
/** Maximum number of image attachments relayed into the context in one call. */
|
|
@@ -235,8 +235,8 @@ export function registerContextTool(ctx) {
|
|
|
235
235
|
return [{ type: 'text', text: parts.join('\n\n') }];
|
|
236
236
|
},
|
|
237
237
|
},
|
|
238
|
-
async execute(args) {
|
|
239
|
-
const resolved =
|
|
238
|
+
async execute(args, exec) {
|
|
239
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
240
240
|
if (!resolved.ok) {
|
|
241
241
|
return { found: false, error: resolved.reason, searched: [] };
|
|
242
242
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { appendFileSync, readFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
4
|
-
import {
|
|
4
|
+
import { resolveProjectRootForExec } from "../config-loader.js";
|
|
5
5
|
const LOG_DIR = '.iterate';
|
|
6
6
|
const LOG_FILE = 'decision-log.jsonl';
|
|
7
7
|
/** All valid DecisionLogEntry `type` values (must stay in sync with Types). */
|
|
@@ -147,8 +147,8 @@ export function registerDecisionLogTool(ctx) {
|
|
|
147
147
|
{ type: 'text', text: JSON.stringify(value, null, 2) },
|
|
148
148
|
],
|
|
149
149
|
},
|
|
150
|
-
async execute(args) {
|
|
151
|
-
const resolved =
|
|
150
|
+
async execute(args, exec) {
|
|
151
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
152
152
|
if (!resolved.ok) {
|
|
153
153
|
return { operation: args.operation, error: resolved.reason };
|
|
154
154
|
}
|
package/dist/tools/fix.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
22
|
-
import { loadEffectiveConfig,
|
|
22
|
+
import { loadEffectiveConfig, resolveProjectRootForExec } from "../config-loader.js";
|
|
23
23
|
import { countTouchedMethods } from "../method-scope.js";
|
|
24
24
|
import { fixBackupPath, fixRegistryPath, fixesDir } from "../paths.js";
|
|
25
25
|
import { appendDecisionEntry } from "./decision-log.js";
|
|
@@ -272,8 +272,8 @@ export function registerFixTool(ctx) {
|
|
|
272
272
|
{ type: 'text', text: value.ok ? `${value.diffSummary ?? 'fixed'} @ ${value.file} (id: ${value.id})` : `fix failed: ${value.error}` },
|
|
273
273
|
],
|
|
274
274
|
},
|
|
275
|
-
async execute(args) {
|
|
276
|
-
const resolved =
|
|
275
|
+
async execute(args, exec) {
|
|
276
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
277
277
|
if (!resolved.ok)
|
|
278
278
|
return { ok: false, error: resolved.reason };
|
|
279
279
|
const projectRoot = resolved.root;
|
|
@@ -433,8 +433,8 @@ export function registerDiffTool(ctx) {
|
|
|
433
433
|
return [{ type: 'text', text }];
|
|
434
434
|
},
|
|
435
435
|
},
|
|
436
|
-
async execute(args) {
|
|
437
|
-
const resolved =
|
|
436
|
+
async execute(args, exec) {
|
|
437
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
438
438
|
if (!resolved.ok)
|
|
439
439
|
return { ok: false, error: resolved.reason };
|
|
440
440
|
const projectRoot = resolved.root;
|
|
@@ -520,8 +520,8 @@ export function registerRollbackTool(ctx) {
|
|
|
520
520
|
{ type: 'text', text: value.ok ? `reverted fix ${value.id} in ${value.file}` : `rollback failed: ${value.error}` },
|
|
521
521
|
],
|
|
522
522
|
},
|
|
523
|
-
async execute(args) {
|
|
524
|
-
const resolved =
|
|
523
|
+
async execute(args, exec) {
|
|
524
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
525
525
|
if (!resolved.ok)
|
|
526
526
|
return { ok: false, error: resolved.reason };
|
|
527
527
|
const projectRoot = resolved.root;
|
package/dist/tools/history.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Complements `iterate_status` (compact summary) with the actual detail.
|
|
9
9
|
*/
|
|
10
10
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
11
|
-
import {
|
|
11
|
+
import { resolveProjectRootForExec } from "../config-loader.js";
|
|
12
12
|
import { readDecisionEntries } from "./decision-log.js";
|
|
13
13
|
import { readRegistry } from "./fix.js";
|
|
14
14
|
const DEFAULT_LIMIT = 50;
|
|
@@ -117,8 +117,8 @@ export function registerHistoryTool(ctx) {
|
|
|
117
117
|
return [{ type: 'text', text: lines.join('\n') }];
|
|
118
118
|
},
|
|
119
119
|
},
|
|
120
|
-
async execute(args) {
|
|
121
|
-
const resolved =
|
|
120
|
+
async execute(args, exec) {
|
|
121
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
122
122
|
if (!resolved.ok)
|
|
123
123
|
return { ok: false, kind: 'history', error: resolved.reason };
|
|
124
124
|
const projectRoot = resolved.root;
|
package/dist/tools/prune.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { existsSync, readdirSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
23
|
-
import {
|
|
23
|
+
import { resolveProjectRootForExec } from "../config-loader.js";
|
|
24
24
|
import { readDecisionEntries, appendDecisionEntry } from "./decision-log.js";
|
|
25
25
|
import { readRegistry, removeRecord, recomputeRoundCounts } from "./fix.js";
|
|
26
26
|
import { iterateDir, fixesDir, checkpointPath, fixRegistryPath } from "../paths.js";
|
|
@@ -225,8 +225,8 @@ export function registerPruneTool(ctx) {
|
|
|
225
225
|
return [{ type: 'text', text: lines.join('\n') }];
|
|
226
226
|
},
|
|
227
227
|
},
|
|
228
|
-
async execute(args) {
|
|
229
|
-
const resolved =
|
|
228
|
+
async execute(args, exec) {
|
|
229
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
230
230
|
if (!resolved.ok)
|
|
231
231
|
return { ok: false, dryRun: true, error: resolved.reason };
|
|
232
232
|
const projectRoot = resolved.root;
|
package/dist/tools/review.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
-
import { loadEffectiveConfig,
|
|
2
|
+
import { loadEffectiveConfig, resolveProjectRootForExec } from "../config-loader.js";
|
|
3
3
|
import { buildReviewPlan, buildReviewReport, sanitizeRounds, validateRoundsSchema, } from "../review.js";
|
|
4
4
|
import { buildFinalReviewReport, metaReviewReport } from "../meta-review.js";
|
|
5
5
|
import { evidenceToPlain, verifyFindings } from "../evidence.js";
|
|
@@ -106,8 +106,8 @@ export function registerReviewTool(ctx) {
|
|
|
106
106
|
{ type: 'text', text: JSON.stringify(value, null, 2) },
|
|
107
107
|
],
|
|
108
108
|
},
|
|
109
|
-
async execute(args) {
|
|
110
|
-
const resolved =
|
|
109
|
+
async execute(args, exec) {
|
|
110
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
111
111
|
if (!resolved.ok) {
|
|
112
112
|
return { operation: args.operation, error: resolved.reason };
|
|
113
113
|
}
|
|
@@ -182,7 +182,7 @@ export function registerReviewTool(ctx) {
|
|
|
182
182
|
operation: 'aggregate',
|
|
183
183
|
mode,
|
|
184
184
|
report: report,
|
|
185
|
-
schemaValidation: schemaValidation,
|
|
185
|
+
schemaValidation: (schemaValidation ?? null),
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
188
|
if (args.operation === 'meta-review') {
|
package/dist/tools/triage.js
CHANGED
|
@@ -2,7 +2,7 @@ import { copyFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
4
4
|
import yaml from 'js-yaml';
|
|
5
|
-
import {
|
|
5
|
+
import { resolveProjectRootForExec } from "../config-loader.js";
|
|
6
6
|
const CONFIG_FILE = 'iterate.config.yaml';
|
|
7
7
|
/** Personalization key that holds the known-intentional list. */
|
|
8
8
|
const PERSONALIZATION_KEY = 'personalization';
|
|
@@ -262,7 +262,7 @@ export function registerTriageTool(ctx) {
|
|
|
262
262
|
skipped: { type: 'integer' },
|
|
263
263
|
count: { type: 'integer' },
|
|
264
264
|
path: { type: 'string' },
|
|
265
|
-
backupPath: { type: 'string' },
|
|
265
|
+
backupPath: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
266
266
|
entries: { type: 'json' },
|
|
267
267
|
errors: { type: 'array', items: { type: 'string' } },
|
|
268
268
|
error: { type: 'string' },
|
|
@@ -272,8 +272,8 @@ export function registerTriageTool(ctx) {
|
|
|
272
272
|
{ type: 'text', text: JSON.stringify(value, null, 2) },
|
|
273
273
|
],
|
|
274
274
|
},
|
|
275
|
-
async execute(args) {
|
|
276
|
-
const resolved =
|
|
275
|
+
async execute(args, exec) {
|
|
276
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
277
277
|
if (!resolved.ok) {
|
|
278
278
|
return { operation: args.operation, error: resolved.reason };
|
|
279
279
|
}
|
|
@@ -321,7 +321,7 @@ export function registerTriageTool(ctx) {
|
|
|
321
321
|
skipped: result.skipped,
|
|
322
322
|
count: result.count,
|
|
323
323
|
path: result.configPath,
|
|
324
|
-
backupPath: result.backupPath ??
|
|
324
|
+
backupPath: result.backupPath ?? null,
|
|
325
325
|
};
|
|
326
326
|
}
|
|
327
327
|
return {
|
package/dist/tools/validate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { exec } from 'node:child_process';
|
|
2
2
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
3
|
-
import { loadEffectiveConfig, isCommandAllowed, flattenCommands,
|
|
3
|
+
import { loadEffectiveConfig, isCommandAllowed, flattenCommands, resolveProjectRootForExec, } from "../config-loader.js";
|
|
4
4
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
5
5
|
/** Hard ceiling on a single validation command's runtime, so a model cannot
|
|
6
6
|
* pin the tool open indefinitely via an unbounded `timeout` argument. */
|
|
@@ -103,8 +103,8 @@ export function registerValidateTool(ctx) {
|
|
|
103
103
|
},
|
|
104
104
|
],
|
|
105
105
|
},
|
|
106
|
-
async execute(args) {
|
|
107
|
-
const resolved =
|
|
106
|
+
async execute(args, exec) {
|
|
107
|
+
const resolved = resolveProjectRootForExec(exec, args.path);
|
|
108
108
|
if (!resolved.ok) {
|
|
109
109
|
return {
|
|
110
110
|
allowed: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iterate-plugin",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.3",
|
|
4
4
|
"description": "dsh plugin that turns the iterate skill into an autonomous closed-loop harness: plan -> parallel review xN -> atomic fixes -> validate -> loop -> auto-stop, plus a dry-run pure-review mode with multi-round convergence and a meta-review that audits the report and emits a final review report.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/config-loader.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { homedir } from 'node:os'
|
|
2
3
|
import { join, resolve, sep } from 'node:path'
|
|
3
4
|
import yaml from 'js-yaml'
|
|
4
5
|
import type { IterateConfig } from './types.ts'
|
|
@@ -183,11 +184,69 @@ export type ProjectRootResult = { ok: true; root: string } | { ok: false; reason
|
|
|
183
184
|
* path is unsafe; callers must short-circuit on the failure and return a
|
|
184
185
|
* structured error instead of proceeding.
|
|
185
186
|
*/
|
|
186
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Resolve a caller-supplied project root to a safe absolute path.
|
|
189
|
+
*
|
|
190
|
+
* Resolution order for the default (no explicit `path`) case:
|
|
191
|
+
* 1. `sessionCwd` — the absolute working directory the calling DSH session
|
|
192
|
+
* was created in (`exec.agent.session.header.cwd`). This is the
|
|
193
|
+
* authoritative workspace for the current conversation and is immune to
|
|
194
|
+
* where the web-service process happened to start.
|
|
195
|
+
* 2. the process cwd, when it is a usable directory (not `/` or the home
|
|
196
|
+
* dir — launchd/daemon-managed servers start with cwd=`/`);
|
|
197
|
+
* 3. the session workspace decoded from `DSH_SESSION_JSONL` (present when
|
|
198
|
+
* the DSH runtime injects per-session env into tool sub-processes).
|
|
199
|
+
*/
|
|
200
|
+
export function resolveProjectRoot(input?: string, sessionCwd?: string): ProjectRootResult {
|
|
187
201
|
const raw = (input ?? '').trim()
|
|
188
|
-
const root = raw ? resolve(raw) : resolve(
|
|
202
|
+
const root = raw ? resolve(raw) : resolve(effectiveCwd(sessionCwd))
|
|
189
203
|
if (!root || root === sep) {
|
|
190
204
|
return { ok: false, reason: 'Refusing filesystem root as project root.' }
|
|
191
205
|
}
|
|
192
206
|
return { ok: true, root }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Thin adapter for tool `execute(args, exec)` bodies: pull the session cwd
|
|
211
|
+
* from the DSH run context and hand it to {@link resolveProjectRoot}.
|
|
212
|
+
*/
|
|
213
|
+
export function resolveProjectRootForExec(
|
|
214
|
+
exec: { agent?: { session?: { header?: { cwd?: string } } } } | undefined,
|
|
215
|
+
input?: string,
|
|
216
|
+
): ProjectRootResult {
|
|
217
|
+
return resolveProjectRoot(input, exec?.agent?.session?.header?.cwd)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Resolve the default working directory for tools invoked without an explicit
|
|
222
|
+
* `path`. Prefers the caller-provided session cwd, then the process cwd, then
|
|
223
|
+
* the session workspace encoded in `DSH_SESSION_JSONL`
|
|
224
|
+
* (`…/sessions/<encoded-workspace>/<session-id>/session.jsonl.zstd`), where
|
|
225
|
+
* the workspace directory is `--`-wrapped with `/` → `-` and percent-encoded
|
|
226
|
+
* bytes spelled as `~<hex>` (e.g. `/Volumes/Eng-Dev/iterate-skill` →
|
|
227
|
+
* `--Volumes-Eng-Dev-iterate-skill--`).
|
|
228
|
+
*/
|
|
229
|
+
function effectiveCwd(sessionCwd?: string): string {
|
|
230
|
+
if (sessionCwd && sessionCwd !== sep && sessionCwd !== homedir()) return sessionCwd
|
|
231
|
+
let cwd = ''
|
|
232
|
+
try {
|
|
233
|
+
cwd = process.cwd()
|
|
234
|
+
} catch {
|
|
235
|
+
// cwd may be unreadable (deleted dir) — fall through to session workspace
|
|
236
|
+
}
|
|
237
|
+
if (cwd && cwd !== sep && cwd !== homedir()) return cwd
|
|
238
|
+
const session = process.env.DSH_SESSION_JSONL
|
|
239
|
+
if (session) {
|
|
240
|
+
const m = session.match(/\/sessions\/([^/]+)\//)
|
|
241
|
+
const encoded = m ? m[1] : undefined
|
|
242
|
+
if (encoded && encoded.startsWith('--') && encoded.endsWith('--')) {
|
|
243
|
+
try {
|
|
244
|
+
const decoded = decodeURIComponent(encoded.slice(2, -2).replace(/~/g, '%'))
|
|
245
|
+
if (decoded && decoded.startsWith(sep)) return decoded
|
|
246
|
+
} catch {
|
|
247
|
+
// malformed encoding — fall through to cwd
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return cwd || sep
|
|
193
252
|
}
|
package/src/tools/checkpoint.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
13
13
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
14
14
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
15
|
-
import {
|
|
15
|
+
import { resolveProjectRootForExec } from '../config-loader.ts'
|
|
16
16
|
import { checkpointPath, iterateDir } from '../paths.ts'
|
|
17
17
|
import { readRegistry } from './fix.ts'
|
|
18
18
|
import { readDecisionEntries } from './decision-log.ts'
|
|
@@ -166,14 +166,14 @@ export function registerCheckpointTool(ctx: { tools: { register: (def: ReturnTyp
|
|
|
166
166
|
],
|
|
167
167
|
},
|
|
168
168
|
|
|
169
|
-
async execute(args) {
|
|
170
|
-
const resolved =
|
|
169
|
+
async execute(args, exec) {
|
|
170
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
171
171
|
if (!resolved.ok) return { operation: args.operation, ok: false, error: resolved.reason }
|
|
172
172
|
const projectRoot = resolved.root
|
|
173
173
|
|
|
174
174
|
if (args.operation === 'load') {
|
|
175
175
|
const checkpoint = readCheckpoint(projectRoot)
|
|
176
|
-
return { operation: 'load', ok: true, checkpoint:
|
|
176
|
+
return { operation: 'load', ok: true, checkpoint: checkpoint as unknown as JsonValue | null }
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
if (args.operation === 'clear') {
|
|
@@ -245,7 +245,7 @@ export function registerStatusTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
245
245
|
additionalProperties: false,
|
|
246
246
|
properties: {
|
|
247
247
|
ok: { type: 'boolean', required: true },
|
|
248
|
-
mode: { type: 'string' },
|
|
248
|
+
mode: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
249
249
|
currentRound: { type: 'integer' },
|
|
250
250
|
totalRounds: { type: 'integer' },
|
|
251
251
|
fixedCount: { type: 'integer' },
|
|
@@ -255,7 +255,7 @@ export function registerStatusTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
255
255
|
hasCheckpoint: { type: 'boolean' },
|
|
256
256
|
interrupted: { type: 'boolean', description: 'True when a checkpoint exists, meaning the previous run was interrupted before finishing.' },
|
|
257
257
|
resumeCount: { type: 'integer', description: 'How many times the current checkpoint has already been resumed.' },
|
|
258
|
-
lastUpdated: { type: 'string' },
|
|
258
|
+
lastUpdated: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
259
259
|
error: { type: 'string' },
|
|
260
260
|
},
|
|
261
261
|
},
|
|
@@ -274,8 +274,8 @@ export function registerStatusTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
274
274
|
},
|
|
275
275
|
},
|
|
276
276
|
|
|
277
|
-
async execute(args) {
|
|
278
|
-
const resolved =
|
|
277
|
+
async execute(args, exec) {
|
|
278
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
279
279
|
if (!resolved.ok) return { ok: false, error: resolved.reason }
|
|
280
280
|
const projectRoot = resolved.root
|
|
281
281
|
const status = computeStatus({
|
|
@@ -285,7 +285,7 @@ export function registerStatusTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
285
285
|
})
|
|
286
286
|
return {
|
|
287
287
|
ok: true,
|
|
288
|
-
mode: status.mode ??
|
|
288
|
+
mode: status.mode ?? null,
|
|
289
289
|
currentRound: status.currentRound,
|
|
290
290
|
totalRounds: status.totalRounds,
|
|
291
291
|
fixedCount: status.fixedCount,
|
|
@@ -295,7 +295,7 @@ export function registerStatusTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
295
295
|
hasCheckpoint: status.hasCheckpoint,
|
|
296
296
|
interrupted: status.interrupted,
|
|
297
297
|
resumeCount: status.resumeCount,
|
|
298
|
-
lastUpdated: status.lastUpdated ??
|
|
298
|
+
lastUpdated: status.lastUpdated ?? null,
|
|
299
299
|
}
|
|
300
300
|
},
|
|
301
301
|
}),
|
package/src/tools/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
2
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
3
3
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
4
|
-
import { loadEffectiveConfig, validateConfig,
|
|
4
|
+
import { loadEffectiveConfig, validateConfig, resolveProjectRootForExec } from '../config-loader.ts'
|
|
5
5
|
import {
|
|
6
6
|
applyConfigUpdates,
|
|
7
7
|
readRawConfig,
|
|
@@ -60,14 +60,14 @@ export function registerConfigTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
60
60
|
properties: {
|
|
61
61
|
found: { type: 'boolean', required: true },
|
|
62
62
|
valid: { type: 'boolean' },
|
|
63
|
-
errors: { type: 'array', items: { type: 'string' } },
|
|
63
|
+
errors: { oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'null' }] },
|
|
64
64
|
section: { type: 'string' },
|
|
65
65
|
data: { type: 'json' },
|
|
66
66
|
config: { type: 'json' },
|
|
67
67
|
availableSections: { type: 'array', items: { type: 'string' } },
|
|
68
68
|
operation: { type: 'string' },
|
|
69
69
|
ok: { type: 'boolean' },
|
|
70
|
-
backupPath: { type: 'string' },
|
|
70
|
+
backupPath: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
71
71
|
error: { type: 'string' },
|
|
72
72
|
},
|
|
73
73
|
},
|
|
@@ -76,8 +76,8 @@ export function registerConfigTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
76
76
|
],
|
|
77
77
|
},
|
|
78
78
|
|
|
79
|
-
async execute(args) {
|
|
80
|
-
const resolved =
|
|
79
|
+
async execute(args, exec) {
|
|
80
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
81
81
|
if (!resolved.ok) {
|
|
82
82
|
return { found: false, error: resolved.reason }
|
|
83
83
|
}
|
|
@@ -113,7 +113,7 @@ export function registerConfigTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
113
113
|
operation: 'write',
|
|
114
114
|
ok: true,
|
|
115
115
|
found: true,
|
|
116
|
-
backupPath: result.backupPath ??
|
|
116
|
+
backupPath: result.backupPath ?? null,
|
|
117
117
|
config: config as unknown as JsonValue,
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -127,7 +127,7 @@ export function registerConfigTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
127
127
|
return {
|
|
128
128
|
found: hasOverride,
|
|
129
129
|
valid: errors.length === 0,
|
|
130
|
-
errors: errors.length > 0 ? errors :
|
|
130
|
+
errors: errors.length > 0 ? errors : null,
|
|
131
131
|
section: 'validation_report',
|
|
132
132
|
}
|
|
133
133
|
}
|
package/src/tools/context.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync, existsSync } from 'node:fs'
|
|
|
2
2
|
import { join, dirname, resolve } from 'node:path'
|
|
3
3
|
import { fileURLToPath } from 'node:url'
|
|
4
4
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
5
|
-
import {
|
|
5
|
+
import { resolveProjectRootForExec } from '../config-loader.ts'
|
|
6
6
|
|
|
7
7
|
/** How many ancestor directories we walk up looking for a SKILL.md. */
|
|
8
8
|
const MAX_SKILL_DIR_LOOKUP_DEPTH = 12
|
|
@@ -257,8 +257,8 @@ export function registerContextTool(ctx: { tools: { register: (def: ReturnType<t
|
|
|
257
257
|
},
|
|
258
258
|
},
|
|
259
259
|
|
|
260
|
-
async execute(args) {
|
|
261
|
-
const resolved =
|
|
260
|
+
async execute(args, exec) {
|
|
261
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
262
262
|
if (!resolved.ok) {
|
|
263
263
|
return { found: false, error: resolved.reason, searched: [] }
|
|
264
264
|
}
|
|
@@ -2,7 +2,7 @@ import { appendFileSync, readFileSync, mkdirSync, existsSync } from 'node:fs'
|
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
4
4
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
5
|
-
import {
|
|
5
|
+
import { resolveProjectRootForExec } from '../config-loader.ts'
|
|
6
6
|
import type { DecisionLogEntry } from '../types.ts'
|
|
7
7
|
|
|
8
8
|
const LOG_DIR = '.iterate'
|
|
@@ -159,8 +159,8 @@ export function registerDecisionLogTool(ctx: { tools: { register: (def: ReturnTy
|
|
|
159
159
|
],
|
|
160
160
|
},
|
|
161
161
|
|
|
162
|
-
async execute(args) {
|
|
163
|
-
const resolved =
|
|
162
|
+
async execute(args, exec) {
|
|
163
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
164
164
|
if (!resolved.ok) {
|
|
165
165
|
return { operation: args.operation, error: resolved.reason }
|
|
166
166
|
}
|
package/src/tools/fix.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from
|
|
|
21
21
|
import { join } from 'node:path'
|
|
22
22
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
23
23
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
24
|
-
import { loadEffectiveConfig,
|
|
24
|
+
import { loadEffectiveConfig, resolveProjectRootForExec } from '../config-loader.ts'
|
|
25
25
|
import { countTouchedMethods } from '../method-scope.ts'
|
|
26
26
|
import { fixBackupPath, fixRegistryPath, fixesDir } from '../paths.ts'
|
|
27
27
|
import { appendDecisionEntry } from './decision-log.ts'
|
|
@@ -284,8 +284,8 @@ export function registerFixTool(ctx: { tools: { register: (def: ReturnType<typeo
|
|
|
284
284
|
],
|
|
285
285
|
},
|
|
286
286
|
|
|
287
|
-
async execute(args) {
|
|
288
|
-
const resolved =
|
|
287
|
+
async execute(args, exec) {
|
|
288
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
289
289
|
if (!resolved.ok) return { ok: false, error: resolved.reason }
|
|
290
290
|
const projectRoot = resolved.root
|
|
291
291
|
const { config } = loadEffectiveConfig(projectRoot)
|
|
@@ -454,8 +454,8 @@ export function registerDiffTool(ctx: { tools: { register: (def: ReturnType<type
|
|
|
454
454
|
},
|
|
455
455
|
},
|
|
456
456
|
|
|
457
|
-
async execute(args) {
|
|
458
|
-
const resolved =
|
|
457
|
+
async execute(args, exec) {
|
|
458
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
459
459
|
if (!resolved.ok) return { ok: false, error: resolved.reason }
|
|
460
460
|
const projectRoot = resolved.root
|
|
461
461
|
const registry = readRegistry(projectRoot)
|
|
@@ -544,8 +544,8 @@ export function registerRollbackTool(ctx: { tools: { register: (def: ReturnType<
|
|
|
544
544
|
],
|
|
545
545
|
},
|
|
546
546
|
|
|
547
|
-
async execute(args) {
|
|
548
|
-
const resolved =
|
|
547
|
+
async execute(args, exec) {
|
|
548
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
549
549
|
if (!resolved.ok) return { ok: false, error: resolved.reason }
|
|
550
550
|
const projectRoot = resolved.root
|
|
551
551
|
const id = typeof args.id === 'string' ? args.id : ''
|
package/src/tools/history.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
12
12
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
13
|
-
import {
|
|
13
|
+
import { resolveProjectRootForExec } from '../config-loader.ts'
|
|
14
14
|
import { readDecisionEntries } from './decision-log.ts'
|
|
15
15
|
import { readRegistry } from './fix.ts'
|
|
16
16
|
import type { DecisionLogEntry, FixRegistry } from '../types.ts'
|
|
@@ -136,8 +136,8 @@ export function registerHistoryTool(ctx: { tools: { register: (def: ReturnType<t
|
|
|
136
136
|
},
|
|
137
137
|
},
|
|
138
138
|
|
|
139
|
-
async execute(args) {
|
|
140
|
-
const resolved =
|
|
139
|
+
async execute(args, exec) {
|
|
140
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
141
141
|
if (!resolved.ok) return { ok: false, kind: 'history', error: resolved.reason }
|
|
142
142
|
const projectRoot = resolved.root
|
|
143
143
|
|
package/src/tools/prune.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { existsSync, readdirSync, rmSync, unlinkSync, writeFileSync } from 'node
|
|
|
22
22
|
import { join } from 'node:path'
|
|
23
23
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
24
24
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
25
|
-
import {
|
|
25
|
+
import { resolveProjectRootForExec } from '../config-loader.ts'
|
|
26
26
|
import { readDecisionEntries, appendDecisionEntry } from './decision-log.ts'
|
|
27
27
|
import { readRegistry, removeRecord, recomputeRoundCounts } from './fix.ts'
|
|
28
28
|
import { iterateDir, fixesDir, checkpointPath, fixRegistryPath } from '../paths.ts'
|
|
@@ -265,8 +265,8 @@ export function registerPruneTool(ctx: { tools: { register: (def: ReturnType<typ
|
|
|
265
265
|
},
|
|
266
266
|
},
|
|
267
267
|
|
|
268
|
-
async execute(args) {
|
|
269
|
-
const resolved =
|
|
268
|
+
async execute(args, exec) {
|
|
269
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
270
270
|
if (!resolved.ok) return { ok: false, dryRun: true, error: resolved.reason }
|
|
271
271
|
const projectRoot = resolved.root
|
|
272
272
|
const retainDays = clampRetainDays(args.retainDays as number | undefined)
|
package/src/tools/review.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
2
2
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
3
|
-
import { loadEffectiveConfig,
|
|
3
|
+
import { loadEffectiveConfig, resolveProjectRootForExec } from '../config-loader.ts'
|
|
4
4
|
import {
|
|
5
5
|
buildReviewPlan,
|
|
6
6
|
buildReviewReport,
|
|
@@ -131,8 +131,8 @@ export function registerReviewTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
131
131
|
],
|
|
132
132
|
},
|
|
133
133
|
|
|
134
|
-
async execute(args) {
|
|
135
|
-
const resolved =
|
|
134
|
+
async execute(args, exec) {
|
|
135
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
136
136
|
if (!resolved.ok) {
|
|
137
137
|
return { operation: args.operation, error: resolved.reason }
|
|
138
138
|
}
|
|
@@ -213,7 +213,7 @@ export function registerReviewTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
213
213
|
operation: 'aggregate',
|
|
214
214
|
mode,
|
|
215
215
|
report: report as unknown as JsonValue,
|
|
216
|
-
schemaValidation: schemaValidation as unknown as JsonValue |
|
|
216
|
+
schemaValidation: (schemaValidation ?? null) as unknown as JsonValue | null,
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
package/src/tools/triage.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { join } from 'node:path'
|
|
|
3
3
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
4
4
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
5
5
|
import yaml from 'js-yaml'
|
|
6
|
-
import {
|
|
6
|
+
import { resolveProjectRootForExec } from '../config-loader.ts'
|
|
7
7
|
import type { KnownIntentional } from '../types.ts'
|
|
8
8
|
|
|
9
9
|
const CONFIG_FILE = 'iterate.config.yaml'
|
|
@@ -302,7 +302,7 @@ export function registerTriageTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
302
302
|
skipped: { type: 'integer' },
|
|
303
303
|
count: { type: 'integer' },
|
|
304
304
|
path: { type: 'string' },
|
|
305
|
-
backupPath: { type: 'string' },
|
|
305
|
+
backupPath: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
|
306
306
|
entries: { type: 'json' },
|
|
307
307
|
errors: { type: 'array', items: { type: 'string' } },
|
|
308
308
|
error: { type: 'string' },
|
|
@@ -313,8 +313,8 @@ export function registerTriageTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
313
313
|
],
|
|
314
314
|
},
|
|
315
315
|
|
|
316
|
-
async execute(args) {
|
|
317
|
-
const resolved =
|
|
316
|
+
async execute(args, exec) {
|
|
317
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
318
318
|
if (!resolved.ok) {
|
|
319
319
|
return { operation: args.operation, error: resolved.reason }
|
|
320
320
|
}
|
|
@@ -363,7 +363,7 @@ export function registerTriageTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
363
363
|
skipped: result.skipped,
|
|
364
364
|
count: result.count,
|
|
365
365
|
path: result.configPath,
|
|
366
|
-
backupPath: result.backupPath ??
|
|
366
|
+
backupPath: result.backupPath ?? null,
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
369
|
|
package/src/tools/validate.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
loadEffectiveConfig,
|
|
5
5
|
isCommandAllowed,
|
|
6
6
|
flattenCommands,
|
|
7
|
-
|
|
7
|
+
resolveProjectRootForExec,
|
|
8
8
|
} from '../config-loader.ts'
|
|
9
9
|
import type { ValidationResult } from '../types.ts'
|
|
10
10
|
|
|
@@ -126,8 +126,8 @@ export function registerValidateTool(ctx: { tools: { register: (def: ReturnType<
|
|
|
126
126
|
],
|
|
127
127
|
},
|
|
128
128
|
|
|
129
|
-
async execute(args) {
|
|
130
|
-
const resolved =
|
|
129
|
+
async execute(args, exec) {
|
|
130
|
+
const resolved = resolveProjectRootForExec(exec, args.path)
|
|
131
131
|
if (!resolved.ok) {
|
|
132
132
|
return {
|
|
133
133
|
allowed: false,
|