regent-code 3.0.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/.github/workflows/ci.yml +38 -0
- package/.opencode/INSTALL.md +82 -0
- package/.opencode/agents/regent-explore.md +10 -0
- package/.opencode/agents/regent-general.md +8 -0
- package/.opencode/commands/accept.md +15 -0
- package/.opencode/commands/delegate.md +16 -0
- package/.opencode/commands/diagnose.md +19 -0
- package/.opencode/commands/orchestrate.md +22 -0
- package/.opencode/commands/plan.md +20 -0
- package/.opencode/commands/research.md +12 -0
- package/.opencode/commands/review.md +23 -0
- package/.opencode/commands/ship.md +18 -0
- package/.opencode/commands/spec.md +18 -0
- package/.opencode/commands/status.md +13 -0
- package/.opencode/commands/tdd.md +18 -0
- package/.opencode/commands/verify.md +18 -0
- package/.opencode/package.json +6 -0
- package/.opencode/plugins/regent.js +1623 -0
- package/.opencode/skills/code-review/SKILL.md +89 -0
- package/.opencode/skills/diagnose/SKILL.md +118 -0
- package/.opencode/skills/grilling/SKILL.md +59 -0
- package/.opencode/skills/handoff/SKILL.md +61 -0
- package/.opencode/skills/merge-conflicts/SKILL.md +39 -0
- package/.opencode/skills/orchestrator/SKILL.md +206 -0
- package/.opencode/skills/prototype/SKILL.md +40 -0
- package/.opencode/skills/ship/SKILL.md +42 -0
- package/.opencode/skills/spec/SKILL.md +61 -0
- package/.opencode/skills/tdd/SKILL.md +102 -0
- package/.opencode/skills/tickets/SKILL.md +71 -0
- package/.opencode/skills/using-regent/SKILL.md +71 -0
- package/.opencode/skills/verification-before-completion/SKILL.md +82 -0
- package/.opencode/skills/wizard/SKILL.md +45 -0
- package/.opencode/skills/worktrees/SKILL.md +39 -0
- package/.opencode/skills/zoom-out/SKILL.md +38 -0
- package/.prettierignore +2 -0
- package/.prettierrc +7 -0
- package/AGENTS.md +38 -0
- package/CONSTITUTION.md +101 -0
- package/LICENSE +21 -0
- package/README.md +264 -0
- package/docs/contributing.md +86 -0
- package/docs/superpowers/plans/windows-guardrail/plan.md +49 -0
- package/docs/superpowers/plans/windows-guardrail/tasks.md +58 -0
- package/docs/superpowers/specs/2026-06-12-regent-health-audit-design.md +49 -0
- package/docs/superpowers/specs/2026-08-26-windows-guardrail.md +66 -0
- package/eslint.config.js +23 -0
- package/handoff.md +100 -0
- package/mcp/cli.js +9 -0
- package/mcp/index.js +805 -0
- package/mcp/install.js +204 -0
- package/mcp/prompts.js +99 -0
- package/mcp/shared.js +428 -0
- package/package.json +52 -0
- package/tsconfig.json +17 -0
package/mcp/shared.js
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// ── Dispatch safety limits ──
|
|
5
|
+
export const MAX_DISPATCH_ITEMS = 10;
|
|
6
|
+
export const MAX_STRING_LENGTH = 8000;
|
|
7
|
+
export const MAX_ID_LENGTH = 200;
|
|
8
|
+
export const MAX_DISPATCHES_PER_WINDOW = 20;
|
|
9
|
+
export const DISPATCH_WINDOW_MS = 60000;
|
|
10
|
+
|
|
11
|
+
// ── Typed recovery actions ──
|
|
12
|
+
export const RecoveryAction = {
|
|
13
|
+
RETRY: 'retry',
|
|
14
|
+
ABORT: 'abort',
|
|
15
|
+
SKIP: 'skip',
|
|
16
|
+
ESCALATE: 'escalate',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function classifyError(err) {
|
|
20
|
+
const msg = (err?.message || String(err)).toLowerCase();
|
|
21
|
+
if (msg.includes('timeout') || msg.includes('rate limit') || msg.includes('too many'))
|
|
22
|
+
return RecoveryAction.RETRY;
|
|
23
|
+
if (
|
|
24
|
+
msg.includes('not found') ||
|
|
25
|
+
msg.includes('missing') ||
|
|
26
|
+
msg.includes('invalid') ||
|
|
27
|
+
msg.includes('enoent')
|
|
28
|
+
)
|
|
29
|
+
return RecoveryAction.ABORT;
|
|
30
|
+
if (msg.includes('permission') || msg.includes('denied') || msg.includes('unauthorized'))
|
|
31
|
+
return RecoveryAction.ESCALATE;
|
|
32
|
+
return RecoveryAction.RETRY;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ── Retry with exponential backoff ──
|
|
36
|
+
export async function withRetry(fn, maxRetries = 2) {
|
|
37
|
+
let lastError;
|
|
38
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
39
|
+
try {
|
|
40
|
+
return await fn();
|
|
41
|
+
} catch (err) {
|
|
42
|
+
lastError = err;
|
|
43
|
+
const action = classifyError(err);
|
|
44
|
+
if (action === RecoveryAction.ABORT || action === RecoveryAction.SKIP) throw err;
|
|
45
|
+
if (attempt < maxRetries) {
|
|
46
|
+
const delay = Math.min(1000 * Math.pow(2, attempt), 4000);
|
|
47
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
throw lastError;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ── Evidence tracking ──
|
|
55
|
+
/** @param {string} absolutePath @returns {{ exists: boolean, size?: number, mtimeMs?: number }} */
|
|
56
|
+
export function fileFingerprint(absolutePath) {
|
|
57
|
+
try {
|
|
58
|
+
const stat = fs.statSync(absolutePath);
|
|
59
|
+
return { exists: true, size: stat.size, mtimeMs: stat.mtimeMs };
|
|
60
|
+
} catch {
|
|
61
|
+
return { exists: false };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string[]} files
|
|
67
|
+
* @param {string} directory
|
|
68
|
+
* @returns {Array<{ file: string, absolute: string, stat: { exists: boolean, size?: number, mtimeMs?: number } }>}
|
|
69
|
+
*/
|
|
70
|
+
function buildFingerprints(files, directory = '') {
|
|
71
|
+
return files.map((file) => {
|
|
72
|
+
const absolute = path.isAbsolute(file) ? file : path.resolve(directory || process.cwd(), file);
|
|
73
|
+
return { file, absolute, stat: fileFingerprint(absolute) };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Grade verification evidence by the current state of the files it points at.
|
|
79
|
+
* An entry is FRESH while every file it claimed still matches the fingerprint
|
|
80
|
+
* recorded when the evidence was captured; STALE when any of them changed.
|
|
81
|
+
*
|
|
82
|
+
* @param {any[]} entries
|
|
83
|
+
* @returns {{ total: number, fresh: number, stale: number, missing: number, stale_files: string[] }}
|
|
84
|
+
*/
|
|
85
|
+
export function evidenceFreshness(entries) {
|
|
86
|
+
const grades = [];
|
|
87
|
+
const staleFiles = [];
|
|
88
|
+
for (const entry of entries) {
|
|
89
|
+
if (entry.verified !== true) continue;
|
|
90
|
+
if (!Array.isArray(entry.fingerprints) || entry.fingerprints.length === 0) {
|
|
91
|
+
grades.push('missing');
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
let checkable = 0;
|
|
95
|
+
let stale = false;
|
|
96
|
+
for (const fp of entry.fingerprints) {
|
|
97
|
+
if (fp?.stat?.exists !== true) continue;
|
|
98
|
+
checkable++;
|
|
99
|
+
const current = fileFingerprint(fp.absolute);
|
|
100
|
+
if (!current.exists || current.size !== fp.stat.size || current.mtimeMs !== fp.stat.mtimeMs) {
|
|
101
|
+
stale = true;
|
|
102
|
+
staleFiles.push(fp.file);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (checkable === 0) {
|
|
106
|
+
grades.push('missing');
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
grades.push(stale ? 'stale' : 'fresh');
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
total: grades.length,
|
|
113
|
+
fresh: grades.filter((grade) => grade === 'fresh').length,
|
|
114
|
+
stale: grades.filter((grade) => grade === 'stale').length,
|
|
115
|
+
missing: grades.filter((grade) => grade === 'missing').length,
|
|
116
|
+
stale_files: [...new Set(staleFiles)],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function recordEvidence(sessionId, files, directory = '') {
|
|
121
|
+
if (files.length > 0) {
|
|
122
|
+
evidenceLog.push({
|
|
123
|
+
sessionId,
|
|
124
|
+
files,
|
|
125
|
+
timestamp: Date.now(),
|
|
126
|
+
verified: false,
|
|
127
|
+
fingerprints: buildFingerprints(files, directory),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function markEvidenceVerified(sessionId) {
|
|
133
|
+
for (const entry of evidenceLog) {
|
|
134
|
+
if (entry.sessionId === sessionId) entry.verified = true;
|
|
135
|
+
}
|
|
136
|
+
for (const [sid, data] of sessionFileChanges) {
|
|
137
|
+
if (sid === sessionId) data.verified = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function evidenceForScope() {
|
|
142
|
+
return evidenceLog;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** @param {string} text @returns {string} */
|
|
146
|
+
export function redactSecrets(text) {
|
|
147
|
+
return String(text)
|
|
148
|
+
.replace(/\bauthorization\s*:\s*(?:bearer\s+)?[^\s,;}\]]+/gi, 'authorization: <REDACTED>')
|
|
149
|
+
.replace(
|
|
150
|
+
/((?:api[_-]?key|api key|secret|token|passwd|password|authorization|bearer)(?:\s+provided)?(?:\s*[:=]\s*|\s+(?:is|was|are|were)\s+))["']?[^"'\s,;}\]]+/gi,
|
|
151
|
+
'$1<REDACTED>',
|
|
152
|
+
)
|
|
153
|
+
.replace(/\beyJ[A-Za-z0-9_-]{10,}(?:\.[A-Za-z0-9_-]{10,}){2}\b/g, '<REDACTED>')
|
|
154
|
+
.replace(
|
|
155
|
+
/\b(?:sk-(?:ant|proj|live)-[A-Za-z0-9]{8,}|sk-[A-Za-z0-9]{16,}|sk_live_[A-Za-z0-9]{16,}|ghp_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{6,}|AKIA[0-9A-Z]{16})\b/g,
|
|
156
|
+
'<REDACTED>',
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Sensitive focus-path protection (shared with explore) ──
|
|
161
|
+
const SENSITIVE_FOCUS_NAMES = new Set([
|
|
162
|
+
'.aws',
|
|
163
|
+
'.azure',
|
|
164
|
+
'.docker',
|
|
165
|
+
'.htpasswd',
|
|
166
|
+
'.kube',
|
|
167
|
+
'.netrc',
|
|
168
|
+
'.npmrc',
|
|
169
|
+
'.pypirc',
|
|
170
|
+
'.ssh',
|
|
171
|
+
'.yarnrc',
|
|
172
|
+
'.yarnrc.yml',
|
|
173
|
+
'.git-credentials',
|
|
174
|
+
'authorized_keys',
|
|
175
|
+
'cookies',
|
|
176
|
+
'cookies.json',
|
|
177
|
+
'known_hosts',
|
|
178
|
+
'login.json',
|
|
179
|
+
'auth.json',
|
|
180
|
+
'oauth.json',
|
|
181
|
+
'passwd',
|
|
182
|
+
'password',
|
|
183
|
+
'passwords',
|
|
184
|
+
'secrets.json',
|
|
185
|
+
'secret.json',
|
|
186
|
+
'session.json',
|
|
187
|
+
'shadow',
|
|
188
|
+
'token.json',
|
|
189
|
+
'cert',
|
|
190
|
+
'certs',
|
|
191
|
+
'certificates',
|
|
192
|
+
'key',
|
|
193
|
+
'keys',
|
|
194
|
+
'private',
|
|
195
|
+
'private-keys',
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
const SENSITIVE_FOCUS_MATERIAL =
|
|
199
|
+
/(?:^|[._-])(?:certificate|cert|certificates|certs|key|keys|private(?:[._-]?key)?|service[-_]?account|firebase[-_]?adminsdk)(?:$|[._-])/i;
|
|
200
|
+
const SENSITIVE_FOCUS_CREDENTIAL =
|
|
201
|
+
/(?:^|[._-])(?:auth|authorization|cookie|cookies|credential|credentials|oauth|pass(?:word|wd)s?|secret|secrets|session|sessions|token|tokens)(?:$|[._-])/i;
|
|
202
|
+
const SENSITIVE_FOCUS_KEY = /^id_(?:rsa|dsa|ecdsa|ed25519)(?:$|[._-])/i;
|
|
203
|
+
const SENSITIVE_FOCUS_EXTENSION =
|
|
204
|
+
/\.(?:asc|cer|crt|csr|der|gpg|jks|keystore|key|p12|p8|pem|pgp|pfx|ppk)$/i;
|
|
205
|
+
|
|
206
|
+
export function isSensitiveFocusPath(focusPath, worktreeRoot) {
|
|
207
|
+
const relative = path.relative(worktreeRoot, focusPath);
|
|
208
|
+
const segments = relative
|
|
209
|
+
.split(path.sep)
|
|
210
|
+
.filter(Boolean)
|
|
211
|
+
.map((segment) => segment.toLowerCase());
|
|
212
|
+
const name = segments.at(-1) || '';
|
|
213
|
+
const parentSegments = segments.slice(0, -1);
|
|
214
|
+
|
|
215
|
+
if (
|
|
216
|
+
parentSegments.some(
|
|
217
|
+
(segment) =>
|
|
218
|
+
segment === '.git' ||
|
|
219
|
+
SENSITIVE_FOCUS_NAMES.has(segment) ||
|
|
220
|
+
SENSITIVE_FOCUS_CREDENTIAL.test(segment) ||
|
|
221
|
+
SENSITIVE_FOCUS_MATERIAL.test(segment),
|
|
222
|
+
)
|
|
223
|
+
) {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
if (name === '.env' || name.startsWith('.env.')) return true;
|
|
227
|
+
if (name === '.git' || SENSITIVE_FOCUS_NAMES.has(name)) return true;
|
|
228
|
+
if (SENSITIVE_FOCUS_CREDENTIAL.test(name) && !/\.(?:log|md|txt)$/i.test(name)) return true;
|
|
229
|
+
return (
|
|
230
|
+
SENSITIVE_FOCUS_MATERIAL.test(name) ||
|
|
231
|
+
SENSITIVE_FOCUS_KEY.test(name) ||
|
|
232
|
+
SENSITIVE_FOCUS_EXTENSION.test(name)
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ── Subagent text-response parsing ──
|
|
237
|
+
/**
|
|
238
|
+
* @param {string} text
|
|
239
|
+
* @returns {{ status: string, concerns: string[], filesChanged: string[] }}
|
|
240
|
+
*/
|
|
241
|
+
export function parseSubagentTextResponse(text) {
|
|
242
|
+
let status = 'done';
|
|
243
|
+
let concerns = [];
|
|
244
|
+
|
|
245
|
+
if (text.includes('BLOCKED')) {
|
|
246
|
+
status = 'blocked';
|
|
247
|
+
} else if (text.includes('NEEDS_CONTEXT')) {
|
|
248
|
+
status = 'needs_context';
|
|
249
|
+
} else if (text.includes('CONCERN:')) {
|
|
250
|
+
status = 'done_with_concerns';
|
|
251
|
+
concerns = text.match(/CONCERN:.*$/gm)?.map((c) => c.replace('CONCERN:', '').trim()) || [];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const pathPattern =
|
|
255
|
+
/(?:^|\n)(?:[\w./\\-]+\.[a-zA-Z0-9]+|[\w.-]+(?:[\\/][\w.-]+)+(?:\.[a-zA-Z0-9]+)?|^[A-Za-z][\w-]+\.[\w-]+|^[A-Za-z][\w-]+(?:\.[\w-]+)*$(?!\.))/gm;
|
|
256
|
+
const matches = text.match(pathPattern);
|
|
257
|
+
const filesChanged = (matches || [])
|
|
258
|
+
.map((f) => f.trim())
|
|
259
|
+
.filter(
|
|
260
|
+
(f) =>
|
|
261
|
+
!f.startsWith('CONCERN:') &&
|
|
262
|
+
!f.startsWith('NEEDS_CONTEXT') &&
|
|
263
|
+
!f.startsWith('BLOCKED') &&
|
|
264
|
+
!/^\d+\.\s/.test(f),
|
|
265
|
+
)
|
|
266
|
+
.slice(0, 20);
|
|
267
|
+
|
|
268
|
+
return { status, concerns, filesChanged };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function unwrapData(result) {
|
|
272
|
+
return result?.data ?? result;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ── State (single MCP process scope; no session lineage) ──
|
|
276
|
+
/** @type {Map<string, { taskId?: string, files: string[], timestamp: number, verified: boolean }>} */
|
|
277
|
+
export const sessionFileChanges = new Map();
|
|
278
|
+
/** @type {any[]} */
|
|
279
|
+
export const evidenceLog = [];
|
|
280
|
+
/** @type {Map<string, number[]>} */
|
|
281
|
+
const dispatchTimesByKey = new Map();
|
|
282
|
+
/** @type {Map<string, { failures: number, recoveryReady?: boolean, halfOpen?: boolean }>} */
|
|
283
|
+
const circuitStateByKey = new Map();
|
|
284
|
+
/** @type {Set<string>} */
|
|
285
|
+
export const workerSessionIds = new Set();
|
|
286
|
+
|
|
287
|
+
export function dispatchRateLimit(key) {
|
|
288
|
+
const now = Date.now();
|
|
289
|
+
const times = dispatchTimesByKey.get(key) || [];
|
|
290
|
+
while (times.length > 0 && times[0] < now - DISPATCH_WINDOW_MS) times.shift();
|
|
291
|
+
if (times.length >= MAX_DISPATCHES_PER_WINDOW) return false;
|
|
292
|
+
times.push(now);
|
|
293
|
+
dispatchTimesByKey.set(key, times);
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function circuitIsOpen(key) {
|
|
298
|
+
const state = circuitStateByKey.get(key);
|
|
299
|
+
if (!state || (state.failures || 0) < 2) return false;
|
|
300
|
+
|
|
301
|
+
// Block once when open, then permit one half-open recovery attempt.
|
|
302
|
+
if (state.recoveryReady) {
|
|
303
|
+
circuitStateByKey.set(key, { failures: state.failures, recoveryReady: false, halfOpen: true });
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
circuitStateByKey.set(key, { failures: state.failures, recoveryReady: true });
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function recordCircuitResult(key, success) {
|
|
312
|
+
if (success) {
|
|
313
|
+
circuitStateByKey.delete(key);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const current = circuitStateByKey.get(key) || { failures: 0 };
|
|
318
|
+
circuitStateByKey.set(key, { failures: current.failures + 1 });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function resetState() {
|
|
322
|
+
evidenceLog.length = 0;
|
|
323
|
+
sessionFileChanges.clear();
|
|
324
|
+
dispatchTimesByKey.clear();
|
|
325
|
+
circuitStateByKey.clear();
|
|
326
|
+
workerSessionIds.clear();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ── MCP content helpers ──
|
|
330
|
+
/** @param {unknown} payload @returns {{ content: Array<{ type: 'text', text: string }> }} */
|
|
331
|
+
export const toContent = (payload) => ({
|
|
332
|
+
content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
/** @param {string} message @returns {{ status: string, output: string, concerns: string[], files_changed: string[] }} */
|
|
336
|
+
export function structuredBlockedResult(message) {
|
|
337
|
+
return {
|
|
338
|
+
status: 'blocked',
|
|
339
|
+
output: `Subagent error: ${message}`,
|
|
340
|
+
concerns: [],
|
|
341
|
+
files_changed: [],
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ── Tool input schemas (identical to the plugin's) ──
|
|
346
|
+
export const TOOL_INPUTS = {
|
|
347
|
+
delegate: {
|
|
348
|
+
type: 'object',
|
|
349
|
+
properties: {
|
|
350
|
+
task: { type: 'string', description: 'The specific task for the subagent to complete' },
|
|
351
|
+
context: { type: 'string', description: 'Background context for the task' },
|
|
352
|
+
expected_output: { type: 'string', description: 'What done looks like for the task' },
|
|
353
|
+
agent: { type: 'string', description: 'Optional child-capable agent ID override' },
|
|
354
|
+
},
|
|
355
|
+
required: ['task', 'context', 'expected_output'],
|
|
356
|
+
additionalProperties: false,
|
|
357
|
+
},
|
|
358
|
+
delegate_many: {
|
|
359
|
+
type: 'object',
|
|
360
|
+
properties: {
|
|
361
|
+
tasks: {
|
|
362
|
+
type: 'array',
|
|
363
|
+
items: {
|
|
364
|
+
type: 'object',
|
|
365
|
+
properties: {
|
|
366
|
+
id: { type: 'string', description: 'Unique identifier for this task' },
|
|
367
|
+
task: { type: 'string', description: 'What this subagent should do' },
|
|
368
|
+
context: { type: 'string', description: 'Background context for this task' },
|
|
369
|
+
expected_output: { type: 'string', description: 'What done looks like for this task' },
|
|
370
|
+
agent: { type: 'string', description: 'Optional child-capable agent ID override' },
|
|
371
|
+
},
|
|
372
|
+
required: ['id', 'task', 'context', 'expected_output'],
|
|
373
|
+
additionalProperties: false,
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
required: ['tasks'],
|
|
378
|
+
additionalProperties: false,
|
|
379
|
+
},
|
|
380
|
+
research: {
|
|
381
|
+
type: 'object',
|
|
382
|
+
properties: {
|
|
383
|
+
questions: {
|
|
384
|
+
type: 'array',
|
|
385
|
+
items: {
|
|
386
|
+
type: 'object',
|
|
387
|
+
properties: {
|
|
388
|
+
id: { type: 'string', description: 'Unique identifier' },
|
|
389
|
+
question: { type: 'string', description: 'The question to research' },
|
|
390
|
+
scope: { type: 'string', description: 'Optional narrowing scope' },
|
|
391
|
+
agent: { type: 'string', description: 'Optional child-capable agent ID override' },
|
|
392
|
+
},
|
|
393
|
+
required: ['id', 'question'],
|
|
394
|
+
additionalProperties: false,
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
required: ['questions'],
|
|
399
|
+
additionalProperties: false,
|
|
400
|
+
},
|
|
401
|
+
explore: {
|
|
402
|
+
type: 'object',
|
|
403
|
+
properties: {
|
|
404
|
+
query: { type: 'string', description: 'What to understand in the codebase' },
|
|
405
|
+
focus: { type: 'string', description: 'Optional directory path, file pattern, or topic' },
|
|
406
|
+
},
|
|
407
|
+
required: ['query'],
|
|
408
|
+
additionalProperties: false,
|
|
409
|
+
},
|
|
410
|
+
'changed-files': {
|
|
411
|
+
type: 'object',
|
|
412
|
+
properties: {
|
|
413
|
+
session_id: { type: 'string', description: 'Optional session ID filter' },
|
|
414
|
+
task_id: { type: 'string', description: 'Optional task ID filter' },
|
|
415
|
+
},
|
|
416
|
+
additionalProperties: false,
|
|
417
|
+
},
|
|
418
|
+
verify: {
|
|
419
|
+
type: 'object',
|
|
420
|
+
properties: {
|
|
421
|
+
requirements: { type: 'string', description: 'The requirements text' },
|
|
422
|
+
implementation_context: { type: 'string', description: 'What was built' },
|
|
423
|
+
session_id: { type: 'string', description: 'Optional session ID to verify' },
|
|
424
|
+
},
|
|
425
|
+
required: ['requirements', 'implementation_context'],
|
|
426
|
+
additionalProperties: false,
|
|
427
|
+
},
|
|
428
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "regent-code",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Agent orchestration for OpenCode. From idea to shipped — zero ceremony. Plugin + MCP server.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": ".opencode/plugins/regent.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./.opencode/plugins/regent.js",
|
|
9
|
+
"./mcp": "./mcp/index.js"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"regent-code": "mcp/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "nathwn12",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
18
|
+
"@opencode-ai/client": "0.0.0-beta-18314",
|
|
19
|
+
"@opencode-ai/plugin": "0.0.0-beta-18314",
|
|
20
|
+
"jsonc-parser": "3.3.1"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/nathwn12/regent-code.git"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"mcp": "node mcp/index.js",
|
|
28
|
+
"lint": "eslint .opencode/plugins/ mcp/ --max-warnings 0",
|
|
29
|
+
"lint:fix": "eslint .opencode/plugins/regent.js skills/ mcp/ --fix",
|
|
30
|
+
"format": "prettier --write .opencode/plugins/ skills/ mcp/ --no-error-on-unmatched-pattern",
|
|
31
|
+
"format:check": "prettier --check .opencode/plugins/ skills/ mcp/ --no-error-on-unmatched-pattern",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "node --test .opencode/tests/regent.test.js .opencode/tests/regent.live-test.js .opencode/tests/regent.v2.test.js .opencode/tests/regent.hybrid.v2.6.1.test.js .opencode/tests/regent.runtime.v2.6.1.test.js mcp/tests/mcp.test.js",
|
|
34
|
+
"verify": "npm run format:check && npm run lint && npm run typecheck && npm test"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@eslint/js": "^10.0.1",
|
|
38
|
+
"@types/node": "^25.9.3",
|
|
39
|
+
"eslint": "^10.4.1",
|
|
40
|
+
"eslint-config-prettier": "^10.1.8",
|
|
41
|
+
"globals": "^17.6.0",
|
|
42
|
+
"prettier": "^3.8.4",
|
|
43
|
+
"typescript": "^5.8.0"
|
|
44
|
+
},
|
|
45
|
+
"overrides": {
|
|
46
|
+
"eslint": {
|
|
47
|
+
"minimatch": {
|
|
48
|
+
"brace-expansion": "5.0.9"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"checkJs": true,
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noImplicitAny": false,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true
|
|
14
|
+
},
|
|
15
|
+
"include": [".opencode/plugins/regent.js", "mcp/**/*.js"],
|
|
16
|
+
"exclude": ["*.test.js"]
|
|
17
|
+
}
|