loop-sdk 0.1.0
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/README.md +591 -0
- package/dist/agent.d.ts +31 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +48 -0
- package/dist/agent.js.map +1 -0
- package/dist/checkpoint.d.ts +16 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint.js +20 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/claude-cli.d.ts +35 -0
- package/dist/claude-cli.d.ts.map +1 -0
- package/dist/claude-cli.js +135 -0
- package/dist/claude-cli.js.map +1 -0
- package/dist/context.d.ts +53 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +95 -0
- package/dist/context.js.map +1 -0
- package/dist/events.d.ts +111 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +53 -0
- package/dist/events.js.map +1 -0
- package/dist/flow.d.ts +36 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/flow.js +62 -0
- package/dist/flow.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +37 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +64 -0
- package/dist/logger.js.map +1 -0
- package/dist/loop.d.ts +199 -0
- package/dist/loop.d.ts.map +1 -0
- package/dist/loop.js +403 -0
- package/dist/loop.js.map +1 -0
- package/dist/loopfile.d.ts +82 -0
- package/dist/loopfile.d.ts.map +1 -0
- package/dist/loopfile.js +235 -0
- package/dist/loopfile.js.map +1 -0
- package/dist/mcp/server.d.ts +26 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +160 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/notify.d.ts +43 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +68 -0
- package/dist/notify.js.map +1 -0
- package/dist/plugins/retry.d.ts +41 -0
- package/dist/plugins/retry.d.ts.map +1 -0
- package/dist/plugins/retry.js +53 -0
- package/dist/plugins/retry.js.map +1 -0
- package/dist/providers/playwright.d.ts +38 -0
- package/dist/providers/playwright.d.ts.map +1 -0
- package/dist/providers/playwright.js +155 -0
- package/dist/providers/playwright.js.map +1 -0
- package/dist/session.d.ts +43 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +26 -0
- package/dist/session.js.map +1 -0
- package/package.json +78 -0
package/dist/loopfile.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import { Loop } from './loop.js';
|
|
5
|
+
import { claudeCli } from './claude-cli.js';
|
|
6
|
+
import { notifyOn } from './notify.js';
|
|
7
|
+
// ── Parser ────────────────────────────────────────────────────────────────────
|
|
8
|
+
export function parseLoopFile(content) {
|
|
9
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
10
|
+
if (!match)
|
|
11
|
+
throw new Error('.loop file must begin with a YAML front-matter block (---)');
|
|
12
|
+
const meta = yaml.load(match[1]);
|
|
13
|
+
if (!meta?.name)
|
|
14
|
+
throw new Error('.loop front-matter must include a "name" field');
|
|
15
|
+
const body = match[2];
|
|
16
|
+
const sections = body.split(/\n(?=## )/).map(s => s.trim()).filter(Boolean);
|
|
17
|
+
const steps = [];
|
|
18
|
+
for (const section of sections) {
|
|
19
|
+
const lines = section.split('\n');
|
|
20
|
+
const name = lines[0].replace(/^##\s*/, '').trim();
|
|
21
|
+
const rest = lines.slice(1).join('\n').trim();
|
|
22
|
+
if (!rest)
|
|
23
|
+
throw new Error(`Step "${name}" has no configuration`);
|
|
24
|
+
const stepConfig = yaml.load(rest);
|
|
25
|
+
if (!stepConfig?.action)
|
|
26
|
+
throw new Error(`Step "${name}" must have an "action" field`);
|
|
27
|
+
steps.push({ name, ...stepConfig });
|
|
28
|
+
}
|
|
29
|
+
if (steps.length === 0)
|
|
30
|
+
throw new Error('.loop file has no steps defined');
|
|
31
|
+
return { meta, steps };
|
|
32
|
+
}
|
|
33
|
+
export function loadLoopFile(filePath) {
|
|
34
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
35
|
+
return parseLoopFile(content);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load a .loop file and return a configured Loop instance ready to run.
|
|
39
|
+
*
|
|
40
|
+
* Built-in actions: claudeCli, navigate, screenshot, log, sub, each
|
|
41
|
+
* Pass `actions` to register custom action handlers.
|
|
42
|
+
*/
|
|
43
|
+
export function loadLoop(filePath, actions = {}) {
|
|
44
|
+
const schema = loadLoopFile(filePath);
|
|
45
|
+
const basePath = path.dirname(path.resolve(filePath));
|
|
46
|
+
const loop = buildLoopFromSteps(schema.meta.name, schema.steps, schema.meta, basePath, actions);
|
|
47
|
+
// Auto-apply notify config so sub-loops honor their own notification settings
|
|
48
|
+
const n = schema.meta.notify;
|
|
49
|
+
if (n) {
|
|
50
|
+
notifyOn(loop, {
|
|
51
|
+
title: schema.meta.name ?? loop.name,
|
|
52
|
+
onStart: Boolean(n.onStart ?? false),
|
|
53
|
+
onComplete: Boolean(n.onComplete ?? true),
|
|
54
|
+
onError: Boolean(n.onError ?? true),
|
|
55
|
+
onStepError: Boolean(n.onStepError ?? false),
|
|
56
|
+
sound: Boolean(n.sound ?? false),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return loop;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Load and run a .loop file in one call.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* await runFile('./research.loop', new PlaywrightSession('run-1'))
|
|
66
|
+
*/
|
|
67
|
+
export async function runFile(filePath, session, opts = {}, actions = {}) {
|
|
68
|
+
const loop = loadLoop(filePath, actions);
|
|
69
|
+
return loop.run({ session, ...opts });
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Load and run a .loop file in the background, returning a RunHandle immediately.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* const handle = runFileBackground('./research.loop', session)
|
|
76
|
+
* const handle2 = runFileBackground('./report.loop', session2)
|
|
77
|
+
*
|
|
78
|
+
* const [log1, log2] = await Promise.all([handle.wait(), handle2.wait()])
|
|
79
|
+
*/
|
|
80
|
+
export function runFileBackground(filePath, session, opts = {}, actions = {}) {
|
|
81
|
+
const loop = loadLoop(filePath, actions);
|
|
82
|
+
return loop.runBackground({ session, ...opts });
|
|
83
|
+
}
|
|
84
|
+
// ── Internal builders ─────────────────────────────────────────────────────────
|
|
85
|
+
function buildLoopFromSteps(name, steps, meta, basePath, actions) {
|
|
86
|
+
const loop = new Loop(name);
|
|
87
|
+
for (const step of steps) {
|
|
88
|
+
loop.step(step.name, buildStepFn(step, meta, basePath, actions), {
|
|
89
|
+
retries: step.retries,
|
|
90
|
+
retryDelay: step.retryDelay,
|
|
91
|
+
retryBackoff: step.retryBackoff,
|
|
92
|
+
skipOnError: step.skipOnError || step.onError === 'skip',
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return loop;
|
|
96
|
+
}
|
|
97
|
+
function buildStepFn(step, meta, basePath, actions) {
|
|
98
|
+
return async (ctx) => {
|
|
99
|
+
switch (step.action) {
|
|
100
|
+
case 'claudeCli': {
|
|
101
|
+
if (!step.prompt)
|
|
102
|
+
throw new Error(`Step "${step.name}": claudeCli requires a prompt`);
|
|
103
|
+
const prompt = interpolate(step.prompt, ctx);
|
|
104
|
+
const result = await claudeCli(ctx, prompt, { model: step.model ?? meta.model });
|
|
105
|
+
ctx.set(step.name, result.output);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case 'navigate': {
|
|
109
|
+
const url = interpolate(step.url ?? step.prompt ?? '', ctx);
|
|
110
|
+
if (!url)
|
|
111
|
+
throw new Error(`Step "${step.name}": navigate requires a url or prompt`);
|
|
112
|
+
await ctx.navigate(url);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case 'screenshot': {
|
|
116
|
+
const buf = await ctx.screenshot();
|
|
117
|
+
ctx.set(step.name, buf);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
case 'log': {
|
|
121
|
+
const msg = interpolate(step.message ?? step.prompt ?? '', ctx);
|
|
122
|
+
ctx.log(msg);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
// ── parallel: run inline steps concurrently, wait for all ─────────────
|
|
126
|
+
case 'parallel': {
|
|
127
|
+
if (!step.steps?.length) {
|
|
128
|
+
throw new Error(`Step "${step.name}": parallel requires inline "steps"`);
|
|
129
|
+
}
|
|
130
|
+
await Promise.all(step.steps.map(subStep => buildStepFn(subStep, meta, basePath, actions)(ctx)));
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
// ── sub: run a .loop file as a nested step, sharing context ────────────
|
|
134
|
+
case 'sub': {
|
|
135
|
+
if (!step.loop)
|
|
136
|
+
throw new Error(`Step "${step.name}": sub requires a "loop" file path`);
|
|
137
|
+
const loopPath = path.resolve(basePath, step.loop);
|
|
138
|
+
const subLoop = loadLoop(loopPath, actions);
|
|
139
|
+
// Resolve any {{ref}} in var values from the parent context before forking
|
|
140
|
+
const resolvedVars = {};
|
|
141
|
+
for (const [k, v] of Object.entries(step.vars ?? {})) {
|
|
142
|
+
resolvedVars[k] = typeof v === 'string' ? interpolate(v, ctx) : v;
|
|
143
|
+
}
|
|
144
|
+
const childCtx = ctx.fork(resolvedVars);
|
|
145
|
+
await subLoop.runWith(childCtx);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
// ── each: iterate over items, run a loop per item ───────────────────────
|
|
149
|
+
case 'each': {
|
|
150
|
+
const items = resolveItems(step, ctx);
|
|
151
|
+
const asVar = step.as ?? 'item';
|
|
152
|
+
// Build the per-item loop (file reference or inline steps)
|
|
153
|
+
let itemLoop;
|
|
154
|
+
let lastStepName;
|
|
155
|
+
if (step.loop) {
|
|
156
|
+
const loopPath = path.resolve(basePath, step.loop);
|
|
157
|
+
const schema = loadLoopFile(loopPath);
|
|
158
|
+
itemLoop = loadLoop(loopPath, actions);
|
|
159
|
+
lastStepName = step.output ?? schema.steps[schema.steps.length - 1].name;
|
|
160
|
+
}
|
|
161
|
+
else if (step.steps?.length) {
|
|
162
|
+
itemLoop = buildLoopFromSteps(`${step.name}-each`, step.steps, meta, basePath, actions);
|
|
163
|
+
lastStepName = step.output ?? step.steps[step.steps.length - 1].name;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
throw new Error(`Step "${step.name}": each requires a "loop" file path or inline "steps"`);
|
|
167
|
+
}
|
|
168
|
+
// Iterate — collect each item's output into an array
|
|
169
|
+
const results = [];
|
|
170
|
+
for (let i = 0; i < items.length; i++) {
|
|
171
|
+
const item = items[i];
|
|
172
|
+
const childCtx = ctx.fork({ [asVar]: item, _index: i, _total: items.length });
|
|
173
|
+
try {
|
|
174
|
+
await itemLoop.runWith(childCtx);
|
|
175
|
+
results.push(childCtx.get(lastStepName));
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
if (!step.continueOnError)
|
|
179
|
+
throw err;
|
|
180
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
181
|
+
ctx.log(`each[${i}]: failed — ${msg}`);
|
|
182
|
+
results.push(null);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
ctx.set(step.name, results);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
default: {
|
|
189
|
+
const handler = actions[step.action];
|
|
190
|
+
if (!handler) {
|
|
191
|
+
throw new Error(`Step "${step.name}": unknown action "${step.action}". Register it in the actions map passed to loadLoop().`);
|
|
192
|
+
}
|
|
193
|
+
const result = await handler(ctx, step);
|
|
194
|
+
if (result !== undefined)
|
|
195
|
+
ctx.set(step.name, result);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
202
|
+
function resolveItems(step, ctx) {
|
|
203
|
+
const raw = step.items;
|
|
204
|
+
if (Array.isArray(raw))
|
|
205
|
+
return raw;
|
|
206
|
+
if (typeof raw === 'string') {
|
|
207
|
+
const refMatch = raw.match(/^\{\{([\w-]+)\}\}$/);
|
|
208
|
+
const key = refMatch ? refMatch[1] : raw;
|
|
209
|
+
const val = ctx.get(key);
|
|
210
|
+
if (Array.isArray(val))
|
|
211
|
+
return val;
|
|
212
|
+
// claudeCli often returns JSON arrays as strings — auto-parse
|
|
213
|
+
if (typeof val === 'string') {
|
|
214
|
+
const trimmed = val.trim();
|
|
215
|
+
if (trimmed.startsWith('[')) {
|
|
216
|
+
try {
|
|
217
|
+
return JSON.parse(trimmed);
|
|
218
|
+
}
|
|
219
|
+
catch { }
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
throw new Error(`each "${step.name}": "{{${key}}}" must be an array or a JSON array string (got ${typeof val})`);
|
|
223
|
+
}
|
|
224
|
+
throw new Error(`Step "${step.name}": each requires an "items" field ({{step-name}} reference or YAML array)`);
|
|
225
|
+
}
|
|
226
|
+
function interpolate(template, ctx) {
|
|
227
|
+
return template.replace(/\{\{([\w-]+)\}\}/g, (_, key) => {
|
|
228
|
+
// Check state first (prior step outputs), then vars (per-iteration values from fork())
|
|
229
|
+
const val = ctx.get(key) ?? ctx.vars[key];
|
|
230
|
+
if (val == null)
|
|
231
|
+
return `{{${key}}}`;
|
|
232
|
+
return typeof val === 'object' ? JSON.stringify(val) : String(val);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=loopfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopfile.js","sourceRoot":"","sources":["../src/loopfile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* loop-sdk MCP server — exposes tools that let an AI agent author .loop files.
|
|
4
|
+
*
|
|
5
|
+
* Run via stdio transport (compatible with `claude -p --mcp-config`):
|
|
6
|
+
* node dist/mcp/server.js
|
|
7
|
+
*
|
|
8
|
+
* MCP config example:
|
|
9
|
+
* {
|
|
10
|
+
* "mcpServers": {
|
|
11
|
+
* "loop": {
|
|
12
|
+
* "command": "node",
|
|
13
|
+
* "args": ["./dist/mcp/server.js"],
|
|
14
|
+
* "env": { "LOOP_DIR": "./loops" }
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* Tools exposed:
|
|
20
|
+
* write_loop — write a .loop file to disk
|
|
21
|
+
* read_loop — read an existing .loop file
|
|
22
|
+
* list_loops — list .loop files in a directory
|
|
23
|
+
* validate_loop — parse a .loop file and report any errors
|
|
24
|
+
*/
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* loop-sdk MCP server — exposes tools that let an AI agent author .loop files.
|
|
4
|
+
*
|
|
5
|
+
* Run via stdio transport (compatible with `claude -p --mcp-config`):
|
|
6
|
+
* node dist/mcp/server.js
|
|
7
|
+
*
|
|
8
|
+
* MCP config example:
|
|
9
|
+
* {
|
|
10
|
+
* "mcpServers": {
|
|
11
|
+
* "loop": {
|
|
12
|
+
* "command": "node",
|
|
13
|
+
* "args": ["./dist/mcp/server.js"],
|
|
14
|
+
* "env": { "LOOP_DIR": "./loops" }
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* Tools exposed:
|
|
20
|
+
* write_loop — write a .loop file to disk
|
|
21
|
+
* read_loop — read an existing .loop file
|
|
22
|
+
* list_loops — list .loop files in a directory
|
|
23
|
+
* validate_loop — parse a .loop file and report any errors
|
|
24
|
+
*/
|
|
25
|
+
import fs from 'node:fs';
|
|
26
|
+
import path from 'node:path';
|
|
27
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
28
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
29
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
30
|
+
import { parseLoopFile } from '../loopfile.js';
|
|
31
|
+
const LOOP_DIR = process.env.LOOP_DIR ?? '.loop';
|
|
32
|
+
const server = new Server({ name: 'loop-sdk', version: '0.1.0' }, { capabilities: { tools: {} } });
|
|
33
|
+
// ── Tool definitions ──────────────────────────────────────────────────────────
|
|
34
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
35
|
+
tools: [
|
|
36
|
+
{
|
|
37
|
+
name: 'write_loop',
|
|
38
|
+
description: 'Write a .loop file to disk. Use this to create or update agentic loop definitions. The content must be a valid .loop file (YAML front-matter + ## step sections).',
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
filename: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'Filename for the .loop file, e.g. "research.loop". Will be created in the LOOP_DIR.',
|
|
45
|
+
},
|
|
46
|
+
content: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description: 'Full .loop file content including YAML front-matter and ## step sections.',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ['filename', 'content'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'read_loop',
|
|
56
|
+
description: 'Read the contents of a .loop file.',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
filename: { type: 'string', description: 'Filename of the .loop file to read.' },
|
|
61
|
+
},
|
|
62
|
+
required: ['filename'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'list_loops',
|
|
67
|
+
description: 'List all .loop files available in the loop directory.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'validate_loop',
|
|
75
|
+
description: 'Parse a .loop file and report any syntax or schema errors without running it.',
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties: {
|
|
79
|
+
filename: { type: 'string', description: 'Filename of the .loop file to validate.' },
|
|
80
|
+
},
|
|
81
|
+
required: ['filename'],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
}));
|
|
86
|
+
// ── Tool handlers ─────────────────────────────────────────────────────────────
|
|
87
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
88
|
+
const { name, arguments: args } = req.params;
|
|
89
|
+
const a = (args ?? {});
|
|
90
|
+
try {
|
|
91
|
+
switch (name) {
|
|
92
|
+
case 'write_loop': {
|
|
93
|
+
const filePath = resolveLoop(a.filename);
|
|
94
|
+
// Validate before writing
|
|
95
|
+
parseLoopFile(a.content);
|
|
96
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
97
|
+
fs.writeFileSync(filePath, a.content, 'utf8');
|
|
98
|
+
return text(`Written: ${filePath}\n\nParsed steps: ${parseLoopFile(a.content).steps.map(s => ` • ${s.name} (${s.action})`).join('\n')}`);
|
|
99
|
+
}
|
|
100
|
+
case 'read_loop': {
|
|
101
|
+
const filePath = resolveLoop(a.filename);
|
|
102
|
+
if (!fs.existsSync(filePath))
|
|
103
|
+
return text(`File not found: ${filePath}`);
|
|
104
|
+
return text(fs.readFileSync(filePath, 'utf8'));
|
|
105
|
+
}
|
|
106
|
+
case 'list_loops': {
|
|
107
|
+
if (!fs.existsSync(LOOP_DIR))
|
|
108
|
+
return text(`Loop directory does not exist: ${LOOP_DIR}`);
|
|
109
|
+
const files = fs.readdirSync(LOOP_DIR).filter(f => f.endsWith('.loop'));
|
|
110
|
+
if (files.length === 0)
|
|
111
|
+
return text('No .loop files found.');
|
|
112
|
+
const summaries = files.map(f => {
|
|
113
|
+
try {
|
|
114
|
+
const schema = parseLoopFile(fs.readFileSync(path.join(LOOP_DIR, f), 'utf8'));
|
|
115
|
+
const steps = schema.steps.map(s => `${s.name}(${s.action})`).join(' → ');
|
|
116
|
+
return `${f} [${schema.meta.name}] ${steps}`;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return `${f} (parse error)`;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return text(summaries.join('\n'));
|
|
123
|
+
}
|
|
124
|
+
case 'validate_loop': {
|
|
125
|
+
const filePath = resolveLoop(a.filename);
|
|
126
|
+
if (!fs.existsSync(filePath))
|
|
127
|
+
return text(`File not found: ${filePath}`);
|
|
128
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
129
|
+
try {
|
|
130
|
+
const schema = parseLoopFile(content);
|
|
131
|
+
const lines = [
|
|
132
|
+
`✓ Valid — "${schema.meta.name}" (${schema.steps.length} steps)`,
|
|
133
|
+
...schema.steps.map((s, i) => ` ${i + 1}. ${s.name} action=${s.action}${s.retries ? ` retries=${s.retries}` : ''}${s.skipOnError ? ' skipOnError' : ''}`)
|
|
134
|
+
];
|
|
135
|
+
return text(lines.join('\n'));
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
return text(`✗ Invalid: ${err.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
default:
|
|
142
|
+
return text(`Unknown tool: ${name}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
return text(`Error: ${err.message}`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
150
|
+
function resolveLoop(filename) {
|
|
151
|
+
const safe = path.basename(filename); // prevent path traversal
|
|
152
|
+
return path.resolve(LOOP_DIR, safe.endsWith('.loop') ? safe : `${safe}.loop`);
|
|
153
|
+
}
|
|
154
|
+
function text(content) {
|
|
155
|
+
return { content: [{ type: 'text', text: content }] };
|
|
156
|
+
}
|
|
157
|
+
// ── Start ─────────────────────────────────────────────────────────────────────
|
|
158
|
+
const transport = new StdioServerTransport();
|
|
159
|
+
await server.connect(transport);
|
|
160
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
package/dist/notify.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Loop } from './loop.js';
|
|
2
|
+
export interface NotifyOptions {
|
|
3
|
+
/** Notification title. Default: 'loop-sdk'. */
|
|
4
|
+
title?: string;
|
|
5
|
+
/** Subtitle shown below the title. */
|
|
6
|
+
subtitle?: string;
|
|
7
|
+
/** Play the default notification sound. Default: false. */
|
|
8
|
+
sound?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Send a macOS notification via osascript. No-op on non-macOS platforms.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* notify('Loop completed in 12.3s', { title: 'My Loop', sound: true })
|
|
15
|
+
*/
|
|
16
|
+
export declare function notify(message: string, opts?: NotifyOptions): void;
|
|
17
|
+
export interface NotifyOnOptions {
|
|
18
|
+
/** Notify when the loop starts. Default: false. */
|
|
19
|
+
onStart?: boolean;
|
|
20
|
+
/** Notify when the loop completes successfully. Default: true. */
|
|
21
|
+
onComplete?: boolean;
|
|
22
|
+
/** Notify when the loop fails. Default: true. */
|
|
23
|
+
onError?: boolean;
|
|
24
|
+
/** Notify when a step fails (in addition to the loop-level error). Default: false. */
|
|
25
|
+
onStepError?: boolean;
|
|
26
|
+
/** Notification title. Default: loop name. */
|
|
27
|
+
title?: string;
|
|
28
|
+
/** Play the default notification sound on completion. Default: false. */
|
|
29
|
+
sound?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Attach macOS notification listeners to a loop. Call before loop.run().
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* import { notifyOn } from 'loop-sdk/notify'
|
|
36
|
+
*
|
|
37
|
+
* const loop = new Loop('research')
|
|
38
|
+
* notifyOn(loop, { onComplete: true, onError: true, title: 'Research Loop' })
|
|
39
|
+
*
|
|
40
|
+
* await loop.run({ session })
|
|
41
|
+
*/
|
|
42
|
+
export declare function notifyOn(loop: Loop, opts?: NotifyOnOptions): void;
|
|
43
|
+
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGrC,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,IAAI,CAYtE;AAID,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,kEAAkE;IAClE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yEAAyE;IACzE,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,GAAE,eAAoB,GAAG,IAAI,CAuCrE"}
|
package/dist/notify.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Send a macOS notification via osascript. No-op on non-macOS platforms.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* notify('Loop completed in 12.3s', { title: 'My Loop', sound: true })
|
|
7
|
+
*/
|
|
8
|
+
export function notify(message, opts = {}) {
|
|
9
|
+
if (process.platform !== 'darwin')
|
|
10
|
+
return;
|
|
11
|
+
const title = opts.title ?? 'loop-sdk';
|
|
12
|
+
const subtitle = opts.subtitle ?? '';
|
|
13
|
+
const sound = opts.sound ?? false;
|
|
14
|
+
let script = `display notification ${osa(message)} with title ${osa(title)}`;
|
|
15
|
+
if (subtitle)
|
|
16
|
+
script += ` subtitle ${osa(subtitle)}`;
|
|
17
|
+
if (sound)
|
|
18
|
+
script += ` sound name "default"`;
|
|
19
|
+
spawnSync('osascript', ['-e', script], { stdio: 'ignore' });
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Attach macOS notification listeners to a loop. Call before loop.run().
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* import { notifyOn } from 'loop-sdk/notify'
|
|
26
|
+
*
|
|
27
|
+
* const loop = new Loop('research')
|
|
28
|
+
* notifyOn(loop, { onComplete: true, onError: true, title: 'Research Loop' })
|
|
29
|
+
*
|
|
30
|
+
* await loop.run({ session })
|
|
31
|
+
*/
|
|
32
|
+
export function notifyOn(loop, opts = {}) {
|
|
33
|
+
const title = opts.title ?? loop.name;
|
|
34
|
+
const onStart = opts.onStart ?? false;
|
|
35
|
+
const onComplete = opts.onComplete ?? true;
|
|
36
|
+
const onError = opts.onError ?? true;
|
|
37
|
+
const onStepError = opts.onStepError ?? false;
|
|
38
|
+
const sound = opts.sound ?? false;
|
|
39
|
+
if (onStart) {
|
|
40
|
+
loop.on('loop:start', ({ totalSteps }) => {
|
|
41
|
+
notify(`Starting — ${totalSteps} step${totalSteps === 1 ? '' : 's'}`, { title, subtitle: 'Started' });
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (onComplete) {
|
|
45
|
+
loop.on('loop:complete', ({ status, durationMs, stepsCompleted }) => {
|
|
46
|
+
if (status !== 'completed')
|
|
47
|
+
return;
|
|
48
|
+
notify(`Finished in ${(durationMs / 1000).toFixed(1)}s — ${stepsCompleted} steps`, { title, subtitle: 'Completed', sound });
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (onError) {
|
|
52
|
+
loop.on('loop:complete', ({ status, durationMs }) => {
|
|
53
|
+
if (status !== 'failed')
|
|
54
|
+
return;
|
|
55
|
+
notify(`Failed after ${(durationMs / 1000).toFixed(1)}s`, { title, subtitle: 'Failed', sound: true });
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (onStepError) {
|
|
59
|
+
loop.on('step:error', ({ step, error }) => {
|
|
60
|
+
notify(error.message, { title, subtitle: `Step failed: ${step}`, sound: true });
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
65
|
+
function osa(s) {
|
|
66
|
+
return `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, ' ')}"`;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Plugin } from '../loop.js';
|
|
2
|
+
export interface RetryPluginOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Total number of attempts per step (including the first).
|
|
5
|
+
* e.g. attempts: 3 means 1 initial + 2 retries. Default: 3.
|
|
6
|
+
*/
|
|
7
|
+
attempts?: number;
|
|
8
|
+
/** Milliseconds to wait before each retry. Default: 1000. */
|
|
9
|
+
delay?: number;
|
|
10
|
+
/**
|
|
11
|
+
* How to scale the delay across retry attempts.
|
|
12
|
+
* - 'flat' — same delay every time
|
|
13
|
+
* - 'linear' — delay * attempt (1x, 2x, 3x …)
|
|
14
|
+
* - 'exponential' — delay * 2^attempt (1x, 2x, 4x …)
|
|
15
|
+
* Default: 'flat'
|
|
16
|
+
*/
|
|
17
|
+
backoff?: 'flat' | 'linear' | 'exponential';
|
|
18
|
+
/** Only retry if this predicate returns true. Default: always retry. */
|
|
19
|
+
retryIf?: (err: Error) => boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Global retry plugin — retries every failing step up to `attempts` times.
|
|
23
|
+
*
|
|
24
|
+
* For per-step retry control, use the `retries` / `retryDelay` / `retryBackoff`
|
|
25
|
+
* options on `loop.step()` instead. Both can coexist: step-level retries run
|
|
26
|
+
* first, then the plugin gets a chance if the step is still failing.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* import { RetryPlugin } from 'loop-sdk/plugins'
|
|
30
|
+
*
|
|
31
|
+
* loop.use(RetryPlugin({ attempts: 3, delay: 1000, backoff: 'exponential' }))
|
|
32
|
+
*
|
|
33
|
+
* // Only retry network errors:
|
|
34
|
+
* loop.use(RetryPlugin({
|
|
35
|
+
* attempts: 5,
|
|
36
|
+
* delay: 2000,
|
|
37
|
+
* retryIf: (err) => err.message.includes('fetch failed'),
|
|
38
|
+
* }))
|
|
39
|
+
*/
|
|
40
|
+
export declare function RetryPlugin(opts?: RetryPluginOptions): Plugin;
|
|
41
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/plugins/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,aAAa,CAAA;IAC3C,wEAAwE;IACxE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAA;CAClC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,MAAM,CAwCjE"}
|