svamp-cli 0.2.223-canary.2 → 0.2.223
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/{agentCommands-MGRUIvWB.mjs → agentCommands-Be_5WSKW.mjs} +5 -5
- package/dist/{auth-FvyDIAch.mjs → auth-CKjLWEcA.mjs} +1 -1
- package/dist/cli.mjs +61 -61
- package/dist/{commands-BuecUAk8.mjs → commands-B_XZmurW.mjs} +1 -1
- package/dist/{commands-Cmjb9Je1.mjs → commands-CK3ZbA0p.mjs} +6 -6
- package/dist/{commands-BiIkCdGB.mjs → commands-ClmIatXG.mjs} +1 -1
- package/dist/{commands-Dm3EQICq.mjs → commands-DGD5uSeq.mjs} +2 -2
- package/dist/{commands-_RXnsA1v.mjs → commands-DeBy9HEU.mjs} +1 -1
- package/dist/{commands-BhFr0kUL.mjs → commands-Dw2YesDl.mjs} +1 -1
- package/dist/{commands-nlra_-D0.mjs → commands-DzwKn57T.mjs} +2 -2
- package/dist/{fleet-Bi3KIev7.mjs → fleet-CPm4Bm3m.mjs} +1 -1
- package/dist/{frpc-BpQyeWvI.mjs → frpc-C2eRCDNy.mjs} +1 -1
- package/dist/{headlessCli-Be0bYOUB.mjs → headlessCli-Bt3ZIoTG.mjs} +2 -2
- package/dist/index.mjs +1 -1
- package/dist/package-BL4Hd940.mjs +63 -0
- package/dist/{rpc-WS0yDVqB.mjs → rpc-CekroPL2.mjs} +1 -1
- package/dist/{rpc-CRZWx5YY.mjs → rpc-CrjSXvID.mjs} +1 -1
- package/dist/{run-DVdqRhtp.mjs → run-BqvXq2yD.mjs} +10 -24
- package/dist/{run-8_mnLg6R.mjs → run-DUquoPkl.mjs} +1 -1
- package/dist/{scheduler-Dx-QrQfP.mjs → scheduler-agjaeQjl.mjs} +1 -1
- package/dist/{serveCommands-CI2P6buK.mjs → serveCommands-Bc4ZhEqi.mjs} +5 -5
- package/dist/{serveManager-B-keYGE9.mjs → serveManager-Ddqi1kbZ.mjs} +2 -2
- package/dist/{sideband-BdYb3ezu.mjs → sideband-lLT0D1PF.mjs} +1 -1
- package/package.json +3 -3
- package/bin/skills/loop/IMPLEMENTATION_PROGRESS.md +0 -49
- package/bin/skills/loop/SKILL.md +0 -100
- package/bin/skills/loop/bin/channel-core.mjs +0 -161
- package/bin/skills/loop/bin/channel-server.mjs +0 -151
- package/bin/skills/loop/bin/inject-loop.mjs +0 -90
- package/bin/skills/loop/bin/loop-init.mjs +0 -194
- package/bin/skills/loop/bin/loop-status.mjs +0 -51
- package/bin/skills/loop/bin/precompact.mjs +0 -30
- package/bin/skills/loop/bin/routine-core.mjs +0 -126
- package/bin/skills/loop/bin/state-fp.mjs +0 -111
- package/bin/skills/loop/bin/stop-gate.mjs +0 -349
- package/bin/skills/loop/test/test-channel-core.mjs +0 -86
- package/bin/skills/loop/test/test-loop-gate.mjs +0 -489
- package/bin/skills/loop/test/test-routine-core.mjs +0 -54
- package/dist/package-CwGmpH_-.mjs +0 -63
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { c as connectToHypha } from './run-
|
|
4
|
+
import { c as connectToHypha } from './run-BqvXq2yD.mjs';
|
|
5
5
|
import { PINNED_CLAUDE_CODE_VERSION } from './pinnedClaudeCode-VIupR1NK.mjs';
|
|
6
6
|
import 'os';
|
|
7
7
|
import 'fs/promises';
|
|
@@ -4,7 +4,7 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, chmodSync, readFileSy
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { homedir, platform, arch } from 'os';
|
|
6
6
|
import { randomUUID, createHash } from 'crypto';
|
|
7
|
-
import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-
|
|
7
|
+
import { h as getFrpsSubdomainHost, i as getFrpsServerPort, j as getFrpsServerAddr } from './run-BqvXq2yD.mjs';
|
|
8
8
|
import 'fs/promises';
|
|
9
9
|
import 'url';
|
|
10
10
|
import 'node:crypto';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as resolveModel, a1 as describeMisconfiguration, a2 as buildMachineDeps } from './run-
|
|
2
|
-
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-
|
|
1
|
+
import { P as resolveModel, a1 as describeMisconfiguration, a2 as buildMachineDeps } from './run-BqvXq2yD.mjs';
|
|
2
|
+
import { handleRealtimeEvent, initMachineVoiceSession } from './sideband-lLT0D1PF.mjs';
|
|
3
3
|
import { WebSocket } from 'ws';
|
|
4
4
|
import { execSync, spawn } from 'child_process';
|
|
5
5
|
import 'os';
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-
|
|
1
|
+
export { c as connectToHypha, a as createSessionStore, d as daemonStatus, g as getHyphaServerUrl, r as registerMachineService, s as startDaemon, b as stopDaemon } from './run-BqvXq2yD.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var name = "svamp-cli";
|
|
2
|
+
var version = "0.2.223";
|
|
3
|
+
var description = "Svamp CLI — AI workspace daemon on Hypha Cloud";
|
|
4
|
+
var author = "Amun AI AB";
|
|
5
|
+
var license = "SEE LICENSE IN LICENSE";
|
|
6
|
+
var type = "module";
|
|
7
|
+
var bin = {
|
|
8
|
+
svamp: "./bin/svamp.mjs"
|
|
9
|
+
};
|
|
10
|
+
var files = [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin"
|
|
13
|
+
];
|
|
14
|
+
var main = "./dist/index.mjs";
|
|
15
|
+
var exports$1 = {
|
|
16
|
+
".": "./dist/index.mjs",
|
|
17
|
+
"./cli": "./dist/cli.mjs"
|
|
18
|
+
};
|
|
19
|
+
var scripts = {
|
|
20
|
+
build: "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
|
|
21
|
+
typecheck: "tsc --noEmit",
|
|
22
|
+
test: "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs",
|
|
23
|
+
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
24
|
+
dev: "tsx src/cli.ts",
|
|
25
|
+
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
26
|
+
"test:e2e": "node --no-warnings test/e2e-session-tests.mjs",
|
|
27
|
+
"test:frpc": "npx tsx test/test-frpc-e2e.mjs"
|
|
28
|
+
};
|
|
29
|
+
var dependencies = {
|
|
30
|
+
"@agentclientprotocol/sdk": "^0.14.1",
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
32
|
+
"hypha-rpc": "0.21.42",
|
|
33
|
+
"node-pty": "1.2.0-beta.11",
|
|
34
|
+
ws: "^8.18.0",
|
|
35
|
+
yaml: "^2.8.2",
|
|
36
|
+
zod: "^3.24.4"
|
|
37
|
+
};
|
|
38
|
+
var devDependencies = {
|
|
39
|
+
"@types/node": ">=20",
|
|
40
|
+
"@types/ws": "^8.5.14",
|
|
41
|
+
pkgroll: "^2.14.2",
|
|
42
|
+
tsx: "^4.20.6",
|
|
43
|
+
typescript: "5.9.3"
|
|
44
|
+
};
|
|
45
|
+
var packageManager = "yarn@1.22.22";
|
|
46
|
+
var _package = {
|
|
47
|
+
name: name,
|
|
48
|
+
version: version,
|
|
49
|
+
description: description,
|
|
50
|
+
author: author,
|
|
51
|
+
license: license,
|
|
52
|
+
type: type,
|
|
53
|
+
bin: bin,
|
|
54
|
+
files: files,
|
|
55
|
+
main: main,
|
|
56
|
+
exports: exports$1,
|
|
57
|
+
scripts: scripts,
|
|
58
|
+
dependencies: dependencies,
|
|
59
|
+
devDependencies: devDependencies,
|
|
60
|
+
packageManager: packageManager
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export { author, bin, _package as default, dependencies, description, devDependencies, exports$1 as exports, files, license, main, name, packageManager, scripts, type, version };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as resumeIssue, p as pauseIssue, q as addComment, t as addIssue, v as listIssues, w as searchIssues, x as isVisibleTo } from './run-
|
|
1
|
+
import { m as resolveProjectRoot, u as updateIssue, n as getIssue, o as resumeIssue, p as pauseIssue, q as addComment, t as addIssue, v as listIssues, w as searchIssues, x as isVisibleTo } from './run-BqvXq2yD.mjs';
|
|
2
2
|
import 'os';
|
|
3
3
|
import 'fs/promises';
|
|
4
4
|
import 'fs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as resolveProjectRoot } from './run-
|
|
1
|
+
import { m as resolveProjectRoot } from './run-BqvXq2yD.mjs';
|
|
2
2
|
import { g as getWorkflow, s as setWorkflowEnabled, r as removeWorkflow, a as saveWorkflow, b as rawWorkflow, l as listWorkflows } from './store-BTs0H_y0.mjs';
|
|
3
3
|
import { g as getRun, l as listRuns, r as runWorkflow } from './runStore-CtptN7US.mjs';
|
|
4
4
|
import 'os';
|
|
@@ -2912,7 +2912,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2912
2912
|
const tunnels = handlers.tunnels;
|
|
2913
2913
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2914
2914
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2915
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2915
|
+
const { FrpcTunnel } = await import('./frpc-C2eRCDNy.mjs');
|
|
2916
2916
|
const tunnel = new FrpcTunnel({
|
|
2917
2917
|
name: params.name,
|
|
2918
2918
|
ports: params.ports,
|
|
@@ -3359,7 +3359,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3359
3359
|
}
|
|
3360
3360
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
3361
3361
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
3362
|
-
const { toolsForRole } = await import('./sideband-
|
|
3362
|
+
const { toolsForRole } = await import('./sideband-lLT0D1PF.mjs');
|
|
3363
3363
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
3364
3364
|
return fmt(r2);
|
|
3365
3365
|
}
|
|
@@ -3458,7 +3458,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3458
3458
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3459
3459
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3460
3460
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3461
|
-
const { queryCore } = await import('./commands-
|
|
3461
|
+
const { queryCore } = await import('./commands-B_XZmurW.mjs');
|
|
3462
3462
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3463
3463
|
let result;
|
|
3464
3464
|
try {
|
|
@@ -11295,20 +11295,6 @@ async function ensureAutoInstalledSkills(logger) {
|
|
|
11295
11295
|
},
|
|
11296
11296
|
marketplaceVersion: async () => readBundledSkillVersion("artifact")
|
|
11297
11297
|
},
|
|
11298
|
-
{
|
|
11299
|
-
// The self-verifying `loop` skill drives loop mode (Stop-hook gate +
|
|
11300
|
-
// LOOP.md injection). Bundled in the npm package (bin/skills/loop/) so the
|
|
11301
|
-
// daemon can run loop-init.mjs even offline / before marketplace publish.
|
|
11302
|
-
name: "loop",
|
|
11303
|
-
install: async () => {
|
|
11304
|
-
try {
|
|
11305
|
-
installBundledSkill("loop");
|
|
11306
|
-
} catch {
|
|
11307
|
-
await installSkillFromMarketplace("loop");
|
|
11308
|
-
}
|
|
11309
|
-
},
|
|
11310
|
-
marketplaceVersion: async () => readBundledSkillVersion("loop")
|
|
11311
|
-
},
|
|
11312
11298
|
{
|
|
11313
11299
|
// The `crew` skill: a lead session delegates features to managed worktree
|
|
11314
11300
|
// children (svamp feature start/list/done/merge) and merges them back.
|
|
@@ -12188,7 +12174,7 @@ async function startDaemon(options) {
|
|
|
12188
12174
|
saveExposedTunnels(list);
|
|
12189
12175
|
}
|
|
12190
12176
|
async function createExposedTunnel(spec) {
|
|
12191
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
12177
|
+
const { FrpcTunnel } = await import('./frpc-C2eRCDNy.mjs');
|
|
12192
12178
|
const tunnel = new FrpcTunnel({
|
|
12193
12179
|
name: spec.name,
|
|
12194
12180
|
ports: spec.ports,
|
|
@@ -12208,7 +12194,7 @@ async function startDaemon(options) {
|
|
|
12208
12194
|
return tunnel;
|
|
12209
12195
|
}
|
|
12210
12196
|
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
12211
|
-
const { ServeManager } = await import('./serveManager-
|
|
12197
|
+
const { ServeManager } = await import('./serveManager-Ddqi1kbZ.mjs');
|
|
12212
12198
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
12213
12199
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
12214
12200
|
});
|
|
@@ -14109,11 +14095,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14109
14095
|
});
|
|
14110
14096
|
},
|
|
14111
14097
|
onIssue: async (params) => {
|
|
14112
|
-
const { issueRpc } = await import('./rpc-
|
|
14098
|
+
const { issueRpc } = await import('./rpc-CekroPL2.mjs');
|
|
14113
14099
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
14114
14100
|
},
|
|
14115
14101
|
onWorkflow: async (params) => {
|
|
14116
|
-
const { workflowRpc } = await import('./rpc-
|
|
14102
|
+
const { workflowRpc } = await import('./rpc-CrjSXvID.mjs');
|
|
14117
14103
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
14118
14104
|
},
|
|
14119
14105
|
onRipgrep: async (args, cwd) => {
|
|
@@ -14646,11 +14632,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14646
14632
|
});
|
|
14647
14633
|
},
|
|
14648
14634
|
onIssue: async (params) => {
|
|
14649
|
-
const { issueRpc } = await import('./rpc-
|
|
14635
|
+
const { issueRpc } = await import('./rpc-CekroPL2.mjs');
|
|
14650
14636
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner, rekickLoopOwner });
|
|
14651
14637
|
},
|
|
14652
14638
|
onWorkflow: async (params) => {
|
|
14653
|
-
const { workflowRpc } = await import('./rpc-
|
|
14639
|
+
const { workflowRpc } = await import('./rpc-CrjSXvID.mjs');
|
|
14654
14640
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
14655
14641
|
},
|
|
14656
14642
|
onRipgrep: async (args, cwd) => {
|
|
@@ -15424,7 +15410,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
15424
15410
|
const PING_TIMEOUT_MS = 15e3;
|
|
15425
15411
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
15426
15412
|
const RECONNECT_JITTER_MS = 2500;
|
|
15427
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
15413
|
+
const { WorkflowScheduler } = await import('./scheduler-agjaeQjl.mjs');
|
|
15428
15414
|
const workflowScheduler = new WorkflowScheduler({
|
|
15429
15415
|
projectRoots: () => {
|
|
15430
15416
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a3 as applyClaudeProxyEnv, a4 as composeSessionId, a5 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a6 as generateHookSettings } from './run-
|
|
1
|
+
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { a3 as applyClaudeProxyEnv, a4 as composeSessionId, a5 as generateFriendlyName, c as connectToHypha, a as createSessionStore, r as registerMachineService, a6 as generateHookSettings } from './run-BqvXq2yD.mjs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { existsSync, readFileSync, watch } from 'node:fs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as resolveProjectRoot, y as cronMatches } from './run-
|
|
1
|
+
import { m as resolveProjectRoot, y as cronMatches } from './run-BqvXq2yD.mjs';
|
|
2
2
|
import { l as listWorkflows, i as isWorkflowEnabled, w as workflowCrons } from './store-BTs0H_y0.mjs';
|
|
3
3
|
import { r as runWorkflow } from './runStore-CtptN7US.mjs';
|
|
4
4
|
import 'os';
|
|
@@ -54,7 +54,7 @@ async function handleServeCommand() {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function serveAdd(args, machineId) {
|
|
57
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
57
|
+
const { connectAndGetMachine } = await import('./commands-B_XZmurW.mjs');
|
|
58
58
|
const pos = positionalArgs(args);
|
|
59
59
|
const name = pos[0];
|
|
60
60
|
if (!name) {
|
|
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
async function serveApply(args, machineId) {
|
|
96
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
96
|
+
const { connectAndGetMachine } = await import('./commands-B_XZmurW.mjs');
|
|
97
97
|
const fs = await import('fs');
|
|
98
98
|
const yaml = await import('yaml');
|
|
99
99
|
const file = positionalArgs(args)[0];
|
|
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
async function serveRemove(args, machineId) {
|
|
185
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
185
|
+
const { connectAndGetMachine } = await import('./commands-B_XZmurW.mjs');
|
|
186
186
|
const pos = positionalArgs(args);
|
|
187
187
|
const name = pos[0];
|
|
188
188
|
if (!name) {
|
|
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
async function serveList(args, machineId) {
|
|
205
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
205
|
+
const { connectAndGetMachine } = await import('./commands-B_XZmurW.mjs');
|
|
206
206
|
const all = hasFlag(args, "--all", "-a");
|
|
207
207
|
const json = hasFlag(args, "--json");
|
|
208
208
|
const sessionId = getFlag(args, "--session");
|
|
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
async function serveInfo(machineId) {
|
|
238
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
238
|
+
const { connectAndGetMachine } = await import('./commands-B_XZmurW.mjs');
|
|
239
239
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
240
240
|
try {
|
|
241
241
|
const info = await machine.serveInfo();
|
|
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as http from 'http';
|
|
5
5
|
import * as net from 'net';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
-
import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-
|
|
7
|
+
import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-BqvXq2yD.mjs';
|
|
8
8
|
import 'os';
|
|
9
9
|
import 'fs/promises';
|
|
10
10
|
import 'url';
|
|
@@ -733,7 +733,7 @@ class ServeManager {
|
|
|
733
733
|
const mount = this.mounts.get(mountName);
|
|
734
734
|
const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, buildLinkSubdomain(subdomainSafe, mount.linkToken)]]) : void 0;
|
|
735
735
|
try {
|
|
736
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
736
|
+
const { FrpcTunnel } = await import('./frpc-C2eRCDNy.mjs');
|
|
737
737
|
let tunnel;
|
|
738
738
|
tunnel = new FrpcTunnel({
|
|
739
739
|
name: tunnelName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as READ_ONLY_TOOLS, B as loadMachineContext, C as buildMachineInstructions, D as machineToolsForRole, E as buildMachineTools } from './run-
|
|
1
|
+
import { R as READ_ONLY_TOOLS, B as loadMachineContext, C as buildMachineInstructions, D as machineToolsForRole, E as buildMachineTools } from './run-BqvXq2yD.mjs';
|
|
2
2
|
import 'node:child_process';
|
|
3
3
|
import 'os';
|
|
4
4
|
import 'fs/promises';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svamp-cli",
|
|
3
|
-
"version": "0.2.223
|
|
3
|
+
"version": "0.2.223",
|
|
4
4
|
"description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
|
|
5
5
|
"author": "Amun AI AB",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"./cli": "./dist/cli.mjs"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"build": "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/
|
|
21
|
+
"build": "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
|
-
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-
|
|
23
|
+
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs",
|
|
24
24
|
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
25
25
|
"dev": "tsx src/cli.ts",
|
|
26
26
|
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# Loops, Routines & Channels — implementation status
|
|
2
|
-
|
|
3
|
-
Branch: `loop-engineering-phase1`. Design docs: `docs/loops-and-routines-design.md`, `docs/channels-design.md`.
|
|
4
|
-
|
|
5
|
-
This consolidates the work into one accurate picture: what's built, how it was verified, and the single remaining step (deploy + UI QA).
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Loop engine — DONE (tested + live-E2E + 2 adversarial reviews)
|
|
10
|
-
Skill-first loop with a harness-enforced completion gate. Files in `skills/loop/`.
|
|
11
|
-
- **`SKILL.md`** — loop-engineering protocol (actor ≠ judge; oracle + isolated evaluator).
|
|
12
|
-
- **`bin/stop-gate.mjs`** — Claude Code `Stop` hook: blocks the turn ending unless the **oracle exits 0 AND a fingerprint-fresh evaluator verdict says "done."** Bounded by clamped `max_iterations` (hard ceiling 200), runtime budget, and token budget (summed from the transcript). Appends a per-iteration audit trail.
|
|
13
|
-
- **`bin/state-fp.mjs`** — working-tree fingerprint (git diff, or filesystem walk incl. symlink targets for non-git dirs) tying a verdict to the exact code it judged.
|
|
14
|
-
- **`bin/inject-loop.mjs`** (SessionStart/UserPromptSubmit) · **`bin/precompact.mjs`** (PreCompact handoff) · **`bin/loop-init.mjs`** (generates LOOP.md + `.claude/settings.json` hooks + optional `loop-evaluator` agent) · **`bin/loop-status.mjs`** (timeline reader).
|
|
15
|
-
- **Tests:** `test/test-loop-gate.mjs` (29) — all pass. **Live-verified** in real svamp sessions: happy path, **early-quit blocked**, isolated evaluator subagent, full regression.
|
|
16
|
-
- Skill↔gate↔inject **contract verified consistent** (verdict path/fields/`state_fp` command all agree).
|
|
17
|
-
|
|
18
|
-
## 2. Routines — DONE (tested + live-E2E)
|
|
19
|
-
Session-scoped triggers → message/loop actions. `skills/loop/bin/routine-*.mjs` (JS engine + supervised server) and `packages/svamp-cli/src/routine/*.ts` (daemon).
|
|
20
|
-
- Triggers: schedule (cron, tz-aware tick **and** catch-up), webhook (capability URL, GET/POST), manual. Actions: message | loop. Overlap (queue/skip/replace concurrent-guard), daily_cap (reserve-before-deliver), missed-run catch-up.
|
|
21
|
-
- `svamp routine add/list/remove/enable/disable/run-now/serve`; daemon RPC (`hyphaSessionService`, 10 runtime tests); frontend **Routines tab**.
|
|
22
|
-
- **Tests:** routine-core, routine-engine (26), cli routine (18), cli routine-rpc (10) — all pass. **Live-verified**: webhook → templated message delivered into a real session.
|
|
23
|
-
|
|
24
|
-
## 3. Channels — DONE (tested + live-E2E both transports + 2 security reviews)
|
|
25
|
-
External / agent-to-agent inbound endpoints. `skills/loop/bin/channel-*.mjs` + `packages/svamp-cli/src/channel/store.ts`.
|
|
26
|
-
- Identity: per-key (verified) / caller-supplied (unverified) / fixed / Hypha `context.user` (opt-in `hypha_allow`, deny-by-default, anonymous rejected). Default **XML provenance template** (all values XML-escaped + control-chars stripped → no forgery/tag-breakout). Auto-generated **skill** per channel.
|
|
27
|
-
- Transports: **Hypha `svamp-channels` service** (verified identity, no key) **and** HTTP `/channel/<id>` (+ `/skill.md`, bearer key). Delivers as a **plain single-tag message**. Actions: **message and loop** (loop seeds `loop-init` + kickoff).
|
|
28
|
-
- `hypha channel list/describe/send` CLI client; daemon RPC (10 runtime tests); frontend **Channels tab**.
|
|
29
|
-
- **Tests:** channel-core (32), cli channel-rpc (10) — all pass. **Live-verified**: HTTP per-key (`from=alice verified`), Hypha (`from=<hypha-user> verified`, no key), loop-action (loop seeded + work completed).
|
|
30
|
-
|
|
31
|
-
## Verification summary
|
|
32
|
-
All feature suites green; **svamp-cli + svamp-app + hypha-cli typecheck 0 errors**. Standalone/skill parts live-E2E-verified. Daemon RPC runtime-verified via `createSessionStore`. Two adversarial review rounds each on the loop gate and channels (caught + fixed 3 CRITICAL block-forever bugs and a CRITICAL provenance-forgery hole); daemon-code review confirmed no destabilization. Review has **converged** (latest pass found nothing).
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## The ONE remaining step — deploy + UI QA (must be done from a SEPARATE session)
|
|
37
|
-
The daemon RPC + the three frontend tabs (Routines / Channels / Loop-monitor) are typecheck-clean and production-bundle clean, but their **live rendering + RPC round-trip have not been visually confirmed** — and this repo has no component-render test infra (UI is verified by deploy/manual). Activating them restarts the daemon hosting this loop session, so it is **deliberately not done here**. To finish:
|
|
38
|
-
```
|
|
39
|
-
yarn workspace svamp-cli build && svamp daemon restart # activates routine/channel RPC
|
|
40
|
-
./deploy/quick-deploy.sh # ships the web UI
|
|
41
|
-
```
|
|
42
|
-
Then open a session → input → settings and confirm the **Routines / Channels / Loop-monitor** tabs render and round-trip. The standalone engines need no deploy — `node skills/loop/bin/loop-init.mjs …`, `svamp routine …`, and the supervised channel/routine servers already work today.
|
|
43
|
-
|
|
44
|
-
## Deferred (by design or low-risk-to-leave)
|
|
45
|
-
- **await-reply (channels v1.1)** — request/response; v1 is async (two channels), per the design decision.
|
|
46
|
-
- **Loop gate on an already-running session** — hooks load at Claude startup, so looping a live session is best-effort (self-contained kickoff); full enforcement needs a fresh/restarted session.
|
|
47
|
-
- **#4/#6/#11 concurrency edges** — cross-process lost-update / git↔walk fp flip / store last-writer-wins. Low-likelihood for the single-session loop; a forced fix needs locking that could itself cause block-forever, so documented rather than fixed.
|
|
48
|
-
- **Token/cost budget in USD** — token budget (from transcript) is implemented; USD cost would need daemon usage accounting.
|
|
49
|
-
</content>
|
package/bin/skills/loop/SKILL.md
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: loop
|
|
3
|
-
version: 0.4.2
|
|
4
|
-
description: Run a task as a reliable, self-verifying loop — iterate until objective exit conditions are met, with an independent evaluator instead of self-judging. Use when a task needs repeated iterations until "done" (fix until tests pass, refactor until clean, build until a spec is met, autonomous long-running work).
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Loop — loop engineering for long-running, self-verifying agents
|
|
8
|
-
|
|
9
|
-
This skill turns a task into a **loop**: the agent iterates until the work is *objectively* done, not until the agent *feels* done. It is built on one hard-won lesson from loop engineering — **the thing that decides "done" must be independent of the thing doing the work.**
|
|
10
|
-
|
|
11
|
-
## Why loops, and why this design
|
|
12
|
-
|
|
13
|
-
Prompting an agent turn-by-turn doesn't scale to long or autonomous work. Loop engineering is the shift from *running the agent yourself* to *designing the system that runs it*. A good loop has a small number of durable parts, and most of the leverage is in **the loop, not the prompt**.
|
|
14
|
-
|
|
15
|
-
The failure this design exists to prevent is **early quitting**: a model asked to judge its own output will confidently declare success on mediocre or incomplete work, and models also tend to "wrap up" as they approach context limits ("context anxiety"). Trusting the actor to say `DONE` is the single biggest source of unreliable loops.
|
|
16
|
-
|
|
17
|
-
So this loop is built on these principles:
|
|
18
|
-
|
|
19
|
-
1. **Actor ≠ judge.** The agent doing the work never decides completion. An *independent* evaluator — a fresh, skeptical agent that only sees the artifacts, not your reasoning — does. Calibrating a separate evaluator toward skepticism is far more reliable than asking yourself to be self-critical.
|
|
20
|
-
2. **Verification needs an oracle.** The strongest signal is something *outside any model* that returns a hard yes/no: a test suite, a build, a linter, a type-check, a script. The loop runs the oracle itself; you cannot fake it.
|
|
21
|
-
3. **State lives on disk, not in context.** *The model forgets; the repo doesn't.* The task, plan, and progress live in **`LOOP.md`** in the project — readable and editable by both you and the human, surviving compaction, restarts, and fresh sessions. It is your durable memory and your self-improving harness.
|
|
22
|
-
4. **The harness enforces, the prompt only requests.** Completion is gated by a Claude Code **`Stop` hook** that the runtime *obeys* — it physically refuses to let the turn end until the exit conditions hold. This is what makes the loop reliable rather than aspirational.
|
|
23
|
-
5. **Bounded by construction.** Every loop has a `max_iterations` and a token/cost budget so it cannot run away.
|
|
24
|
-
6. **Stay auditable.** Every iteration leaves a trail (oracle result, evaluator verdict, progress notes) so a human can review *why* it stopped, not just *that* it stopped. Build the loop like someone who intends to stay the engineer.
|
|
25
|
-
|
|
26
|
-
These principles are stable even as models get stronger. As the actor improves, you simply lean on lighter scaffolding (e.g. trust longer continuous context, fewer resets) — but *independent verification of "done"* and *durable on-disk state* remain correct regardless of model capability. Don't reimplement what the runtime already gives you (subagents, compaction, worktrees, usage accounting); compose them.
|
|
27
|
-
|
|
28
|
-
## The loop, in one picture
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
read LOOP.md ─► do real work on the task ─► update LOOP.md progress
|
|
32
|
-
▲ │
|
|
33
|
-
│ (think you're done?)
|
|
34
|
-
│ ▼
|
|
35
|
-
│ run ORACLE + spawn independent EVALUATOR
|
|
36
|
-
│ │
|
|
37
|
-
Stop hook blocks ◄── not done ── GATE ── done ──► loop ends
|
|
38
|
-
(oracle/verdict fails)
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Each turn is one iteration. When you try to end your turn, the gate re-checks the exit conditions. If they don't hold, you are sent back with specific feedback. You converge by *fixing the blocking issue*, not by re-asserting that you're done.
|
|
42
|
-
|
|
43
|
-
## Exit conditions (what "done" means)
|
|
44
|
-
|
|
45
|
-
The loop ends only when **all** configured conditions hold:
|
|
46
|
-
- **Oracle passes** — the configured command exits 0. The gate runs it; do not stub or fake it.
|
|
47
|
-
- **Independent evaluator says "done"** (when enabled) — a *fresh* verdict tied to the *current* code state. A verdict written before later edits is stale and rejected.
|
|
48
|
-
|
|
49
|
-
## Your protocol each iteration
|
|
50
|
-
|
|
51
|
-
1. **Read `LOOP.md`.** It holds the task, success criteria, plan, and progress. (It is auto-injected each turn.)
|
|
52
|
-
2. **Make real progress** on the task. Prefer the smallest change that advances a success criterion.
|
|
53
|
-
3. **Keep `LOOP.md` current** — refine the plan, append what you did, what passed, what's left. This is the memory the *next* iteration (or a post-restart fresh session) relies on. You may restructure it; you may **not** weaken the success criteria to make quitting easier (that's visible in git and to the human).
|
|
54
|
-
4. **Run the oracle yourself** to check you're actually passing before claiming done.
|
|
55
|
-
5. **When you believe it's complete, get an independent verdict — do not grade yourself:**
|
|
56
|
-
- Spawn a fresh subagent with the `Task` tool (use the `loop-evaluator` agent if present, otherwise an inline *skeptical reviewer* prompt). Give it **only** the goal (from `LOOP.md`), the current diff, and the oracle output — never your chain of thought.
|
|
57
|
-
- For an **issue-backlog** loop the evaluator must go further than the oracle: confirm EACH issue closed this round is genuinely resolved by the real change, AND judge **holistically from the user's perspective** — re-read the *original request* behind each closed issue and the *overall project goal*, and confirm we delivered what the user actually wanted end-to-end (built / published / deployed wherever the request implied it, not merely committed). A green oracle only means "no open issues"; reject `done` if the project goal isn't truly met for the user.
|
|
58
|
-
- It returns `{"verdict":"done"|"continue","reason":...,"guidance":...}`. It should default to `continue` on any doubt.
|
|
59
|
-
- Record it to `.claude/loop/evaluator-verdict.json`, adding the current state fingerprint:
|
|
60
|
-
`{"verdict":"done","reason":"...","guidance":"...","state_fp":"<output of: node .claude/loop/bin/state-fp.mjs>"}`
|
|
61
|
-
6. **End your turn.** The Stop gate verifies oracle + fresh verdict and either ends the loop or sends you back with the exact blocking reason. Address that reason next iteration.
|
|
62
|
-
|
|
63
|
-
If you're blocked by something genuinely outside the task (missing secret, ambiguous requirement, external outage), record it clearly in `LOOP.md` so the human sees it on review.
|
|
64
|
-
|
|
65
|
-
## Setting up a loop
|
|
66
|
-
|
|
67
|
-
Initialise the loop scaffolding in a project (idempotent):
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
node <skill>/bin/loop-init.mjs <project-dir> \
|
|
71
|
-
--task "Make the failing auth tests pass without weakening them" \
|
|
72
|
-
--criteria "All auth tests pass; no test was weakened or skipped" \
|
|
73
|
-
--oracle "npm test --silent" \
|
|
74
|
-
--evaluator on \
|
|
75
|
-
--max 20
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
This generates: `LOOP.md` (task/criteria/progress), `.claude/loop/loop.config.json`, machine state in `.claude/loop/loop-state.json`, the `Stop`/`SessionStart` hooks in `.claude/settings.json`, and (optionally) a materialised `.claude/agents/loop-evaluator.md` skeptical reviewer. Re-running with new flags retunes a live loop.
|
|
79
|
-
|
|
80
|
-
## Anatomy (files & ownership)
|
|
81
|
-
|
|
82
|
-
| File | Owner | Purpose |
|
|
83
|
-
|---|---|---|
|
|
84
|
-
| `LOOP.md` | you + human | task · success criteria · plan · progress (durable memory, git-visible) |
|
|
85
|
-
| `.claude/loop/loop.config.json` | config | oracle command, evaluator on/off + model, max_iterations |
|
|
86
|
-
| `.claude/loop/loop-state.json` | the gate | iteration, phase, last oracle/eval result (machine state — don't hand-edit) |
|
|
87
|
-
| `.claude/loop/evaluator-verdict.json` | written from the evaluator subagent | latest verdict + state fingerprint |
|
|
88
|
-
| `.claude/settings.json` (hooks) | generated | `Stop` gate + `SessionStart`/`UserPromptSubmit` LOOP.md injection |
|
|
89
|
-
| `.claude/agents/loop-evaluator.md` | generated (optional) | skeptical reviewer persona + model |
|
|
90
|
-
|
|
91
|
-
## Tuning & extending (future-proofing)
|
|
92
|
-
|
|
93
|
-
- **No oracle?** Lean fully on the evaluator (weaker — prefer adding *some* objective check, even a smoke script).
|
|
94
|
-
- **Stronger judgment?** Point the evaluator at a stronger model via `--model`.
|
|
95
|
-
- **Cheaper?** Disable the evaluator (`--evaluator off`) and rely on the oracle alone for purely objective tasks.
|
|
96
|
-
- **Different exit logic** (scores/thresholds, FINISH/REFINE/PIVOT strategies, multi-dimensional rubrics): the verdict file is just JSON and the gate reads it — richer verdicts can be added without changing the protocol.
|
|
97
|
-
- **Scheduling & lifecycle** (run on a schedule): handled by Workflows (`svamp workflow add … --on schedule --cron "…"`, GitHub-Actions-style), not this skill — a workflow step can start a loop (`svamp session loop …`). The loop itself stays a pure iterate-until-done engine.
|
|
98
|
-
- **Compaction handoff**: progress in `LOOP.md` is the handoff artifact; a `PreCompact` hook can flush it automatically for very long runs.
|
|
99
|
-
|
|
100
|
-
The contract that must never change: **work is decided done by something other than the worker, and the loop's memory lives on disk.** Everything else is tunable.
|