fifony 0.1.22 → 0.1.23
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 +33 -9
- package/app/dist/assets/KeyboardShortcutsHelp-BB5jLK_E.js +1 -0
- package/app/dist/assets/OnboardingWizard-xyM3Okjv.js +1 -0
- package/app/dist/assets/analytics.lazy-CfJXsh6r.js +1 -0
- package/app/dist/assets/index-C1QEwHZG.js +43 -0
- package/app/dist/assets/index-DjmUHXd1.css +1 -0
- package/app/dist/dinofffaur.png +0 -0
- package/app/dist/index.html +2 -2
- package/app/dist/service-worker.js +1 -1
- package/app/public/dinofffaur.png +0 -0
- package/dist/agent/cli-wrapper.js +1 -1
- package/dist/agent/cli-wrapper.js.map +1 -1
- package/dist/agent/run-local.js +163 -33
- package/dist/agent/run-local.js.map +1 -1
- package/dist/{chunk-O665NS5E.js → chunk-DD5BE2W6.js} +40 -43
- package/dist/chunk-DD5BE2W6.js.map +1 -0
- package/dist/chunk-DVU3CXWA.js +75 -0
- package/dist/chunk-DVU3CXWA.js.map +1 -0
- package/dist/chunk-G6CQK2WH.js +2137 -0
- package/dist/chunk-G6CQK2WH.js.map +1 -0
- package/dist/{chunk-VP6TGOMT.js → chunk-KT5V7N5H.js} +5219 -7032
- package/dist/chunk-KT5V7N5H.js.map +1 -0
- package/dist/chunk-NLIVWBNV.js +91 -0
- package/dist/chunk-NLIVWBNV.js.map +1 -0
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/issue-runner-6VFNHYJL.js +13 -0
- package/dist/issue-state-machine-NSDN4MV4.js +39 -0
- package/dist/mcp/server.js +6 -13
- package/dist/mcp/server.js.map +1 -1
- package/dist/{queue-workers-LAYOT4E5.js → queue-workers-VHYQYYLG.js} +3 -4
- package/dist/queue-workers-VHYQYYLG.js.map +1 -0
- package/dist/store-FH7L6KR2.js +56 -0
- package/dist/store-FH7L6KR2.js.map +1 -0
- package/package.json +1 -1
- package/FIFONY.md +0 -173
- package/app/dist/assets/KeyboardShortcutsHelp-DFstgyXD.js +0 -1
- package/app/dist/assets/OnboardingWizard-Daehu2Uj.js +0 -1
- package/app/dist/assets/analytics.lazy-C1-iSRM_.js +0 -1
- package/app/dist/assets/index-DbIrs0MK.css +0 -1
- package/app/dist/assets/index-O_FDwkw6.js +0 -43
- package/dist/chunk-F6JEQIP2.js +0 -449
- package/dist/chunk-F6JEQIP2.js.map +0 -1
- package/dist/chunk-O665NS5E.js.map +0 -1
- package/dist/chunk-VP6TGOMT.js.map +0 -1
- package/dist/issue-runner-MDCJ4G26.js +0 -11
- package/src/fixtures/agent-catalog.json +0 -208
- package/src/fixtures/skill-catalog.json +0 -67
- /package/dist/{issue-runner-MDCJ4G26.js.map → issue-runner-6VFNHYJL.js.map} +0 -0
- /package/dist/{queue-workers-LAYOT4E5.js.map → issue-state-machine-NSDN4MV4.js.map} +0 -0
package/dist/agent/run-local.js
CHANGED
|
@@ -6,38 +6,46 @@ import {
|
|
|
6
6
|
buildQueueTitle,
|
|
7
7
|
buildRuntimeState,
|
|
8
8
|
cleanStalePidFile,
|
|
9
|
-
cleanWorkspace,
|
|
10
9
|
closeStateStore,
|
|
11
|
-
|
|
10
|
+
createContainer,
|
|
12
11
|
deriveConfig,
|
|
13
|
-
detectAvailableProviders,
|
|
14
|
-
detectDefaultBranch,
|
|
15
12
|
detectProjectName,
|
|
16
|
-
|
|
13
|
+
ensureNotStale,
|
|
14
|
+
hasTerminalQueue,
|
|
17
15
|
hydrate,
|
|
18
|
-
initLogger,
|
|
19
|
-
initQueueWorkers,
|
|
20
16
|
initStateStore,
|
|
21
17
|
installGracefulShutdown,
|
|
22
18
|
isAgentStillRunning,
|
|
19
|
+
isShuttingDown,
|
|
23
20
|
loadPersistedState,
|
|
24
21
|
loadRuntimeSettings,
|
|
25
|
-
logger,
|
|
26
|
-
parsePort,
|
|
27
22
|
persistDetectedProvidersSetting,
|
|
28
23
|
persistState,
|
|
29
24
|
persistStateFull,
|
|
30
25
|
recoverPlanningSession,
|
|
31
|
-
resolveDefaultProvider,
|
|
32
26
|
resolveProjectMetadata,
|
|
33
|
-
scheduler,
|
|
34
|
-
setSkipSource,
|
|
35
27
|
startApiServer,
|
|
36
|
-
stopQueueWorkers,
|
|
37
28
|
syncRuntimeConfigSettings,
|
|
38
29
|
validateConfig
|
|
39
|
-
} from "../chunk-
|
|
40
|
-
import
|
|
30
|
+
} from "../chunk-KT5V7N5H.js";
|
|
31
|
+
import {
|
|
32
|
+
enqueueForExecution,
|
|
33
|
+
enqueueForPlanning,
|
|
34
|
+
enqueueForReview,
|
|
35
|
+
initQueueWorkers,
|
|
36
|
+
stopQueueWorkers
|
|
37
|
+
} from "../chunk-NLIVWBNV.js";
|
|
38
|
+
import {
|
|
39
|
+
cleanWorkspace,
|
|
40
|
+
computeMetrics,
|
|
41
|
+
detectAvailableProviders,
|
|
42
|
+
detectDefaultBranch,
|
|
43
|
+
executeTransition,
|
|
44
|
+
getProviderDefaultCommand,
|
|
45
|
+
hasDirtyState,
|
|
46
|
+
resolveDefaultProvider,
|
|
47
|
+
setSkipSource
|
|
48
|
+
} from "../chunk-G6CQK2WH.js";
|
|
41
49
|
import {
|
|
42
50
|
CLI_ARGS,
|
|
43
51
|
PACKAGE_ROOT,
|
|
@@ -45,17 +53,34 @@ import {
|
|
|
45
53
|
TARGET_ROOT,
|
|
46
54
|
debugBoot,
|
|
47
55
|
fail,
|
|
48
|
-
now
|
|
49
|
-
|
|
56
|
+
now,
|
|
57
|
+
parseIntArg,
|
|
58
|
+
sleep
|
|
59
|
+
} from "../chunk-DD5BE2W6.js";
|
|
60
|
+
import {
|
|
61
|
+
initLogger,
|
|
62
|
+
logger
|
|
63
|
+
} from "../chunk-DVU3CXWA.js";
|
|
50
64
|
|
|
51
|
-
// src/
|
|
65
|
+
// src/boot.ts
|
|
52
66
|
import { mkdirSync } from "fs";
|
|
53
67
|
import { env, exit, argv } from "process";
|
|
54
|
-
|
|
55
|
-
// src/agent/dev-server.ts
|
|
56
68
|
import { resolve } from "path";
|
|
57
|
-
|
|
69
|
+
function parsePort(args) {
|
|
70
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
71
|
+
const arg = args[i];
|
|
72
|
+
if (arg === "--port") {
|
|
73
|
+
const value = args[i + 1];
|
|
74
|
+
if (!value || !/^\d+$/.test(value)) {
|
|
75
|
+
fail(`Invalid value for --port: ${value ?? "<empty>"}`);
|
|
76
|
+
}
|
|
77
|
+
return parseIntArg(value, 4040);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
58
82
|
async function startDevFrontend(apiPort, devPort) {
|
|
83
|
+
const VITE_CONFIG_PATH = resolve(PACKAGE_ROOT, "app/vite.config.js");
|
|
59
84
|
let createViteServer;
|
|
60
85
|
try {
|
|
61
86
|
const vite = await import("vite");
|
|
@@ -64,15 +89,51 @@ async function startDevFrontend(apiPort, devPort) {
|
|
|
64
89
|
logger.warn("Vite not installed (devDependency). Run 'pnpm install' in the project to enable --dev mode.");
|
|
65
90
|
return;
|
|
66
91
|
}
|
|
92
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
93
|
+
try {
|
|
94
|
+
const res = await fetch(`http://localhost:${apiPort}/api/health`);
|
|
95
|
+
if (res.ok) break;
|
|
96
|
+
} catch {
|
|
97
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
67
100
|
try {
|
|
68
101
|
const server = await createViteServer({
|
|
69
102
|
configFile: VITE_CONFIG_PATH,
|
|
103
|
+
customLogger: {
|
|
104
|
+
info: (msg) => logger.info(`[Vite] ${msg}`),
|
|
105
|
+
warn: (msg) => logger.warn(`[Vite] ${msg}`),
|
|
106
|
+
warnOnce: (msg) => logger.warn(`[Vite] ${msg}`),
|
|
107
|
+
error: (msg) => {
|
|
108
|
+
if (msg.includes("ws proxy error") || msg.includes("ws proxy socket error")) {
|
|
109
|
+
logger.debug(`[Vite] ${msg.split("\n")[0]} (transient, suppressed)`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
logger.error(`[Vite] ${msg}`);
|
|
113
|
+
},
|
|
114
|
+
hasErrorLogged: () => false,
|
|
115
|
+
clearScreen: () => {
|
|
116
|
+
},
|
|
117
|
+
hasWarned: false
|
|
118
|
+
},
|
|
70
119
|
server: {
|
|
71
120
|
port: devPort,
|
|
72
121
|
host: true,
|
|
73
122
|
proxy: {
|
|
74
123
|
"/api": `http://localhost:${apiPort}`,
|
|
75
|
-
"/ws": {
|
|
124
|
+
"/ws": {
|
|
125
|
+
target: `ws://localhost:${apiPort}`,
|
|
126
|
+
ws: true,
|
|
127
|
+
configure: (proxy) => {
|
|
128
|
+
const silence = (err) => {
|
|
129
|
+
logger.debug(`[Vite] WS proxy transient: ${err.code || err.message}`);
|
|
130
|
+
};
|
|
131
|
+
proxy.on("error", silence);
|
|
132
|
+
proxy.on("proxyReqWs", (_proxyReq, _req, socket) => {
|
|
133
|
+
socket.on("error", silence);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
},
|
|
76
137
|
"/docs": `http://localhost:${apiPort}`,
|
|
77
138
|
"/health": `http://localhost:${apiPort}`,
|
|
78
139
|
"/manifest.webmanifest": `http://localhost:${apiPort}`,
|
|
@@ -89,8 +150,6 @@ async function startDevFrontend(apiPort, devPort) {
|
|
|
89
150
|
logger.warn(`Failed to start Vite dev server: ${String(error)}`);
|
|
90
151
|
}
|
|
91
152
|
}
|
|
92
|
-
|
|
93
|
-
// src/agent/run-local.ts
|
|
94
153
|
function usage() {
|
|
95
154
|
console.log(
|
|
96
155
|
`Usage: ${argv[1]} [options]
|
|
@@ -171,6 +230,8 @@ async function main() {
|
|
|
171
230
|
booting: true
|
|
172
231
|
};
|
|
173
232
|
let apiState = earlyState;
|
|
233
|
+
createContainer(apiState);
|
|
234
|
+
debugBoot("main:container-early-init");
|
|
174
235
|
if (dashboardPort) {
|
|
175
236
|
await startApiServer(apiState, dashboardPort);
|
|
176
237
|
debugBoot("main:api-server-early-start");
|
|
@@ -197,6 +258,23 @@ async function main() {
|
|
|
197
258
|
state.config.dashboardPort = dashboardPort ? String(dashboardPort) : void 0;
|
|
198
259
|
state.updatedAt = now();
|
|
199
260
|
state.booting = false;
|
|
261
|
+
try {
|
|
262
|
+
const { getIssueStateMachinePlugin, ISSUE_STATE_MACHINE_ID } = await import("../issue-state-machine-NSDN4MV4.js");
|
|
263
|
+
const fsmPlugin = getIssueStateMachinePlugin();
|
|
264
|
+
if (fsmPlugin?.getState) {
|
|
265
|
+
for (const issue of state.issues) {
|
|
266
|
+
try {
|
|
267
|
+
const fsmState = await fsmPlugin.getState(ISSUE_STATE_MACHINE_ID, issue.id);
|
|
268
|
+
if (fsmState && fsmState !== issue.state) {
|
|
269
|
+
logger.warn({ issueId: issue.id, memoryState: issue.state, fsmState }, "[Boot] Reconciling desync \u2014 FSM is source of truth");
|
|
270
|
+
issue.state = fsmState;
|
|
271
|
+
}
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
}
|
|
200
278
|
if (!state.config.defaultBranch) {
|
|
201
279
|
try {
|
|
202
280
|
const detectedBranch = detectDefaultBranch(TARGET_ROOT);
|
|
@@ -241,13 +319,22 @@ async function main() {
|
|
|
241
319
|
const { alive, pid } = isAgentStillRunning(issue);
|
|
242
320
|
if (alive && pid) {
|
|
243
321
|
logger.info(`Agent for ${issue.identifier} still alive (PID ${pid.pid}), keeping state as Running.`);
|
|
244
|
-
issue.state
|
|
322
|
+
if (issue.state !== "Running") {
|
|
323
|
+
try {
|
|
324
|
+
await executeTransition(issue, "RUN", { issue, note: `Orphaned agent detected (PID ${pid.pid}), still alive \u2014 tracking resumed.` });
|
|
325
|
+
} catch {
|
|
326
|
+
issue.state = "Running";
|
|
327
|
+
}
|
|
328
|
+
}
|
|
245
329
|
addEvent(state, issue.id, "info", `Orphaned agent detected (PID ${pid.pid}), still alive \u2014 tracking resumed.`);
|
|
246
330
|
} else {
|
|
247
331
|
if (issue.workspacePath) cleanStalePidFile(issue.workspacePath);
|
|
248
332
|
if (issue.state === "Running") {
|
|
249
|
-
|
|
250
|
-
|
|
333
|
+
try {
|
|
334
|
+
await executeTransition(issue, "REQUEUE", { issue, note: `Agent process not found on boot \u2014 marked Queued.` });
|
|
335
|
+
} catch {
|
|
336
|
+
issue.state = "Queued";
|
|
337
|
+
}
|
|
251
338
|
addEvent(state, issue.id, "info", `Agent for ${issue.identifier} not found, marked Queued.`);
|
|
252
339
|
}
|
|
253
340
|
}
|
|
@@ -255,16 +342,18 @@ async function main() {
|
|
|
255
342
|
}
|
|
256
343
|
}
|
|
257
344
|
state.metrics = computeMetrics(state.issues);
|
|
345
|
+
if (dashboardPort) {
|
|
346
|
+
Object.assign(apiState, state);
|
|
347
|
+
debugBoot("main:api-state-swapped");
|
|
348
|
+
}
|
|
349
|
+
createContainer(apiState);
|
|
350
|
+
logger.info("[Boot] DI container initialized with full state");
|
|
258
351
|
await persistStateFull(state);
|
|
259
352
|
try {
|
|
260
353
|
await initQueueWorkers(state);
|
|
261
354
|
} catch (error) {
|
|
262
355
|
logger.warn({ err: error }, "[Boot] Queue workers failed to initialize \u2014 continuing without queue-based dispatch");
|
|
263
356
|
}
|
|
264
|
-
if (dashboardPort) {
|
|
265
|
-
Object.assign(apiState, state);
|
|
266
|
-
debugBoot("main:api-state-swapped");
|
|
267
|
-
}
|
|
268
357
|
const running = /* @__PURE__ */ new Set();
|
|
269
358
|
installGracefulShutdown(state, running);
|
|
270
359
|
logger.info("[Boot] Runtime ready");
|
|
@@ -278,8 +367,49 @@ async function main() {
|
|
|
278
367
|
try {
|
|
279
368
|
addEvent(state, void 0, "info", `Runtime started in local-only mode (filesystem tracker).`);
|
|
280
369
|
const runForever = !runOnce && (Boolean(dashboardPort) || interfaceMode === "mcp");
|
|
281
|
-
logger.info({ runForever, runOnce, dashboardPort, interfaceMode }, "[Boot] Entering
|
|
282
|
-
|
|
370
|
+
logger.info({ runForever, runOnce, dashboardPort, interfaceMode }, "[Boot] Entering queue supervisor loop");
|
|
371
|
+
for (const issue of state.issues) {
|
|
372
|
+
try {
|
|
373
|
+
if (issue.state === "Planning" && issue.planningStatus !== "planning") {
|
|
374
|
+
await enqueueForPlanning(issue);
|
|
375
|
+
} else if (issue.state === "Queued" || issue.state === "Running") {
|
|
376
|
+
await enqueueForExecution(issue);
|
|
377
|
+
} else if (issue.state === "Reviewing") {
|
|
378
|
+
await enqueueForReview(issue);
|
|
379
|
+
}
|
|
380
|
+
} catch (err) {
|
|
381
|
+
logger.error({ err, issueId: issue.id, state: issue.state }, "[Boot] Failed to enqueue issue for recovery");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const PERSIST_DEBOUNCE_MS = 5e3;
|
|
385
|
+
let lastPersistAt = 0;
|
|
386
|
+
if (runForever) {
|
|
387
|
+
while (!isShuttingDown()) {
|
|
388
|
+
const statesBefore = new Map(state.issues.map((i) => [i.id, i.state]));
|
|
389
|
+
await ensureNotStale(state, state.config.staleInProgressTimeoutMs);
|
|
390
|
+
for (const issue of state.issues) {
|
|
391
|
+
const prev = statesBefore.get(issue.id);
|
|
392
|
+
if (prev !== issue.state) {
|
|
393
|
+
if (issue.state === "Queued") enqueueForExecution(issue).catch(() => {
|
|
394
|
+
});
|
|
395
|
+
else if (issue.state === "Reviewing") enqueueForReview(issue).catch(() => {
|
|
396
|
+
});
|
|
397
|
+
else if (issue.state === "Planning") enqueueForPlanning(issue).catch(() => {
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
state.updatedAt = now();
|
|
402
|
+
if (hasDirtyState() || Date.now() - lastPersistAt > PERSIST_DEBOUNCE_MS) {
|
|
403
|
+
await persistState(state);
|
|
404
|
+
lastPersistAt = Date.now();
|
|
405
|
+
}
|
|
406
|
+
await sleep(1e3);
|
|
407
|
+
}
|
|
408
|
+
} else {
|
|
409
|
+
while (!hasTerminalQueue(state)) {
|
|
410
|
+
await sleep(state.config.pollIntervalMs);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
283
413
|
} catch (error) {
|
|
284
414
|
console.error("FATAL STACK TRACE:", error);
|
|
285
415
|
addEvent(state, void 0, "error", `Fatal runtime error: ${String(error)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/agent/run-local.ts","../../src/agent/dev-server.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { mkdirSync } from \"node:fs\";\nimport { env, exit, argv } from \"node:process\";\nimport { CLI_ARGS, STATE_ROOT, TARGET_ROOT } from \"./constants.ts\";\nimport { debugBoot, fail, now } from \"./helpers.ts\";\nimport { initLogger, logger } from \"./logger.ts\";\nimport { initStateStore, loadPersistedState, persistState, persistStateFull, closeStateStore } from \"./store.ts\";\nimport { initQueueWorkers, stopQueueWorkers } from \"./queue-workers.ts\";\nimport {\n applyPersistedSettings,\n loadRuntimeSettings,\n persistDetectedProvidersSetting,\n syncRuntimeConfigSettings,\n} from \"./settings.ts\";\nimport { buildQueueTitle, detectProjectName, resolveProjectMetadata } from \"./project-meta.ts\";\nimport {\n detectAvailableProviders,\n resolveDefaultProvider,\n getProviderDefaultCommand,\n} from \"./providers.ts\";\nimport { parsePort, setSkipSource } from \"./workflow.ts\";\nimport { deriveConfig, applyWorkflowConfig, buildRuntimeState, computeMetrics, addEvent, validateConfig } from \"./issues.ts\";\nimport { startApiServer } from \"./api-server.ts\";\nimport { scheduler, installGracefulShutdown } from \"./scheduler.ts\";\nimport { cleanWorkspace, isAgentStillRunning, cleanStalePidFile } from \"./agent.ts\";\nimport { startDevFrontend } from \"./dev-server.ts\";\nimport { recoverPlanningSession } from \"./issue-planner.ts\";\nimport { hydrate as hydrateTokenLedger } from \"./token-ledger.ts\";\nimport { detectDefaultBranch } from \"./workspace-setup.ts\";\nimport type { RuntimeState } from \"./types.ts\";\n\nfunction usage() {\n console.log(\n `Usage: ${argv[1]} [options]\\n` +\n \"Options:\\n\" +\n \" --workspace <path> Target workspace root (default: current directory)\\n\" +\n \" --persistence <path> Persistence root (default: current directory)\\n\" +\n \" --port <n> Start local dashboard\\n\" +\n \" --concurrency <n> Maximum number of local workers\\n\" +\n \" --attempts <n> Maximum attempts per issue\\n\" +\n \" --poll <ms> Scheduler interval in ms\\n\" +\n \" --timeout <ms> Agent command timeout in ms (default: 1800000)\\n\" +\n \" --dev Start Vite dev server alongside API (HMR on port+1)\\n\" +\n \" --once Process once and exit\\n\" +\n \" --skip-source Skip source snapshot copy\\n\" +\n \" --skip-scan Skip project analysis\\n\" +\n \" --skip-recovery Skip orphaned agent recovery\\n\" +\n \" --fast-boot Equivalent to --skip-source --skip-scan --skip-recovery\\n\",\n );\n}\n\nasync function main() {\n debugBoot(\"main:start\");\n\n const args = CLI_ARGS;\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n usage();\n return;\n }\n\n mkdirSync(STATE_ROOT, { recursive: true });\n initLogger(STATE_ROOT);\n logger.info(\"[Boot] Fifony runtime starting\");\n logger.info({ stateRoot: STATE_ROOT, cwd: process.cwd() }, \"[Boot] State root initialized\");\n\n // Detect available providers\n const detectedProviders = detectAvailableProviders();\n for (const p of detectedProviders) {\n logger.info(`Provider ${p.name}: ${p.available ? `available at ${p.path}` : \"not found\"}`);\n }\n\n const interfaceMode = (env.FIFONY_INTERFACE ?? \"cli\").trim().toLowerCase();\n const runOnce = args.includes(\"--once\");\n const devMode = args.includes(\"--dev\") || env.NODE_ENV === \"development\";\n const fastBoot = args.includes(\"--fast-boot\");\n const skipSource = fastBoot || args.includes(\"--skip-source\");\n if (skipSource) setSkipSource(true);\n\n debugBoot(\"main:state-root-ready\");\n\n const port = parsePort(args);\n let config = applyWorkflowConfig(deriveConfig(args), port);\n\n // Auto-resolve provider command if not configured\n if (!config.agentCommand.trim()) {\n const defaultProvider = resolveDefaultProvider(detectedProviders);\n if (defaultProvider) {\n const defaultCommand = getProviderDefaultCommand(defaultProvider);\n if (defaultCommand) {\n config = { ...config, agentProvider: defaultProvider, agentCommand: defaultCommand };\n logger.info(`Auto-detected provider: ${defaultProvider} → ${defaultCommand}`);\n }\n }\n }\n\n const dashboardPort = port ?? (config.dashboardPort ? Number.parseInt(config.dashboardPort, 10) : undefined);\n const skipRecovery = args.includes(\"--skip-recovery\") || args.includes(\"--fast-boot\");\n const detectedProjectName = detectProjectName(TARGET_ROOT);\n\n // ── Phase B: Parallel initialization ────────────────────────────────────────\n debugBoot(\"main:phase-b-start\");\n logger.debug(\"[Boot] Initializing state store (s3db)\");\n await initStateStore();\n logger.info(\"[Boot] State store initialized\");\n debugBoot(\"main:store-initialized\");\n\n // ── Early API start: dashboard available while boot continues ─────────────\n // Build a minimal placeholder state for the early API server\n const earlyState: RuntimeState = {\n projectName: detectedProjectName,\n detectedProjectName,\n projectNameSource: detectedProjectName ? \"detected\" : \"missing\",\n queueTitle: buildQueueTitle(detectedProjectName),\n startedAt: now(),\n updatedAt: now(),\n trackerKind: \"filesystem\",\n sourceRepoUrl: TARGET_ROOT,\n sourceRef: \"workspace\",\n config,\n issues: [],\n events: [],\n metrics: { total: 0, planning: 0, queued: 0, inProgress: 0, blocked: 0, done: 0, cancelled: 0, activeWorkers: 0 },\n notes: [],\n booting: true,\n };\n\n let apiState = earlyState;\n if (dashboardPort) {\n await startApiServer(apiState, dashboardPort);\n debugBoot(\"main:api-server-early-start\");\n\n if (devMode) {\n const devPort = dashboardPort + 1;\n await startDevFrontend(dashboardPort, devPort);\n }\n }\n\n // ── Phase C: Parallel state loading ─────────────────────────────────────────\n debugBoot(\"main:phase-c-start\");\n logger.debug(\"[Boot] Loading persisted state, settings, and recovering sessions\");\n const [previous, persistedSettings] = await Promise.all([\n loadPersistedState(),\n loadRuntimeSettings(),\n persistDetectedProvidersSetting(detectedProviders),\n recoverPlanningSession(),\n ]);\n logger.info({ hadPreviousState: previous !== null, issueCount: previous?.issues?.length ?? 0, settingsCount: persistedSettings.length }, \"[Boot] State loaded from persistence\");\n debugBoot(\"main:state-loaded\");\n\n config = applyPersistedSettings(config, persistedSettings);\n await syncRuntimeConfigSettings(config, persistedSettings);\n const projectMetadata = resolveProjectMetadata(persistedSettings, TARGET_ROOT);\n const state = buildRuntimeState(previous, config, projectMetadata);\n debugBoot(\"main:state-merged\");\n\n state.config.dashboardPort = dashboardPort ? String(dashboardPort) : undefined;\n state.updatedAt = now();\n state.booting = false;\n\n // Detect and lock the default branch once at startup\n if (!state.config.defaultBranch) {\n try {\n const detectedBranch = detectDefaultBranch(TARGET_ROOT);\n state.config.defaultBranch = detectedBranch;\n logger.info({ defaultBranch: detectedBranch }, \"[Agent] Default branch detected\");\n } catch {\n // Not a git repo or detection failed — leave undefined\n }\n }\n\n if (state.config.agentCommand) {\n state.notes.push(`Using agent command: ${state.config.agentCommand}`);\n }\n state.notes.push(`Agent session max turns: ${state.config.maxTurns}`);\n state.notes.push(`Agent provider: ${state.config.agentProvider}`);\n state.notes.push(`Interface mode: ${interfaceMode}`);\n\n if (!state.config.agentCommand.trim()) {\n const available = detectedProviders.filter((p) => p.available).map((p) => p.name);\n fail(\n available.length === 0\n ? \"No agent command configured and no providers (claude, codex) found in PATH.\\nInstall claude or codex, or set FIFONY_AGENT_COMMAND.\"\n : \"No agent command configured. Set FIFONY_AGENT_COMMAND.\",\n );\n }\n\n // Validate config at startup (spec §6.3)\n const configErrors = validateConfig(config);\n if (configErrors.length > 0) {\n for (const err of configErrors) logger.warn(`Config validation: ${err}`);\n }\n\n // Clean terminal workspaces in background (non-blocking boot)\n const terminalIssues = state.issues.filter((i) => i.state === \"Done\" || i.state === \"Cancelled\");\n if (terminalIssues.length > 0) {\n logger.info(`Scheduling cleanup of ${terminalIssues.length} terminal workspace(s) in background...`);\n setImmediate(async () => {\n for (const issue of terminalIssues) {\n try { await cleanWorkspace(issue.id, issue, state); } catch {}\n }\n logger.info(\"Background workspace cleanup complete.\");\n });\n }\n\n // Recover orphaned agent processes from previous session\n if (!skipRecovery) {\n logger.debug({ issueCount: state.issues.filter((i) => i.state === \"Running\" || i.state === \"Queued\").length }, \"[Boot] Checking for orphaned agent processes\");\n for (const issue of state.issues) {\n if (issue.state === \"Running\" || issue.state === \"Queued\") {\n const { alive, pid } = isAgentStillRunning(issue);\n if (alive && pid) {\n logger.info(`Agent for ${issue.identifier} still alive (PID ${pid.pid}), keeping state as Running.`);\n issue.state = \"Running\";\n addEvent(state, issue.id, \"info\", `Orphaned agent detected (PID ${pid.pid}), still alive — tracking resumed.`);\n } else {\n // Agent died — clean PID file, mark as Queued for resumption\n if (issue.workspacePath) cleanStalePidFile(issue.workspacePath);\n if (issue.state === \"Running\") {\n issue.state = \"Queued\";\n issue.history.push(`[${now()}] Agent process not found on boot — marked Queued.`);\n addEvent(state, issue.id, \"info\", `Agent for ${issue.identifier} not found, marked Queued.`);\n }\n }\n }\n }\n }\n\n state.metrics = computeMetrics(state.issues);\n await persistStateFull(state);\n\n // Initialize queue workers after state is fully ready\n try {\n await initQueueWorkers(state);\n } catch (error) {\n logger.warn({ err: error }, \"[Boot] Queue workers failed to initialize — continuing without queue-based dispatch\");\n }\n\n // Update the API server to use the real state (swap reference)\n if (dashboardPort) {\n // The API server closure captures apiState — mutate it to point to real state\n Object.assign(apiState, state);\n debugBoot(\"main:api-state-swapped\");\n }\n\n const running = new Set<string>();\n installGracefulShutdown(state, running);\n\n logger.info(\"[Boot] Runtime ready\");\n hydrateTokenLedger(state.issues);\n logger.info(`Loaded issues: ${state.issues.length}`);\n logger.info(`Worker concurrency: ${state.config.workerConcurrency}`);\n logger.info(`Max attempts: ${state.config.maxAttemptsDefault}`);\n logger.info(`Max turns: ${state.config.maxTurns}`);\n logger.info(`Agent provider: ${state.config.agentProvider}`);\n logger.info(`Interface mode: ${interfaceMode}`);\n\n try {\n addEvent(state, undefined, \"info\", `Runtime started in local-only mode (filesystem tracker).`);\n const runForever = !runOnce && (Boolean(dashboardPort) || interfaceMode === \"mcp\");\n logger.info({ runForever, runOnce, dashboardPort, interfaceMode }, \"[Boot] Entering scheduler loop\");\n await scheduler(state, running, runForever);\n } catch (error) {\n console.error(\"FATAL STACK TRACE:\", error);\n addEvent(state, undefined, \"error\", `Fatal runtime error: ${String(error)}`);\n await persistState(state);\n throw error;\n } finally {\n state.updatedAt = now();\n state.metrics = computeMetrics(state.issues);\n await persistStateFull(state);\n try { await stopQueueWorkers(); } catch {}\n await closeStateStore();\n }\n}\n\nmain().catch((error) => {\n logger.error({ err: error }, `Fatal runtime error: ${String(error)}`);\n exit(1);\n});\n","/**\n * Development server that runs Vite dev server alongside the API.\n * Uses dynamic import so vite (devDependency) is only loaded when --dev is used.\n */\nimport { resolve } from \"node:path\";\nimport { PACKAGE_ROOT } from \"./constants.ts\";\nimport { logger } from \"./logger.ts\";\n\nconst VITE_CONFIG_PATH = resolve(PACKAGE_ROOT, \"app/vite.config.js\");\n\nexport async function startDevFrontend(apiPort: number, devPort: number): Promise<void> {\n let createViteServer: typeof import(\"vite\").createServer;\n try {\n const vite = await import(\"vite\");\n createViteServer = vite.createServer;\n } catch {\n logger.warn(\"Vite not installed (devDependency). Run 'pnpm install' in the project to enable --dev mode.\");\n return;\n }\n\n try {\n const server = await createViteServer({\n configFile: VITE_CONFIG_PATH,\n server: {\n port: devPort,\n host: true,\n proxy: {\n \"/api\": `http://localhost:${apiPort}`,\n \"/ws\": { target: `ws://localhost:${apiPort}`, ws: true },\n \"/docs\": `http://localhost:${apiPort}`,\n \"/health\": `http://localhost:${apiPort}`,\n \"/manifest.webmanifest\": `http://localhost:${apiPort}`,\n \"/service-worker.js\": `http://localhost:${apiPort}`,\n \"/icon.svg\": `http://localhost:${apiPort}`,\n \"/icon-maskable.svg\": `http://localhost:${apiPort}`,\n \"/offline.html\": `http://localhost:${apiPort}`,\n },\n },\n });\n\n await server.listen();\n logger.info(`Dev frontend available at http://localhost:${devPort}`);\n } catch (error) {\n logger.warn(`Failed to start Vite dev server: ${String(error)}`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/boot.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { mkdirSync } from \"node:fs\";\nimport { env, exit, argv } from \"node:process\";\nimport { CLI_ARGS, PACKAGE_ROOT, STATE_ROOT, TARGET_ROOT } from \"./concerns/constants.ts\";\nimport { debugBoot, fail, now, sleep, parseIntArg } from \"./concerns/helpers.ts\";\nimport { initLogger, logger } from \"./concerns/logger.ts\";\nimport { initStateStore, loadPersistedState, persistState, persistStateFull, closeStateStore } from \"./persistence/store.ts\";\nimport { initQueueWorkers, stopQueueWorkers, enqueueForPlanning, enqueueForExecution, enqueueForReview } from \"./persistence/plugins/queue-workers.ts\";\nimport { createContainer } from \"./persistence/container.ts\";\nimport {\n applyPersistedSettings,\n loadRuntimeSettings,\n persistDetectedProvidersSetting,\n syncRuntimeConfigSettings,\n} from \"./persistence/settings.ts\";\nimport { buildQueueTitle, detectProjectName, resolveProjectMetadata } from \"./domains/project.ts\";\nimport {\n detectAvailableProviders,\n resolveDefaultProvider,\n getProviderDefaultCommand,\n} from \"./agents/providers.ts\";\nimport { setSkipSource, detectDefaultBranch } from \"./domains/workspace.ts\";\nimport { deriveConfig, applyWorkflowConfig, buildRuntimeState, computeMetrics, addEvent, validateConfig } from \"./domains/issues.ts\";\nimport { hasDirtyState } from \"./persistence/dirty-tracker.ts\";\nimport { executeTransition } from \"./persistence/plugins/issue-state-machine.ts\";\nimport { startApiServer } from \"./persistence/plugins/api-server.ts\";\nimport { installGracefulShutdown, isShuttingDown, ensureNotStale, hasTerminalQueue } from \"./persistence/plugins/scheduler.ts\";\nimport { cleanWorkspace, isAgentStillRunning, cleanStalePidFile } from \"./agents/agent.ts\";\nimport { recoverPlanningSession } from \"./agents/planning/issue-planner.ts\";\nimport { hydrate as hydrateTokenLedger } from \"./domains/tokens.ts\";\nimport { resolve } from \"node:path\";\nimport type { RuntimeState } from \"./types.ts\";\n\nfunction parsePort(args: string[]): number | undefined {\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n if (arg === \"--port\") {\n const value = args[i + 1];\n if (!value || !/^\\d+$/.test(value)) {\n fail(`Invalid value for --port: ${value ?? \"<empty>\"}`);\n }\n return parseIntArg(value, 4040);\n }\n }\n return undefined;\n}\n\nasync function startDevFrontend(apiPort: number, devPort: number): Promise<void> {\n const VITE_CONFIG_PATH = resolve(PACKAGE_ROOT, \"app/vite.config.js\");\n let createViteServer: typeof import(\"vite\").createServer;\n try {\n const vite = await import(\"vite\");\n createViteServer = vite.createServer;\n } catch {\n logger.warn(\"Vite not installed (devDependency). Run 'pnpm install' in the project to enable --dev mode.\");\n return;\n }\n // Wait for the API server to be ready before starting the proxy\n for (let attempt = 0; attempt < 20; attempt++) {\n try {\n const res = await fetch(`http://localhost:${apiPort}/api/health`);\n if (res.ok) break;\n } catch {\n await new Promise((r) => setTimeout(r, 250));\n }\n }\n\n try {\n const server = await createViteServer({\n configFile: VITE_CONFIG_PATH,\n customLogger: {\n info: (msg: string) => logger.info(`[Vite] ${msg}`),\n warn: (msg: string) => logger.warn(`[Vite] ${msg}`),\n warnOnce: (msg: string) => logger.warn(`[Vite] ${msg}`),\n error: (msg: string) => {\n if (msg.includes(\"ws proxy error\") || msg.includes(\"ws proxy socket error\")) {\n logger.debug(`[Vite] ${msg.split(\"\\n\")[0]} (transient, suppressed)`);\n return;\n }\n logger.error(`[Vite] ${msg}`);\n },\n hasErrorLogged: () => false,\n clearScreen: () => {},\n hasWarned: false,\n },\n server: {\n port: devPort,\n host: true,\n proxy: {\n \"/api\": `http://localhost:${apiPort}`,\n \"/ws\": {\n target: `ws://localhost:${apiPort}`,\n ws: true,\n configure: (proxy) => {\n const silence = (err: any) => {\n logger.debug(`[Vite] WS proxy transient: ${err.code || err.message}`);\n };\n proxy.on(\"error\", silence);\n proxy.on(\"proxyReqWs\", (_proxyReq: any, _req: any, socket: any) => {\n socket.on(\"error\", silence);\n });\n },\n },\n \"/docs\": `http://localhost:${apiPort}`,\n \"/health\": `http://localhost:${apiPort}`,\n \"/manifest.webmanifest\": `http://localhost:${apiPort}`,\n \"/service-worker.js\": `http://localhost:${apiPort}`,\n \"/icon.svg\": `http://localhost:${apiPort}`,\n \"/icon-maskable.svg\": `http://localhost:${apiPort}`,\n \"/offline.html\": `http://localhost:${apiPort}`,\n },\n },\n });\n await server.listen();\n logger.info(`Dev frontend available at http://localhost:${devPort}`);\n } catch (error) {\n logger.warn(`Failed to start Vite dev server: ${String(error)}`);\n }\n}\n\nfunction usage() {\n console.log(\n `Usage: ${argv[1]} [options]\\n` +\n \"Options:\\n\" +\n \" --workspace <path> Target workspace root (default: current directory)\\n\" +\n \" --persistence <path> Persistence root (default: current directory)\\n\" +\n \" --port <n> Start local dashboard\\n\" +\n \" --concurrency <n> Maximum number of local workers\\n\" +\n \" --attempts <n> Maximum attempts per issue\\n\" +\n \" --poll <ms> Scheduler interval in ms\\n\" +\n \" --timeout <ms> Agent command timeout in ms (default: 1800000)\\n\" +\n \" --dev Start Vite dev server alongside API (HMR on port+1)\\n\" +\n \" --once Process once and exit\\n\" +\n \" --skip-source Skip source snapshot copy\\n\" +\n \" --skip-scan Skip project analysis\\n\" +\n \" --skip-recovery Skip orphaned agent recovery\\n\" +\n \" --fast-boot Equivalent to --skip-source --skip-scan --skip-recovery\\n\",\n );\n}\n\nasync function main() {\n debugBoot(\"main:start\");\n\n const args = CLI_ARGS;\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n usage();\n return;\n }\n\n mkdirSync(STATE_ROOT, { recursive: true });\n initLogger(STATE_ROOT);\n logger.info(\"[Boot] Fifony runtime starting\");\n logger.info({ stateRoot: STATE_ROOT, cwd: process.cwd() }, \"[Boot] State root initialized\");\n\n // Detect available providers\n const detectedProviders = detectAvailableProviders();\n for (const p of detectedProviders) {\n logger.info(`Provider ${p.name}: ${p.available ? `available at ${p.path}` : \"not found\"}`);\n }\n\n const interfaceMode = (env.FIFONY_INTERFACE ?? \"cli\").trim().toLowerCase();\n const runOnce = args.includes(\"--once\");\n const devMode = args.includes(\"--dev\") || env.NODE_ENV === \"development\";\n const fastBoot = args.includes(\"--fast-boot\");\n const skipSource = fastBoot || args.includes(\"--skip-source\");\n if (skipSource) setSkipSource(true);\n\n debugBoot(\"main:state-root-ready\");\n\n const port = parsePort(args);\n let config = applyWorkflowConfig(deriveConfig(args), port);\n\n // Auto-resolve provider command if not configured\n if (!config.agentCommand.trim()) {\n const defaultProvider = resolveDefaultProvider(detectedProviders);\n if (defaultProvider) {\n const defaultCommand = getProviderDefaultCommand(defaultProvider);\n if (defaultCommand) {\n config = { ...config, agentProvider: defaultProvider, agentCommand: defaultCommand };\n logger.info(`Auto-detected provider: ${defaultProvider} → ${defaultCommand}`);\n }\n }\n }\n\n const dashboardPort = port ?? (config.dashboardPort ? Number.parseInt(config.dashboardPort, 10) : undefined);\n const skipRecovery = args.includes(\"--skip-recovery\") || args.includes(\"--fast-boot\");\n const detectedProjectName = detectProjectName(TARGET_ROOT);\n\n // ── Phase B: Parallel initialization ────────────────────────────────────────\n debugBoot(\"main:phase-b-start\");\n logger.debug(\"[Boot] Initializing state store (s3db)\");\n await initStateStore();\n logger.info(\"[Boot] State store initialized\");\n debugBoot(\"main:store-initialized\");\n\n // ── Early API start: dashboard available while boot continues ─────────────\n // Build a minimal placeholder state for the early API server\n const earlyState: RuntimeState = {\n projectName: detectedProjectName,\n detectedProjectName,\n projectNameSource: detectedProjectName ? \"detected\" : \"missing\",\n queueTitle: buildQueueTitle(detectedProjectName),\n startedAt: now(),\n updatedAt: now(),\n trackerKind: \"filesystem\",\n sourceRepoUrl: TARGET_ROOT,\n sourceRef: \"workspace\",\n config,\n issues: [],\n events: [],\n metrics: { total: 0, planning: 0, queued: 0, inProgress: 0, blocked: 0, done: 0, cancelled: 0, activeWorkers: 0 },\n notes: [],\n booting: true,\n };\n\n let apiState = earlyState;\n // Initialize container early so API routes can use commands immediately\n createContainer(apiState);\n debugBoot(\"main:container-early-init\");\n\n if (dashboardPort) {\n await startApiServer(apiState, dashboardPort);\n debugBoot(\"main:api-server-early-start\");\n\n if (devMode) {\n const devPort = dashboardPort + 1;\n await startDevFrontend(dashboardPort, devPort);\n }\n }\n\n // ── Phase C: Parallel state loading ─────────────────────────────────────────\n debugBoot(\"main:phase-c-start\");\n logger.debug(\"[Boot] Loading persisted state, settings, and recovering sessions\");\n const [previous, persistedSettings] = await Promise.all([\n loadPersistedState(),\n loadRuntimeSettings(),\n persistDetectedProvidersSetting(detectedProviders),\n recoverPlanningSession(),\n ]);\n logger.info({ hadPreviousState: previous !== null, issueCount: previous?.issues?.length ?? 0, settingsCount: persistedSettings.length }, \"[Boot] State loaded from persistence\");\n debugBoot(\"main:state-loaded\");\n\n config = applyPersistedSettings(config, persistedSettings);\n await syncRuntimeConfigSettings(config, persistedSettings);\n const projectMetadata = resolveProjectMetadata(persistedSettings, TARGET_ROOT);\n const state = buildRuntimeState(previous, config, projectMetadata);\n debugBoot(\"main:state-merged\");\n\n state.config.dashboardPort = dashboardPort ? String(dashboardPort) : undefined;\n state.updatedAt = now();\n state.booting = false;\n\n // Reconcile in-memory state with FSM persisted state (source of truth)\n try {\n const { getIssueStateMachinePlugin, ISSUE_STATE_MACHINE_ID } = await import(\"./persistence/plugins/issue-state-machine.ts\");\n const fsmPlugin = getIssueStateMachinePlugin();\n if (fsmPlugin?.getState) {\n for (const issue of state.issues) {\n try {\n const fsmState = await fsmPlugin.getState(ISSUE_STATE_MACHINE_ID, issue.id);\n if (fsmState && fsmState !== issue.state) {\n logger.warn({ issueId: issue.id, memoryState: issue.state, fsmState }, \"[Boot] Reconciling desync — FSM is source of truth\");\n issue.state = fsmState as typeof issue.state;\n }\n } catch { /* FSM entity may not exist yet */ }\n }\n }\n } catch { /* FSM plugin may not be ready */ }\n\n // Detect and lock the default branch once at startup\n if (!state.config.defaultBranch) {\n try {\n const detectedBranch = detectDefaultBranch(TARGET_ROOT);\n state.config.defaultBranch = detectedBranch;\n logger.info({ defaultBranch: detectedBranch }, \"[Agent] Default branch detected\");\n } catch {\n // Not a git repo or detection failed — leave undefined\n }\n }\n\n if (state.config.agentCommand) {\n state.notes.push(`Using agent command: ${state.config.agentCommand}`);\n }\n state.notes.push(`Agent session max turns: ${state.config.maxTurns}`);\n state.notes.push(`Agent provider: ${state.config.agentProvider}`);\n state.notes.push(`Interface mode: ${interfaceMode}`);\n\n if (!state.config.agentCommand.trim()) {\n const available = detectedProviders.filter((p) => p.available).map((p) => p.name);\n fail(\n available.length === 0\n ? \"No agent command configured and no providers (claude, codex) found in PATH.\\nInstall claude or codex, or set FIFONY_AGENT_COMMAND.\"\n : \"No agent command configured. Set FIFONY_AGENT_COMMAND.\",\n );\n }\n\n // Validate config at startup (spec §6.3)\n const configErrors = validateConfig(config);\n if (configErrors.length > 0) {\n for (const err of configErrors) logger.warn(`Config validation: ${err}`);\n }\n\n // Clean terminal workspaces in background (non-blocking boot)\n const terminalIssues = state.issues.filter((i) => i.state === \"Done\" || i.state === \"Cancelled\");\n if (terminalIssues.length > 0) {\n logger.info(`Scheduling cleanup of ${terminalIssues.length} terminal workspace(s) in background...`);\n setImmediate(async () => {\n for (const issue of terminalIssues) {\n try { await cleanWorkspace(issue.id, issue, state); } catch {}\n }\n logger.info(\"Background workspace cleanup complete.\");\n });\n }\n\n // Recover orphaned agent processes from previous session\n if (!skipRecovery) {\n logger.debug({ issueCount: state.issues.filter((i) => i.state === \"Running\" || i.state === \"Queued\").length }, \"[Boot] Checking for orphaned agent processes\");\n for (const issue of state.issues) {\n if (issue.state === \"Running\" || issue.state === \"Queued\") {\n const { alive, pid } = isAgentStillRunning(issue);\n if (alive && pid) {\n logger.info(`Agent for ${issue.identifier} still alive (PID ${pid.pid}), keeping state as Running.`);\n if (issue.state !== \"Running\") {\n try { await executeTransition(issue, \"RUN\", { issue, note: `Orphaned agent detected (PID ${pid.pid}), still alive — tracking resumed.` }); }\n catch { issue.state = \"Running\"; }\n }\n addEvent(state, issue.id, \"info\", `Orphaned agent detected (PID ${pid.pid}), still alive — tracking resumed.`);\n } else {\n // Agent died — clean PID file, mark as Queued for resumption\n if (issue.workspacePath) cleanStalePidFile(issue.workspacePath);\n if (issue.state === \"Running\") {\n try { await executeTransition(issue, \"REQUEUE\", { issue, note: `Agent process not found on boot — marked Queued.` }); }\n catch { issue.state = \"Queued\"; }\n addEvent(state, issue.id, \"info\", `Agent for ${issue.identifier} not found, marked Queued.`);\n }\n }\n }\n }\n }\n\n state.metrics = computeMetrics(state.issues);\n\n // Swap state into API server IMMEDIATELY so the dashboard shows real data\n if (dashboardPort) {\n Object.assign(apiState, state);\n debugBoot(\"main:api-state-swapped\");\n }\n createContainer(apiState);\n logger.info(\"[Boot] DI container initialized with full state\");\n\n await persistStateFull(state);\n\n // Initialize queue workers (can be slow — dashboard already has real data)\n try {\n await initQueueWorkers(state);\n } catch (error) {\n logger.warn({ err: error }, \"[Boot] Queue workers failed to initialize — continuing without queue-based dispatch\");\n }\n\n const running = new Set<string>();\n installGracefulShutdown(state, running);\n\n logger.info(\"[Boot] Runtime ready\");\n hydrateTokenLedger(state.issues);\n logger.info(`Loaded issues: ${state.issues.length}`);\n logger.info(`Worker concurrency: ${state.config.workerConcurrency}`);\n logger.info(`Max attempts: ${state.config.maxAttemptsDefault}`);\n logger.info(`Max turns: ${state.config.maxTurns}`);\n logger.info(`Agent provider: ${state.config.agentProvider}`);\n logger.info(`Interface mode: ${interfaceMode}`);\n\n try {\n addEvent(state, undefined, \"info\", `Runtime started in local-only mode (filesystem tracker).`);\n const runForever = !runOnce && (Boolean(dashboardPort) || interfaceMode === \"mcp\");\n logger.info({ runForever, runOnce, dashboardPort, interfaceMode }, \"[Boot] Entering queue supervisor loop\");\n\n // Boot recovery: enqueue all in-progress issues so queue workers pick them up\n for (const issue of state.issues) {\n try {\n if (issue.state === \"Planning\" && issue.planningStatus !== \"planning\") {\n await enqueueForPlanning(issue);\n } else if (issue.state === \"Queued\" || issue.state === \"Running\") {\n await enqueueForExecution(issue);\n } else if (issue.state === \"Reviewing\") {\n await enqueueForReview(issue);\n }\n } catch (err) {\n logger.error({ err, issueId: issue.id, state: issue.state }, \"[Boot] Failed to enqueue issue for recovery\");\n }\n }\n\n const PERSIST_DEBOUNCE_MS = 5_000;\n let lastPersistAt = 0;\n\n if (runForever) {\n while (!isShuttingDown()) {\n // Take a snapshot of states before stale recovery so we can detect transitions\n const statesBefore = new Map(state.issues.map((i) => [i.id, i.state]));\n await ensureNotStale(state, state.config.staleInProgressTimeoutMs);\n\n // Re-enqueue any issues that just changed state due to stale recovery or retry eligibility\n for (const issue of state.issues) {\n const prev = statesBefore.get(issue.id);\n if (prev !== issue.state) {\n if (issue.state === \"Queued\") enqueueForExecution(issue).catch(() => {});\n else if (issue.state === \"Reviewing\") enqueueForReview(issue).catch(() => {});\n else if (issue.state === \"Planning\") enqueueForPlanning(issue).catch(() => {});\n }\n }\n\n state.updatedAt = now();\n if (hasDirtyState() || Date.now() - lastPersistAt > PERSIST_DEBOUNCE_MS) {\n await persistState(state);\n lastPersistAt = Date.now();\n }\n await sleep(1_000);\n }\n } else {\n // Batch mode: wait until all issues reach terminal states\n while (!hasTerminalQueue(state)) {\n await sleep(state.config.pollIntervalMs);\n }\n }\n } catch (error) {\n console.error(\"FATAL STACK TRACE:\", error);\n addEvent(state, undefined, \"error\", `Fatal runtime error: ${String(error)}`);\n await persistState(state);\n throw error;\n } finally {\n state.updatedAt = now();\n state.metrics = computeMetrics(state.issues);\n await persistStateFull(state);\n try { await stopQueueWorkers(); } catch {}\n await closeStateStore();\n }\n}\n\nmain().catch((error) => {\n logger.error({ err: error }, `Fatal runtime error: ${String(error)}`);\n exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,SAAS,iBAAiB;AAC1B,SAAS,KAAK,MAAM,YAAY;AA4BhC,SAAS,eAAe;AAGxB,SAAS,UAAU,MAAoC;AACrD,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,UAAU;AACpB,YAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,UAAI,CAAC,SAAS,CAAC,QAAQ,KAAK,KAAK,GAAG;AAClC,aAAK,6BAA6B,SAAS,SAAS,EAAE;AAAA,MACxD;AACA,aAAO,YAAY,OAAO,IAAI;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,iBAAiB,SAAiB,SAAgC;AAC/E,QAAM,mBAAmB,QAAQ,cAAc,oBAAoB;AACnE,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,MAAM;AAChC,uBAAmB,KAAK;AAAA,EAC1B,QAAQ;AACN,WAAO,KAAK,6FAA6F;AACzG;AAAA,EACF;AAEA,WAAS,UAAU,GAAG,UAAU,IAAI,WAAW;AAC7C,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,oBAAoB,OAAO,aAAa;AAChE,UAAI,IAAI,GAAI;AAAA,IACd,QAAQ;AACN,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAAA,IAC7C;AAAA,EACF;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,iBAAiB;AAAA,MACpC,YAAY;AAAA,MACZ,cAAc;AAAA,QACZ,MAAM,CAAC,QAAgB,OAAO,KAAK,UAAU,GAAG,EAAE;AAAA,QAClD,MAAM,CAAC,QAAgB,OAAO,KAAK,UAAU,GAAG,EAAE;AAAA,QAClD,UAAU,CAAC,QAAgB,OAAO,KAAK,UAAU,GAAG,EAAE;AAAA,QACtD,OAAO,CAAC,QAAgB;AACtB,cAAI,IAAI,SAAS,gBAAgB,KAAK,IAAI,SAAS,uBAAuB,GAAG;AAC3E,mBAAO,MAAM,UAAU,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC,0BAA0B;AACnE;AAAA,UACF;AACA,iBAAO,MAAM,UAAU,GAAG,EAAE;AAAA,QAC9B;AAAA,QACA,gBAAgB,MAAM;AAAA,QACtB,aAAa,MAAM;AAAA,QAAC;AAAA,QACpB,WAAW;AAAA,MACb;AAAA,MACA,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,UACL,QAAQ,oBAAoB,OAAO;AAAA,UACnC,OAAO;AAAA,YACL,QAAQ,kBAAkB,OAAO;AAAA,YACjC,IAAI;AAAA,YACJ,WAAW,CAAC,UAAU;AACpB,oBAAM,UAAU,CAAC,QAAa;AAC5B,uBAAO,MAAM,8BAA8B,IAAI,QAAQ,IAAI,OAAO,EAAE;AAAA,cACtE;AACA,oBAAM,GAAG,SAAS,OAAO;AACzB,oBAAM,GAAG,cAAc,CAAC,WAAgB,MAAW,WAAgB;AACjE,uBAAO,GAAG,SAAS,OAAO;AAAA,cAC5B,CAAC;AAAA,YACH;AAAA,UACF;AAAA,UACA,SAAS,oBAAoB,OAAO;AAAA,UACpC,WAAW,oBAAoB,OAAO;AAAA,UACtC,yBAAyB,oBAAoB,OAAO;AAAA,UACpD,sBAAsB,oBAAoB,OAAO;AAAA,UACjD,aAAa,oBAAoB,OAAO;AAAA,UACxC,sBAAsB,oBAAoB,OAAO;AAAA,UACjD,iBAAiB,oBAAoB,OAAO;AAAA,QAC9C;AAAA,MACF;AAAA,IACF,CAAC;AACD,UAAM,OAAO,OAAO;AACpB,WAAO,KAAK,8CAA8C,OAAO,EAAE;AAAA,EACrE,SAAS,OAAO;AACd,WAAO,KAAK,oCAAoC,OAAO,KAAK,CAAC,EAAE;AAAA,EACjE;AACF;AAEA,SAAS,QAAQ;AACf,UAAQ;AAAA,IACN,UAAU,KAAK,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAenB;AACF;AAEA,eAAe,OAAO;AACpB,YAAU,YAAY;AAEtB,QAAM,OAAO;AACb,MAAI,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;AAClD,UAAM;AACN;AAAA,EACF;AAEA,YAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AACzC,aAAW,UAAU;AACrB,SAAO,KAAK,gCAAgC;AAC5C,SAAO,KAAK,EAAE,WAAW,YAAY,KAAK,QAAQ,IAAI,EAAE,GAAG,+BAA+B;AAG1F,QAAM,oBAAoB,yBAAyB;AACnD,aAAW,KAAK,mBAAmB;AACjC,WAAO,KAAK,YAAY,EAAE,IAAI,KAAK,EAAE,YAAY,gBAAgB,EAAE,IAAI,KAAK,WAAW,EAAE;AAAA,EAC3F;AAEA,QAAM,iBAAiB,IAAI,oBAAoB,OAAO,KAAK,EAAE,YAAY;AACzE,QAAM,UAAU,KAAK,SAAS,QAAQ;AACtC,QAAM,UAAU,KAAK,SAAS,OAAO,KAAK,IAAI,aAAa;AAC3D,QAAM,WAAW,KAAK,SAAS,aAAa;AAC5C,QAAM,aAAa,YAAY,KAAK,SAAS,eAAe;AAC5D,MAAI,WAAY,eAAc,IAAI;AAElC,YAAU,uBAAuB;AAEjC,QAAM,OAAO,UAAU,IAAI;AAC3B,MAAI,SAAS,oBAAoB,aAAa,IAAI,GAAG,IAAI;AAGzD,MAAI,CAAC,OAAO,aAAa,KAAK,GAAG;AAC/B,UAAM,kBAAkB,uBAAuB,iBAAiB;AAChE,QAAI,iBAAiB;AACnB,YAAM,iBAAiB,0BAA0B,eAAe;AAChE,UAAI,gBAAgB;AAClB,iBAAS,EAAE,GAAG,QAAQ,eAAe,iBAAiB,cAAc,eAAe;AACnF,eAAO,KAAK,2BAA2B,eAAe,WAAM,cAAc,EAAE;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,gBAAgB,SAAS,OAAO,gBAAgB,OAAO,SAAS,OAAO,eAAe,EAAE,IAAI;AAClG,QAAM,eAAe,KAAK,SAAS,iBAAiB,KAAK,KAAK,SAAS,aAAa;AACpF,QAAM,sBAAsB,kBAAkB,WAAW;AAGzD,YAAU,oBAAoB;AAC9B,SAAO,MAAM,wCAAwC;AACrD,QAAM,eAAe;AACrB,SAAO,KAAK,gCAAgC;AAC5C,YAAU,wBAAwB;AAIlC,QAAM,aAA2B;AAAA,IAC/B,aAAa;AAAA,IACb;AAAA,IACA,mBAAmB,sBAAsB,aAAa;AAAA,IACtD,YAAY,gBAAgB,mBAAmB;AAAA,IAC/C,WAAW,IAAI;AAAA,IACf,WAAW,IAAI;AAAA,IACf,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,QAAQ,CAAC;AAAA,IACT,SAAS,EAAE,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,eAAe,EAAE;AAAA,IAChH,OAAO,CAAC;AAAA,IACR,SAAS;AAAA,EACX;AAEA,MAAI,WAAW;AAEf,kBAAgB,QAAQ;AACxB,YAAU,2BAA2B;AAErC,MAAI,eAAe;AACjB,UAAM,eAAe,UAAU,aAAa;AAC5C,cAAU,6BAA6B;AAEvC,QAAI,SAAS;AACX,YAAM,UAAU,gBAAgB;AAChC,YAAM,iBAAiB,eAAe,OAAO;AAAA,IAC/C;AAAA,EACF;AAGA,YAAU,oBAAoB;AAC9B,SAAO,MAAM,mEAAmE;AAChF,QAAM,CAAC,UAAU,iBAAiB,IAAI,MAAM,QAAQ,IAAI;AAAA,IACtD,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,gCAAgC,iBAAiB;AAAA,IACjD,uBAAuB;AAAA,EACzB,CAAC;AACD,SAAO,KAAK,EAAE,kBAAkB,aAAa,MAAM,YAAY,UAAU,QAAQ,UAAU,GAAG,eAAe,kBAAkB,OAAO,GAAG,sCAAsC;AAC/K,YAAU,mBAAmB;AAE7B,WAAS,uBAAuB,QAAQ,iBAAiB;AACzD,QAAM,0BAA0B,QAAQ,iBAAiB;AACzD,QAAM,kBAAkB,uBAAuB,mBAAmB,WAAW;AAC7E,QAAM,QAAQ,kBAAkB,UAAU,QAAQ,eAAe;AACjE,YAAU,mBAAmB;AAE7B,QAAM,OAAO,gBAAgB,gBAAgB,OAAO,aAAa,IAAI;AACrE,QAAM,YAAY,IAAI;AACtB,QAAM,UAAU;AAGhB,MAAI;AACF,UAAM,EAAE,4BAA4B,uBAAuB,IAAI,MAAM,OAAO,oCAA8C;AAC1H,UAAM,YAAY,2BAA2B;AAC7C,QAAI,WAAW,UAAU;AACvB,iBAAW,SAAS,MAAM,QAAQ;AAChC,YAAI;AACF,gBAAM,WAAW,MAAM,UAAU,SAAS,wBAAwB,MAAM,EAAE;AAC1E,cAAI,YAAY,aAAa,MAAM,OAAO;AACxC,mBAAO,KAAK,EAAE,SAAS,MAAM,IAAI,aAAa,MAAM,OAAO,SAAS,GAAG,yDAAoD;AAC3H,kBAAM,QAAQ;AAAA,UAChB;AAAA,QACF,QAAQ;AAAA,QAAqC;AAAA,MAC/C;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAAoC;AAG5C,MAAI,CAAC,MAAM,OAAO,eAAe;AAC/B,QAAI;AACF,YAAM,iBAAiB,oBAAoB,WAAW;AACtD,YAAM,OAAO,gBAAgB;AAC7B,aAAO,KAAK,EAAE,eAAe,eAAe,GAAG,iCAAiC;AAAA,IAClF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,MAAM,OAAO,cAAc;AAC7B,UAAM,MAAM,KAAK,wBAAwB,MAAM,OAAO,YAAY,EAAE;AAAA,EACtE;AACA,QAAM,MAAM,KAAK,4BAA4B,MAAM,OAAO,QAAQ,EAAE;AACpE,QAAM,MAAM,KAAK,mBAAmB,MAAM,OAAO,aAAa,EAAE;AAChE,QAAM,MAAM,KAAK,mBAAmB,aAAa,EAAE;AAEnD,MAAI,CAAC,MAAM,OAAO,aAAa,KAAK,GAAG;AACrC,UAAM,YAAY,kBAAkB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAChF;AAAA,MACE,UAAU,WAAW,IACjB,uIACA;AAAA,IACN;AAAA,EACF;AAGA,QAAM,eAAe,eAAe,MAAM;AAC1C,MAAI,aAAa,SAAS,GAAG;AAC3B,eAAW,OAAO,aAAc,QAAO,KAAK,sBAAsB,GAAG,EAAE;AAAA,EACzE;AAGA,QAAM,iBAAiB,MAAM,OAAO,OAAO,CAAC,MAAM,EAAE,UAAU,UAAU,EAAE,UAAU,WAAW;AAC/F,MAAI,eAAe,SAAS,GAAG;AAC7B,WAAO,KAAK,yBAAyB,eAAe,MAAM,yCAAyC;AACnG,iBAAa,YAAY;AACvB,iBAAW,SAAS,gBAAgB;AAClC,YAAI;AAAE,gBAAM,eAAe,MAAM,IAAI,OAAO,KAAK;AAAA,QAAG,QAAQ;AAAA,QAAC;AAAA,MAC/D;AACA,aAAO,KAAK,wCAAwC;AAAA,IACtD,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,cAAc;AACjB,WAAO,MAAM,EAAE,YAAY,MAAM,OAAO,OAAO,CAAC,MAAM,EAAE,UAAU,aAAa,EAAE,UAAU,QAAQ,EAAE,OAAO,GAAG,8CAA8C;AAC7J,eAAW,SAAS,MAAM,QAAQ;AAChC,UAAI,MAAM,UAAU,aAAa,MAAM,UAAU,UAAU;AACzD,cAAM,EAAE,OAAO,IAAI,IAAI,oBAAoB,KAAK;AAChD,YAAI,SAAS,KAAK;AAChB,iBAAO,KAAK,aAAa,MAAM,UAAU,qBAAqB,IAAI,GAAG,8BAA8B;AACnG,cAAI,MAAM,UAAU,WAAW;AAC7B,gBAAI;AAAE,oBAAM,kBAAkB,OAAO,OAAO,EAAE,OAAO,MAAM,gCAAgC,IAAI,GAAG,0CAAqC,CAAC;AAAA,YAAG,QACrI;AAAE,oBAAM,QAAQ;AAAA,YAAW;AAAA,UACnC;AACA,mBAAS,OAAO,MAAM,IAAI,QAAQ,gCAAgC,IAAI,GAAG,yCAAoC;AAAA,QAC/G,OAAO;AAEL,cAAI,MAAM,cAAe,mBAAkB,MAAM,aAAa;AAC9D,cAAI,MAAM,UAAU,WAAW;AAC7B,gBAAI;AAAE,oBAAM,kBAAkB,OAAO,WAAW,EAAE,OAAO,MAAM,wDAAmD,CAAC;AAAA,YAAG,QAChH;AAAE,oBAAM,QAAQ;AAAA,YAAU;AAChC,qBAAS,OAAO,MAAM,IAAI,QAAQ,aAAa,MAAM,UAAU,4BAA4B;AAAA,UAC7F;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,eAAe,MAAM,MAAM;AAG3C,MAAI,eAAe;AACjB,WAAO,OAAO,UAAU,KAAK;AAC7B,cAAU,wBAAwB;AAAA,EACpC;AACA,kBAAgB,QAAQ;AACxB,SAAO,KAAK,iDAAiD;AAE7D,QAAM,iBAAiB,KAAK;AAG5B,MAAI;AACF,UAAM,iBAAiB,KAAK;AAAA,EAC9B,SAAS,OAAO;AACd,WAAO,KAAK,EAAE,KAAK,MAAM,GAAG,0FAAqF;AAAA,EACnH;AAEA,QAAM,UAAU,oBAAI,IAAY;AAChC,0BAAwB,OAAO,OAAO;AAEtC,SAAO,KAAK,sBAAsB;AAClC,UAAmB,MAAM,MAAM;AAC/B,SAAO,KAAK,kBAAkB,MAAM,OAAO,MAAM,EAAE;AACnD,SAAO,KAAK,uBAAuB,MAAM,OAAO,iBAAiB,EAAE;AACnE,SAAO,KAAK,iBAAiB,MAAM,OAAO,kBAAkB,EAAE;AAC9D,SAAO,KAAK,cAAc,MAAM,OAAO,QAAQ,EAAE;AACjD,SAAO,KAAK,mBAAmB,MAAM,OAAO,aAAa,EAAE;AAC3D,SAAO,KAAK,mBAAmB,aAAa,EAAE;AAE9C,MAAI;AACF,aAAS,OAAO,QAAW,QAAQ,0DAA0D;AAC7F,UAAM,aAAa,CAAC,YAAY,QAAQ,aAAa,KAAK,kBAAkB;AAC5E,WAAO,KAAK,EAAE,YAAY,SAAS,eAAe,cAAc,GAAG,uCAAuC;AAG1G,eAAW,SAAS,MAAM,QAAQ;AAChC,UAAI;AACF,YAAI,MAAM,UAAU,cAAc,MAAM,mBAAmB,YAAY;AACrE,gBAAM,mBAAmB,KAAK;AAAA,QAChC,WAAW,MAAM,UAAU,YAAY,MAAM,UAAU,WAAW;AAChE,gBAAM,oBAAoB,KAAK;AAAA,QACjC,WAAW,MAAM,UAAU,aAAa;AACtC,gBAAM,iBAAiB,KAAK;AAAA,QAC9B;AAAA,MACF,SAAS,KAAK;AACZ,eAAO,MAAM,EAAE,KAAK,SAAS,MAAM,IAAI,OAAO,MAAM,MAAM,GAAG,6CAA6C;AAAA,MAC5G;AAAA,IACF;AAEA,UAAM,sBAAsB;AAC5B,QAAI,gBAAgB;AAEpB,QAAI,YAAY;AACd,aAAO,CAAC,eAAe,GAAG;AAExB,cAAM,eAAe,IAAI,IAAI,MAAM,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AACrE,cAAM,eAAe,OAAO,MAAM,OAAO,wBAAwB;AAGjE,mBAAW,SAAS,MAAM,QAAQ;AAChC,gBAAM,OAAO,aAAa,IAAI,MAAM,EAAE;AACtC,cAAI,SAAS,MAAM,OAAO;AACxB,gBAAI,MAAM,UAAU,SAAU,qBAAoB,KAAK,EAAE,MAAM,MAAM;AAAA,YAAC,CAAC;AAAA,qBAC9D,MAAM,UAAU,YAAa,kBAAiB,KAAK,EAAE,MAAM,MAAM;AAAA,YAAC,CAAC;AAAA,qBACnE,MAAM,UAAU,WAAY,oBAAmB,KAAK,EAAE,MAAM,MAAM;AAAA,YAAC,CAAC;AAAA,UAC/E;AAAA,QACF;AAEA,cAAM,YAAY,IAAI;AACtB,YAAI,cAAc,KAAK,KAAK,IAAI,IAAI,gBAAgB,qBAAqB;AACvE,gBAAM,aAAa,KAAK;AACxB,0BAAgB,KAAK,IAAI;AAAA,QAC3B;AACA,cAAM,MAAM,GAAK;AAAA,MACnB;AAAA,IACF,OAAO;AAEL,aAAO,CAAC,iBAAiB,KAAK,GAAG;AAC/B,cAAM,MAAM,MAAM,OAAO,cAAc;AAAA,MACzC;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,sBAAsB,KAAK;AACzC,aAAS,OAAO,QAAW,SAAS,wBAAwB,OAAO,KAAK,CAAC,EAAE;AAC3E,UAAM,aAAa,KAAK;AACxB,UAAM;AAAA,EACR,UAAE;AACA,UAAM,YAAY,IAAI;AACtB,UAAM,UAAU,eAAe,MAAM,MAAM;AAC3C,UAAM,iBAAiB,KAAK;AAC5B,QAAI;AAAE,YAAM,iBAAiB;AAAA,IAAG,QAAQ;AAAA,IAAC;AACzC,UAAM,gBAAgB;AAAA,EACxB;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,SAAO,MAAM,EAAE,KAAK,MAAM,GAAG,wBAAwB,OAAO,KAAK,CAAC,EAAE;AACpE,OAAK,CAAC;AACR,CAAC;","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/concerns/constants.ts
|
|
2
2
|
import { basename, dirname, join, resolve } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import { env, argv, cwd as getCwd } from "process";
|
|
@@ -36,9 +36,7 @@ var SOURCE_ROOT = `${STATE_ROOT}/source`;
|
|
|
36
36
|
var WORKSPACE_ROOT = `${STATE_ROOT}/workspaces`;
|
|
37
37
|
var SOURCE_MARKER = `${SOURCE_ROOT}/.fifony-local-source-ready`;
|
|
38
38
|
var ATTACHMENTS_ROOT = `${STATE_ROOT}/attachments`;
|
|
39
|
-
var S3DB_DATABASE_PATH = `${STATE_ROOT}/
|
|
40
|
-
var S3DB_BUCKET = env.FIFONY_STORAGE_BUCKET ?? "fifony";
|
|
41
|
-
var S3DB_KEY_PREFIX = env.FIFONY_STORAGE_KEY_PREFIX ?? "state";
|
|
39
|
+
var S3DB_DATABASE_PATH = `${STATE_ROOT}/fifony.sqlite`;
|
|
42
40
|
var S3DB_RUNTIME_RESOURCE = "runtime_state";
|
|
43
41
|
var S3DB_ISSUE_RESOURCE = "issues";
|
|
44
42
|
var S3DB_ISSUE_PLAN_RESOURCE = "issue_plans";
|
|
@@ -75,7 +73,7 @@ var SKIP_SOURCE = FAST_BOOT || CLI_ARGS.includes("--skip-source");
|
|
|
75
73
|
var SKIP_SCAN = FAST_BOOT || CLI_ARGS.includes("--skip-scan");
|
|
76
74
|
var SKIP_RECOVERY = FAST_BOOT || CLI_ARGS.includes("--skip-recovery");
|
|
77
75
|
|
|
78
|
-
// src/
|
|
76
|
+
// src/concerns/helpers.ts
|
|
79
77
|
import { env as env2 } from "process";
|
|
80
78
|
import { parse as parseYaml } from "yaml";
|
|
81
79
|
function now() {
|
|
@@ -537,25 +535,25 @@ function mergeCapabilityProviders(baseProviders, resolution) {
|
|
|
537
535
|
});
|
|
538
536
|
}
|
|
539
537
|
|
|
540
|
-
// src/prompting.ts
|
|
538
|
+
// src/agents/prompting.ts
|
|
541
539
|
import { TemplateEngine } from "recker";
|
|
542
540
|
|
|
543
|
-
// src/generated/prompts.ts
|
|
541
|
+
// src/agents/generated/prompts.ts
|
|
544
542
|
var PROMPT_TEMPLATES = {
|
|
545
543
|
"agent-provider-base": '{{#if isPlanner}}\nRole: planner.\nAnalyze the issue and prepare an execution plan for the implementation agents.\nDo not claim the issue is complete unless the plan itself is the deliverable.\n{{else}}\n{{#if isReviewer}}\nRole: reviewer.\nInspect the workspace and review the current implementation critically.\nIf rework is required, emit `FIFONY_STATUS=continue` and provide actionable `nextPrompt` feedback.\nEmit `FIFONY_STATUS=done` only when the work is acceptable.\n{{else}}\nRole: executor.\nImplement the required changes in the workspace.\nUse any planner guidance or prior reviewer feedback already persisted in the workspace.\n{{/if}}\n{{/if}}\n\n{{#if hasImpeccableOverlay}}\nImpeccable overlay is active.\nRaise the bar on UI polish, clarity, responsiveness, visual hierarchy, and interaction quality.\n{{#if isReviewer}}\nReview with a stricter frontend and product-quality standard than a normal correctness-only pass.\n{{else}}\nWhen touching frontend work, do not settle for baseline implementation quality.\n{{/if}}\n{{/if}}\n\n{{#if hasFrontendDesignOverlay}}\nFrontend-design overlay is active.\nPrefer stronger hierarchy, spacing, and readability decisions over generic implementation choices.\n{{/if}}\n\n{{#if profileInstructions}}\n## Agent Profile\n{{profileInstructions}}\n{{/if}}\n\n{{#if skillContext}}\n{{skillContext}}\n{{/if}}\n\n{{#if capabilityCategory}}\nCapability routing: {{capabilityCategory}}.\nSelection reason: {{selectionReason}}\n{{#if overlays.length}}\nOverlays: {{overlays | join ", "}}.\n{{/if}}\n{{/if}}\n\n{{#if targetPaths.length}}\nTarget paths: {{targetPaths | join ", "}}\n{{/if}}\n\nWorkspace: {{workspacePath}}\n\n{{basePrompt}}\n',
|
|
546
|
-
// src/prompts/agent-provider-base.stub.md
|
|
544
|
+
// src/agents/prompts/agent-provider-base.stub.md
|
|
547
545
|
"agent-turn": 'Continue working on {{issueIdentifier}}.\nTurn {{turnIndex}} of {{maxTurns}}.\n\nBase objective:\n{{basePrompt}}\n\nContinuation guidance:\n{{continuation}}\n\nPrevious command output tail:\n```text\n{{outputTail}}\n```\n\nBefore exiting successfully, emit one of the following control markers:\n- `FIFONY_STATUS=continue` if more turns are required.\n- `FIFONY_STATUS=done` if the issue is complete.\n- `FIFONY_STATUS=blocked` if manual intervention is required.\nYou may also write `fifony-result.json` with `{ "status": "...", "summary": "...", "nextPrompt": "..." }`.\n',
|
|
548
|
-
// src/prompts/agent-turn.stub.md
|
|
546
|
+
// src/agents/prompts/agent-turn.stub.md
|
|
549
547
|
"compile-execution-claude": '{{#if isPlanner}}\nRole: planner. Analyze the issue and prepare an execution plan.\n{{else}}\n{{#if isReviewer}}\nRole: reviewer. Inspect and review the implementation critically.\n{{else}}\nRole: executor. Implement the required changes.\n{{/if}}\n{{/if}}\n\n{{#if profileInstructions}}\n## Agent Profile\n{{profileInstructions}}\n{{/if}}\n\n{{#if skillContext}}\n{{skillContext}}\n{{/if}}\n\n{{planPrompt}}\n\n{{#if subagentsToUse.length}}\n## Subagent Strategy (Claude-specific)\nYou have access to the Agent tool for spawning subagents. Use them for:\n{{#each subagentsToUse}}\n- **{{name}}** ({{role}}): {{why}}\n{{/each}}\n\nLaunch subagents for independent subtasks to maximize parallelism.\nUse the main thread for coordination and integration.\n{{/if}}\n\n{{#if skillsToUse.length}}\n## Skills to Activate\n{{#each skillsToUse}}\n- Invoke **/{{name}}** - {{why}}\n{{/each}}\n{{/if}}\n\n{{#if suggestedPaths.length}}\nTarget paths: {{suggestedPaths | join ", "}}\n{{/if}}\n\nWorkspace: {{workspacePath}}\n\nIssue: {{issueIdentifier}}\nTitle: {{title}}\nDescription: {{description}}\n\n## Structured Input\nThe file `fifony-execution-payload.json` in the workspace contains the canonical structured data for this task.\nUse it as the source of truth for constraints, success criteria, execution intent, and plan details.\nIf there is any conflict between this prompt and the structured fields in the payload, prioritize the payload.\n\n{{#if validationItems.length}}\n## Pre-completion enforcement\nBefore reporting done, verify:\n{{#each validationItems}}\n- {{value}}\n{{/each}}\n{{/if}}\n',
|
|
550
|
-
// src/prompts/compile-execution-claude.stub.md
|
|
548
|
+
// src/agents/prompts/compile-execution-claude.stub.md
|
|
551
549
|
"compile-execution-codex": '{{#if isReviewer}}\nRole: reviewer. Inspect and review the implementation critically.\n{{else}}\n{{#if isPlanner}}\nRole: planner. Analyze and prepare an execution plan.\n{{else}}\nRole: executor. Implement the required changes in the workspace.\n{{/if}}\n{{/if}}\n\n{{#if profileInstructions}}\n## Agent Profile\n{{profileInstructions}}\n{{/if}}\n\n{{#if skillContext}}\n{{skillContext}}\n{{/if}}\n\nIssue: {{issueIdentifier}}\nTitle: {{title}}\nDescription: {{description}}\nWorkspace: {{workspacePath}}\n\n{{planPrompt}}\n\n{{#if phases.length}}\n## Checkpoint Execution (Codex mode)\nExecute in strict phases. After each phase, verify outputs before proceeding.\n{{#each phases}}\n- **{{phaseName}}**: {{goal}}\n{{#if outputs.length}} Checkpoint: verify {{outputs | join ", "}} before next phase.{{/if}}\n{{/each}}\n{{else}}\n## Execution Order\nExecute steps in order. Verify each step\'s `doneWhen` criterion before proceeding.\n{{/if}}\n\n{{#if suggestedPaths.length}}\nTarget paths: {{suggestedPaths | join ", "}}\nFocus changes on these paths. Do not make unnecessary changes elsewhere.\n{{/if}}\n\n{{#if skillsToUse.length}}\n## Specialized Procedures\n{{#each skillsToUse}}\n- Apply **{{name}}** procedure: {{why}}\n{{/each}}\n{{/if}}\n\n{{#if validationItems.length}}\n## Pre-completion checks\nBefore reporting done, run:\n{{#each validationItems}}\n- {{value}}\n{{/each}}\n{{/if}}\n\n## Structured Input\nThe file `fifony-execution-payload.json` in the workspace contains the canonical structured data for this task.\nUse it as the source of truth for constraints, success criteria, execution intent, and plan details.\nIf there is any conflict between this prompt and the structured fields in the payload, prioritize the payload.\n\n## Output Format\n\n{{outputContract}}\n',
|
|
552
|
-
// src/prompts/compile-execution-codex.stub.md
|
|
550
|
+
// src/agents/prompts/compile-execution-codex.stub.md
|
|
553
551
|
"compile-review": "Review the work done for {{issueIdentifier}}.\n\nTitle: {{title}}\nDescription: {{description}}\nWorkspace: {{workspacePath}}\n\n{{#if planPrompt}}\n# Original Execution Plan\n\n{{planPrompt}}\n{{/if}}\n\n{{#if successCriteria.length}}\n# Success Criteria (evaluate against these)\n{{#each successCriteria}}\n- [ ] {{value}}\n{{/each}}\n{{/if}}\n\n{{#if deliverables.length}}\n# Expected Deliverables\n{{#each deliverables}}\n- [ ] {{value}}\n{{/each}}\n{{/if}}\n\n{{#if diffSummary}}\n# Changes Made (diff summary)\n```\n{{diffSummary}}\n```\n{{/if}}\n\n# Structured Context\nIf `fifony-execution-payload.json` exists in the workspace, read it for the canonical structured task data.\nUse the `successCriteria`, `constraints`, and `deliverables` fields as your evaluation checklist.\n\n# Review Instructions\n\n1. Verify each success criterion from the plan is met.\n2. Check that all expected deliverables are present.\n3. Review the diff for correctness, security issues, and code quality.\n4. Verify validation checks pass (run commands if specified in the plan).\n5. Check for unintended side effects or regressions.\n\nIf the work is acceptable, emit FIFONY_STATUS=done.\nIf rework is needed, emit FIFONY_STATUS=continue and provide actionable feedback in nextPrompt.\nIf the work is fundamentally broken, emit FIFONY_STATUS=blocked.\n",
|
|
554
|
-
// src/prompts/compile-review.stub.md
|
|
552
|
+
// src/agents/prompts/compile-review.stub.md
|
|
555
553
|
"integrations-agency-agents": '---\nagent:\n providers:\n - provider: claude\n role: planner\n profile: agency-senior-project-manager\n - provider: codex\n role: executor\n profile: agency-senior-developer\n - provider: claude\n role: reviewer\n profile: agency-code-reviewer\ncodex:\n command: "codex"\nclaude:\n command: "claude"\n---\n\nUse local agency agent profiles discovered from workspace or home directories.\nWorkspace: {{workspaceRoot}}\n',
|
|
556
|
-
// src/prompts/integrations-agency-agents.stub.md
|
|
554
|
+
// src/agents/prompts/integrations-agency-agents.stub.md
|
|
557
555
|
"integrations-impeccable": '# Impeccable integration idea\n\nUse impeccable-oriented skills as a frontend review layer around fifony issues.\n\nSuggested pattern:\n\n1. Use `agency-senior-developer` or `codex` as executor.\n2. Route UI-heavy issues to a reviewer prompt that explicitly asks for impeccable-style critique.\n3. Expose the resulting review through the fifony MCP prompts or as a follow-up review issue.\n\nSuggested labels:\n- frontend\n- ui\n- design-system\n\nSuggested reviewer prompt seed:\n"Review this implementation using impeccable standards for frontend quality, polish, hierarchy, spacing, responsiveness, and interaction clarity."\n',
|
|
558
|
-
// src/prompts/integrations-impeccable.stub.md
|
|
556
|
+
// src/agents/prompts/integrations-impeccable.stub.md
|
|
559
557
|
"issue-enhancer-description": `You are helping improve issue metadata for a software execution queue.
|
|
560
558
|
Rewrite the description to be clearer, complete, and directly actionable.
|
|
561
559
|
Return strict JSON only with this schema:
|
|
@@ -582,9 +580,9 @@ Rules:
|
|
|
582
580
|
- Avoid extra wrappers, outer quotes, or extra explanation.
|
|
583
581
|
- The value should be in Portuguese if the input is in Portuguese; otherwise in English.
|
|
584
582
|
`,
|
|
585
|
-
// src/prompts/issue-enhancer-description.stub.md
|
|
583
|
+
// src/agents/prompts/issue-enhancer-description.stub.md
|
|
586
584
|
"issue-enhancer-title": 'You are helping improve issue metadata for a software execution queue.\nRewrite the title for clarity, actionability, and specificity.\nReturn strict JSON only with this schema:\n{ "field": "title", "value": "..." }\n\nIssue type: {{issueType}}\nCurrent title: {{title}}\nDescription context: {{description}}\n{{#if images}}\nVisual evidence (attached screenshots for context):\n{{#each images}}\n- {{this}}\n{{/each}}\n{{/if}}\n\nRules:\n- Keep it concise and suitable as a task title.\n- Use imperative language when possible.\n- If the issue type is "bug", start with "fix: ". If "feature", start with "feat: ". If "refactor", start with "refactor: ". If "docs", start with "docs: ". If "chore", start with "chore: ". For "blank", use no prefix.\n- Do not include markdown, quotes, or extra explanation.\n- The value should be in Portuguese if the input is in Portuguese; otherwise in English.\n',
|
|
587
|
-
// src/prompts/issue-enhancer-title.stub.md
|
|
585
|
+
// src/agents/prompts/issue-enhancer-title.stub.md
|
|
588
586
|
"issue-planner": `You are a senior technical execution planner.
|
|
589
587
|
Produce the best possible plan for the issue below, filling the JSON schema precisely.
|
|
590
588
|
{{#if fast}}
|
|
@@ -633,50 +631,51 @@ Effort suggestion:
|
|
|
633
631
|
- Set per-role if different: planner, executor, reviewer
|
|
634
632
|
{{/unless}}
|
|
635
633
|
|
|
636
|
-
Return strict JSON matching
|
|
634
|
+
Return strict JSON matching this schema. No text outside JSON. Use these exact field names.
|
|
635
|
+
IMPORTANT: Replace ALL placeholder values with real content specific to the issue above. Do NOT copy the example values literally \u2014 every field must contain actual plan content derived from the issue.
|
|
637
636
|
|
|
638
637
|
\`\`\`json
|
|
639
638
|
{
|
|
640
|
-
"summary": "one-line summary
|
|
639
|
+
"summary": "<YOUR one-line summary here>",
|
|
641
640
|
"estimatedComplexity": "trivial|low|medium|high",
|
|
642
641
|
"steps": [
|
|
643
642
|
{
|
|
644
643
|
"step": 1,
|
|
645
|
-
"action": "
|
|
646
|
-
"files": ["path/to/file.ts"],
|
|
647
|
-
"details": "additional context
|
|
644
|
+
"action": "<YOUR concrete action here>",
|
|
645
|
+
"files": ["<real/path/to/file.ts>"],
|
|
646
|
+
"details": "<YOUR additional context>",
|
|
648
647
|
"ownerType": "agent|human|skill|subagent|tool",
|
|
649
|
-
"doneWhen": "acceptance criterion
|
|
648
|
+
"doneWhen": "<YOUR acceptance criterion>"
|
|
650
649
|
}
|
|
651
650
|
],
|
|
652
|
-
"assumptions": ["
|
|
653
|
-
"constraints": ["
|
|
651
|
+
"assumptions": ["<YOUR assumptions>"],
|
|
652
|
+
"constraints": ["<YOUR constraints>"],
|
|
654
653
|
"unknowns": [
|
|
655
|
-
{ "question": "
|
|
654
|
+
{ "question": "<YOUR question>", "whyItMatters": "<YOUR reason>", "howToResolve": "<YOUR approach>" }
|
|
656
655
|
],
|
|
657
|
-
"successCriteria": ["
|
|
656
|
+
"successCriteria": ["<YOUR criteria>"],
|
|
658
657
|
"risks": [
|
|
659
|
-
{ "risk": "
|
|
658
|
+
{ "risk": "<YOUR risk>", "impact": "<YOUR impact>", "mitigation": "<YOUR mitigation>" }
|
|
660
659
|
],
|
|
661
|
-
"suggestedPaths": ["path/to/relevant/file.ts"],
|
|
662
|
-
"suggestedLabels": ["frontend", "bug", "feature"
|
|
660
|
+
"suggestedPaths": ["<real/path/to/relevant/file.ts>"],
|
|
661
|
+
"suggestedLabels": ["frontend", "bug", "feature"],
|
|
663
662
|
"suggestedEffort": { "default": "medium", "planner": "low", "executor": "medium", "reviewer": "medium" }
|
|
664
663
|
}
|
|
665
664
|
\`\`\`
|
|
666
665
|
`,
|
|
667
|
-
// src/prompts/issue-planner.stub.md
|
|
666
|
+
// src/agents/prompts/issue-planner.stub.md
|
|
668
667
|
"issue-planner-refine": "You are a senior technical execution planner refining an existing plan based on user feedback.\n\n## Original Issue\nTitle: {{title}}\nDescription: {{description}}\n\n## Current Plan (JSON)\n```json\n{{currentPlan}}\n```\n\n## User Feedback\n{{feedback}}\n\n## Instructions\n\nRevise the plan above to address the user's feedback precisely.\n\nRules:\n- Keep all parts of the plan that are NOT affected by the feedback unchanged.\n- Only modify, add, or remove elements that the feedback specifically requests.\n- Preserve the same JSON schema structure as the current plan.\n- Maintain step numbering consistency after changes.\n- If feedback asks to add steps, insert them in the logical position and renumber.\n- If feedback asks to remove steps, renumber remaining steps sequentially.\n- Update the summary if the overall direction changed.\n- Re-evaluate estimatedComplexity if the scope changed significantly.\n- Update suggestedPaths and suggestedLabels if affected by the changes.\n\nReturn strict JSON. No text outside JSON.\n",
|
|
669
|
-
// src/prompts/issue-planner-refine.stub.md
|
|
668
|
+
// src/agents/prompts/issue-planner-refine.stub.md
|
|
670
669
|
"mcp-integrate-client": "Integrate {{client}} with the local fifony MCP server.\n\nGoal: {{goal}}\n\n{{integrationGuide}}\n\nUse the available fifony resources and tools instead of inventing your own persistence model.\n",
|
|
671
|
-
// src/prompts/mcp-integrate-client.stub.md
|
|
670
|
+
// src/agents/prompts/mcp-integrate-client.stub.md
|
|
672
671
|
"mcp-integration-guide": '# fifony MCP integration\n\nWorkspace root: `{{workspaceRoot}}`\nPersistence root: `{{persistenceRoot}}`\nState root: `{{stateRoot}}`\n\nRecommended MCP client command:\n\n```json\n{\n "mcpServers": {\n "fifony": {\n "command": "npx",\n "args": ["fifony", "mcp", "--workspace", "{{workspaceRoot}}", "--persistence", "{{persistenceRoot}}"]\n }\n }\n}\n```\n\nExpected workflow:\n\n1. Read `fifony://guide/overview` and `fifony://state/summary`.\n2. Use `fifony.list_issues` or read `fifony://issues`.\n3. Create work with `fifony.create_issue`.\n4. Update workflow state with `fifony.update_issue_state`.\n5. Use the prompts exposed by this MCP server to structure planning or execution.\n\nThe MCP server is read-write against the same `s3db` filesystem store used by the fifony runtime.\n',
|
|
673
|
-
// src/prompts/mcp-integration-guide.stub.md
|
|
672
|
+
// src/agents/prompts/mcp-integration-guide.stub.md
|
|
674
673
|
"mcp-issue": 'You are integrating with fifony as the {{role}} using {{provider}}.\n\nIssue ID: {{id}}\nTitle: {{title}}\nState: {{state}}\nCapability category: {{capabilityCategory}}\n{{#if overlays.length}}\nOverlays: {{overlays | join ", "}}\n{{/if}}\n{{#if paths.length}}\nPaths: {{paths | join ", "}}\n{{/if}}\nDescription:\n{{description}}\n\nUse fifony as the source of truth:\n- Persist transitions through the fifony tools instead of inventing local state.\n- Keep outputs actionable and aligned with the tracked issue lifecycle.\n',
|
|
675
|
-
// src/prompts/mcp-issue.stub.md
|
|
674
|
+
// src/agents/prompts/mcp-issue.stub.md
|
|
676
675
|
"mcp-review-workflow": "Review the pipeline configuration for this fifony workspace as {{provider}}.\n\nWorkspace: {{workspaceRoot}}\n\nFocus on:\n- provider orchestration quality (plan/execute/review stages)\n- hooks safety (beforeRun, afterRun, afterCreate, beforeRemove)\n- prompt clarity\n- issue lifecycle correctness\n- what an MCP client needs in order to integrate cleanly\n",
|
|
677
|
-
// src/prompts/mcp-review-workflow.stub.md
|
|
676
|
+
// src/agents/prompts/mcp-review-workflow.stub.md
|
|
678
677
|
"mcp-route-task": "Use this routing decision as the execution plan for the task.\n\n{{resolutionJson}}\n",
|
|
679
|
-
// src/prompts/mcp-route-task.stub.md
|
|
678
|
+
// src/agents/prompts/mcp-route-task.stub.md
|
|
680
679
|
"project-analysis": `You are analyzing a software project to help configure an AI-powered development assistant.
|
|
681
680
|
|
|
682
681
|
Look at the project structure, source code, configuration files, and any documentation you can find. Pay special attention to:
|
|
@@ -702,14 +701,14 @@ For "suggestedAgents", choose from: frontend-developer, backend-architect, datab
|
|
|
702
701
|
|
|
703
702
|
Return ONLY the JSON object. No markdown fences, no explanation, no extra text.
|
|
704
703
|
`,
|
|
705
|
-
// src/prompts/project-analysis.stub.md
|
|
704
|
+
// src/agents/prompts/project-analysis.stub.md
|
|
706
705
|
"workflow-default": "You are working on {{issue.identifier}}.\n\nTitle: {{issue.title}}\nDescription:\n{{issue.description}}\n",
|
|
707
|
-
// src/prompts/workflow-default.stub.md
|
|
706
|
+
// src/agents/prompts/workflow-default.stub.md
|
|
708
707
|
"workflow-plan-section": '## Execution Plan\n\nComplexity: {{estimatedComplexity}}\nSummary: {{summary}}\n\nSteps:\n{{#each steps}}\n{{step}}. {{action}}{{#if files.length}} (files: {{files | join ", "}}){{/if}}{{#if details}} - {{details}}{{/if}}\n{{/each}}\n\nFollow this plan. Complete each step in order.\n'
|
|
709
|
-
// src/prompts/workflow-plan-section.stub.md
|
|
708
|
+
// src/agents/prompts/workflow-plan-section.stub.md
|
|
710
709
|
};
|
|
711
710
|
|
|
712
|
-
// src/prompting.ts
|
|
711
|
+
// src/agents/prompting.ts
|
|
713
712
|
var engine = new TemplateEngine({
|
|
714
713
|
cache: true,
|
|
715
714
|
format: "raw",
|
|
@@ -736,8 +735,6 @@ export {
|
|
|
736
735
|
SOURCE_MARKER,
|
|
737
736
|
ATTACHMENTS_ROOT,
|
|
738
737
|
S3DB_DATABASE_PATH,
|
|
739
|
-
S3DB_BUCKET,
|
|
740
|
-
S3DB_KEY_PREFIX,
|
|
741
738
|
S3DB_RUNTIME_RESOURCE,
|
|
742
739
|
S3DB_ISSUE_RESOURCE,
|
|
743
740
|
S3DB_ISSUE_PLAN_RESOURCE,
|
|
@@ -782,4 +779,4 @@ export {
|
|
|
782
779
|
mergeCapabilityProviders,
|
|
783
780
|
renderPrompt
|
|
784
781
|
};
|
|
785
|
-
//# sourceMappingURL=chunk-
|
|
782
|
+
//# sourceMappingURL=chunk-DD5BE2W6.js.map
|