MCPcopy Index your code
hub / github.com/ByBrawe/opencode-loop / addLoop

Function addLoop

src/index.js:1376–1408  ·  view source on GitHub ↗
(directory, client, sessionID, args, defaults = {})

Source from the content-addressed store, hash-verified

1374}
1375
1376function normalizeActionForCompare(value) {
1377 return String(value || "").replace(/\s+/g, " ").trim()
1378}
1379
1380function sameLoopDefinition(a, b) {
1381 if (!a || !b) return false
1382 return (a.name || "") === (b.name || "") &&
1383 Number(a.intervalMs || 0) === Number(b.intervalMs || 0) &&
1384 normalizeActionForCompare(a.action) === normalizeActionForCompare(b.action) &&
1385 normalizeActionForCompare(a.kind) === normalizeActionForCompare(b.kind) &&
1386 normalizeActionForCompare(a.promptFile) === normalizeActionForCompare(b.promptFile)
1387}
1388
1389async function addLoop(directory, client, sessionID, args, defaults = {}) {
1390 const parsed = parseLoopArgs(args, defaults)
1391 if (!parsed.ok) { await toast(client, parsed.error, "warning"); return }
1392 if (parsed.job.watchPaths.length) parsed.job.watchSnapshot = await snapshotPaths(directory, parsed.job.watchPaths)
1393 if (!parsed.job.activeRecoveryMs) parsed.job.activeRecoveryMs = Math.max(20_000, Math.min(90_000, (parsed.job.intervalMs || 0) + 10_000))
1394 if (parsed.job.dryRun) { await toast(client, `Loop dry run: ${jobLabel(parsed.job)}`, "info"); await say(client, sessionID, "OpenCode loop dry run:\n```json\n" + JSON.stringify(parsed.job, null, 2) + "\n```"); return }
1395 const state = await readState(directory, sessionID)
1396 const jobs = Array.isArray(state.jobs) ? state.jobs : []
1397
1398 // Default behavior is replace/upsert, not append forever. This prevents duplicate
1399 // loops when OpenCode emits both command.execute.before and command.executed,
1400 // and it matches the common expectation that /loop configures the current loop.
1401 let replaced = false
1402 if (!parsed.job.multi) {
1403 const targetName = parsed.job.name || "default"
1404 parsed.job.name = targetName
1405 state.jobs = jobs.filter((existing) => {
1406 const existingName = existing.name || "default"
1407 const shouldReplace = existingName === targetName || sameLoopDefinition(existing, parsed.job)
1408 if (shouldReplace) replaced = true
1409 return !shouldReplace
1410 })
1411 } else {

Callers 2

addGoalFunction · 0.85
handleCommandFunction · 0.85

Calls 11

parseLoopArgsFunction · 0.85
toastFunction · 0.85
snapshotPathsFunction · 0.85
jobLabelFunction · 0.85
sayFunction · 0.85
readStateFunction · 0.85
sameLoopDefinitionFunction · 0.85
writeStateFunction · 0.85
scheduleDueWorkFunction · 0.85
scheduleIdleWorkFunction · 0.85
appendLoopLogFunction · 0.85

Tested by

no test coverage detected