fifony 0.1.21 → 0.1.22-next.ca3682c
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 +19 -7
- package/app/dist/assets/KeyboardShortcutsHelp-CqCRSinb.js +1 -0
- package/app/dist/assets/OnboardingWizard-sB4Jirx5.js +1 -0
- package/app/dist/assets/analytics.lazy-BPdQoH_Z.js +1 -0
- package/app/dist/assets/{createLucideIcon-DtZs0TX0.js → createLucideIcon-BWC-guQt.js} +1 -1
- package/app/dist/assets/index-BaDR-1h-.js +43 -0
- package/app/dist/assets/index-DjmUHXd1.css +1 -0
- package/app/dist/assets/vendor-BTlTWMUF.js +9 -0
- package/app/dist/dinofffaur.png +0 -0
- package/app/dist/index.html +4 -5
- package/app/dist/service-worker.js +1 -1
- package/app/public/dinofffaur.png +0 -0
- package/bin/fifony-wrap.js +53 -0
- package/dist/agent/cli-wrapper.js +78 -0
- package/dist/agent/cli-wrapper.js.map +1 -0
- package/dist/agent/run-local.js +217 -7893
- package/dist/agent/run-local.js.map +1 -1
- package/dist/chunk-CWC7H4H3.js +91 -0
- package/dist/chunk-CWC7H4H3.js.map +1 -0
- package/dist/{chunk-SMGXYOWU.js → chunk-DD5BE2W6.js} +430 -31
- 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-IXE2JEIU.js +7115 -0
- package/dist/chunk-IXE2JEIU.js.map +1 -0
- package/dist/chunk-NA6PJPZD.js +2110 -0
- package/dist/chunk-NA6PJPZD.js.map +1 -0
- package/dist/cli.js +187 -1
- package/dist/cli.js.map +1 -1
- package/dist/issue-runner-OEUARE4A.js +13 -0
- package/dist/issue-runner-OEUARE4A.js.map +1 -0
- package/dist/issue-state-machine-OKQIWTVN.js +37 -0
- package/dist/issue-state-machine-OKQIWTVN.js.map +1 -0
- package/dist/mcp/server.js +592 -605
- package/dist/mcp/server.js.map +1 -1
- package/dist/queue-workers-SVNB4ESJ.js +20 -0
- package/dist/queue-workers-SVNB4ESJ.js.map +1 -0
- package/dist/store-NIW6OEYC.js +56 -0
- package/dist/store-NIW6OEYC.js.map +1 -0
- package/package.json +10 -9
- package/FIFONY.md +0 -173
- package/app/dist/assets/KeyboardShortcutsHelp-BTjiQe_Y.js +0 -1
- package/app/dist/assets/OnboardingWizard-BALlquG0.js +0 -1
- package/app/dist/assets/analytics.lazy-DjSzXIey.js +0 -1
- package/app/dist/assets/index-BV11ScVl.js +0 -42
- package/app/dist/assets/index-DWbxgKSd.css +0 -1
- package/app/dist/assets/vendor-BoGBoEwT.js +0 -9
- package/app/dist/assets/zap-DpjdVd1i.js +0 -1
- package/dist/chunk-SMGXYOWU.js.map +0 -1
- package/src/fixtures/agent-catalog.json +0 -208
- package/src/fixtures/skill-catalog.json +0 -67
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-DVU3CXWA.js";
|
|
4
|
+
|
|
5
|
+
// src/persistence/plugins/queue-workers.ts
|
|
6
|
+
var runtimeState = null;
|
|
7
|
+
var active = false;
|
|
8
|
+
async function initQueueWorkers(state) {
|
|
9
|
+
runtimeState = state;
|
|
10
|
+
active = true;
|
|
11
|
+
logger.info("[QueueWorkers] Workers ready (direct dispatch)");
|
|
12
|
+
}
|
|
13
|
+
async function stopQueueWorkers() {
|
|
14
|
+
active = false;
|
|
15
|
+
runtimeState = null;
|
|
16
|
+
logger.info("[QueueWorkers] Workers stopped");
|
|
17
|
+
}
|
|
18
|
+
function areQueueWorkersActive() {
|
|
19
|
+
return active;
|
|
20
|
+
}
|
|
21
|
+
function getCurrentIssue(id) {
|
|
22
|
+
return runtimeState?.issues.find((i) => i.id === id);
|
|
23
|
+
}
|
|
24
|
+
async function enqueueForPlanning(issue) {
|
|
25
|
+
if (!active || !runtimeState) return;
|
|
26
|
+
const current = getCurrentIssue(issue.id);
|
|
27
|
+
if (!current || current.state !== "Planning") {
|
|
28
|
+
logger.debug({ issueId: issue.id, state: current?.state }, "[QueueWorkers:plan] Skipping \u2014 not in Planning state");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (current.planningStatus === "planning") {
|
|
32
|
+
logger.debug({ issueId: issue.id }, "[QueueWorkers:plan] Already planning, skipping");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
logger.info({ issueId: issue.id, identifier: current.identifier }, "[QueueWorkers:plan] Dispatching planning job");
|
|
36
|
+
try {
|
|
37
|
+
const { runPlanningJob } = await import("./issue-runner-OEUARE4A.js");
|
|
38
|
+
await runPlanningJob(runtimeState, current);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
logger.error({ err: error, issueId: issue.id }, "[QueueWorkers:plan] Planning job failed");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function enqueueForExecution(issue) {
|
|
44
|
+
if (!active || !runtimeState) return;
|
|
45
|
+
const running = /* @__PURE__ */ new Set();
|
|
46
|
+
const { runIssueOnce } = await import("./issue-runner-OEUARE4A.js");
|
|
47
|
+
while (active && runtimeState) {
|
|
48
|
+
const current = getCurrentIssue(issue.id);
|
|
49
|
+
if (!current || current.state !== "Queued" && current.state !== "Running") {
|
|
50
|
+
logger.debug({ issueId: issue.id, state: current?.state }, "[QueueWorkers:execute] Issue no longer in Queued/Running \u2014 stopping dispatch loop");
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
logger.info({ issueId: issue.id, identifier: current.identifier, state: current.state }, "[QueueWorkers:execute] Dispatching execution job");
|
|
54
|
+
try {
|
|
55
|
+
await runIssueOnce(runtimeState, current, running);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
logger.error({ err: error, issueId: issue.id }, "[QueueWorkers:execute] Execution job failed");
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function enqueueForReview(issue) {
|
|
63
|
+
if (!active || !runtimeState) return;
|
|
64
|
+
const current = getCurrentIssue(issue.id);
|
|
65
|
+
if (!current || current.state !== "Reviewing") {
|
|
66
|
+
logger.debug({ issueId: issue.id, state: current?.state }, "[QueueWorkers:review] Skipping \u2014 not in Reviewing state");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
logger.info({ issueId: issue.id, identifier: current.identifier }, "[QueueWorkers:review] Dispatching review job");
|
|
70
|
+
const running = /* @__PURE__ */ new Set();
|
|
71
|
+
try {
|
|
72
|
+
const { runIssueOnce } = await import("./issue-runner-OEUARE4A.js");
|
|
73
|
+
await runIssueOnce(runtimeState, current, running);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
logger.error({ err: error, issueId: issue.id }, "[QueueWorkers:review] Review job failed");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function getQueueStats() {
|
|
79
|
+
return { plan: null, execute: null, review: null };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
initQueueWorkers,
|
|
84
|
+
stopQueueWorkers,
|
|
85
|
+
areQueueWorkersActive,
|
|
86
|
+
enqueueForPlanning,
|
|
87
|
+
enqueueForExecution,
|
|
88
|
+
enqueueForReview,
|
|
89
|
+
getQueueStats
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=chunk-CWC7H4H3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/persistence/plugins/queue-workers.ts"],"sourcesContent":["import type { IssueEntry, RuntimeState } from \"../../types.ts\";\nimport { logger } from \"../../concerns/logger.ts\";\n\nlet runtimeState: RuntimeState | null = null;\nlet active = false;\n\nexport async function initQueueWorkers(state: RuntimeState): Promise<void> {\n runtimeState = state;\n active = true;\n logger.info(\"[QueueWorkers] Workers ready (direct dispatch)\");\n}\n\nexport async function stopQueueWorkers(): Promise<void> {\n active = false;\n runtimeState = null;\n logger.info(\"[QueueWorkers] Workers stopped\");\n}\n\nexport function areQueueWorkersActive(): boolean {\n return active;\n}\n\nfunction getCurrentIssue(id: string): IssueEntry | undefined {\n return runtimeState?.issues.find((i) => i.id === id);\n}\n\n// ── Dispatch: call handlers directly, no queue middleware ─────────────────\n\nexport async function enqueueForPlanning(issue: IssueEntry): Promise<void> {\n if (!active || !runtimeState) return;\n const current = getCurrentIssue(issue.id);\n if (!current || current.state !== \"Planning\") {\n logger.debug({ issueId: issue.id, state: current?.state }, \"[QueueWorkers:plan] Skipping — not in Planning state\");\n return;\n }\n if (current.planningStatus === \"planning\") {\n logger.debug({ issueId: issue.id }, \"[QueueWorkers:plan] Already planning, skipping\");\n return;\n }\n logger.info({ issueId: issue.id, identifier: current.identifier }, \"[QueueWorkers:plan] Dispatching planning job\");\n try {\n const { runPlanningJob } = await import(\"../../agents/issue-runner.ts\");\n await runPlanningJob(runtimeState, current);\n } catch (error) {\n logger.error({ err: error, issueId: issue.id }, \"[QueueWorkers:plan] Planning job failed\");\n }\n}\n\nexport async function enqueueForExecution(issue: IssueEntry): Promise<void> {\n if (!active || !runtimeState) return;\n const running = new Set<string>();\n const { runIssueOnce } = await import(\"../../agents/issue-runner.ts\");\n\n // Loop: keep running until the issue leaves Running/Queued state (e.g., moves to Reviewing/Done/Blocked)\n while (active && runtimeState) {\n const current = getCurrentIssue(issue.id);\n if (!current || (current.state !== \"Queued\" && current.state !== \"Running\")) {\n logger.debug({ issueId: issue.id, state: current?.state }, \"[QueueWorkers:execute] Issue no longer in Queued/Running — stopping dispatch loop\");\n break;\n }\n logger.info({ issueId: issue.id, identifier: current.identifier, state: current.state }, \"[QueueWorkers:execute] Dispatching execution job\");\n try {\n await runIssueOnce(runtimeState, current, running);\n } catch (error) {\n logger.error({ err: error, issueId: issue.id }, \"[QueueWorkers:execute] Execution job failed\");\n break;\n }\n }\n}\n\nexport async function enqueueForReview(issue: IssueEntry): Promise<void> {\n if (!active || !runtimeState) return;\n const current = getCurrentIssue(issue.id);\n if (!current || current.state !== \"Reviewing\") {\n logger.debug({ issueId: issue.id, state: current?.state }, \"[QueueWorkers:review] Skipping — not in Reviewing state\");\n return;\n }\n logger.info({ issueId: issue.id, identifier: current.identifier }, \"[QueueWorkers:review] Dispatching review job\");\n const running = new Set<string>();\n try {\n const { runIssueOnce } = await import(\"../../agents/issue-runner.ts\");\n await runIssueOnce(runtimeState, current, running);\n } catch (error) {\n logger.error({ err: error, issueId: issue.id }, \"[QueueWorkers:review] Review job failed\");\n }\n}\n\nexport async function getQueueStats(): Promise<Record<string, unknown>> {\n return { plan: null, execute: null, review: null };\n}\n"],"mappings":";;;;;AAGA,IAAI,eAAoC;AACxC,IAAI,SAAS;AAEb,eAAsB,iBAAiB,OAAoC;AACzE,iBAAe;AACf,WAAS;AACT,SAAO,KAAK,gDAAgD;AAC9D;AAEA,eAAsB,mBAAkC;AACtD,WAAS;AACT,iBAAe;AACf,SAAO,KAAK,gCAAgC;AAC9C;AAEO,SAAS,wBAAiC;AAC/C,SAAO;AACT;AAEA,SAAS,gBAAgB,IAAoC;AAC3D,SAAO,cAAc,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AACrD;AAIA,eAAsB,mBAAmB,OAAkC;AACzE,MAAI,CAAC,UAAU,CAAC,aAAc;AAC9B,QAAM,UAAU,gBAAgB,MAAM,EAAE;AACxC,MAAI,CAAC,WAAW,QAAQ,UAAU,YAAY;AAC5C,WAAO,MAAM,EAAE,SAAS,MAAM,IAAI,OAAO,SAAS,MAAM,GAAG,2DAAsD;AACjH;AAAA,EACF;AACA,MAAI,QAAQ,mBAAmB,YAAY;AACzC,WAAO,MAAM,EAAE,SAAS,MAAM,GAAG,GAAG,gDAAgD;AACpF;AAAA,EACF;AACA,SAAO,KAAK,EAAE,SAAS,MAAM,IAAI,YAAY,QAAQ,WAAW,GAAG,8CAA8C;AACjH,MAAI;AACF,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,4BAA8B;AACtE,UAAM,eAAe,cAAc,OAAO;AAAA,EAC5C,SAAS,OAAO;AACd,WAAO,MAAM,EAAE,KAAK,OAAO,SAAS,MAAM,GAAG,GAAG,yCAAyC;AAAA,EAC3F;AACF;AAEA,eAAsB,oBAAoB,OAAkC;AAC1E,MAAI,CAAC,UAAU,CAAC,aAAc;AAC9B,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,EAAE,aAAa,IAAI,MAAM,OAAO,4BAA8B;AAGpE,SAAO,UAAU,cAAc;AAC7B,UAAM,UAAU,gBAAgB,MAAM,EAAE;AACxC,QAAI,CAAC,WAAY,QAAQ,UAAU,YAAY,QAAQ,UAAU,WAAY;AAC3E,aAAO,MAAM,EAAE,SAAS,MAAM,IAAI,OAAO,SAAS,MAAM,GAAG,wFAAmF;AAC9I;AAAA,IACF;AACA,WAAO,KAAK,EAAE,SAAS,MAAM,IAAI,YAAY,QAAQ,YAAY,OAAO,QAAQ,MAAM,GAAG,kDAAkD;AAC3I,QAAI;AACF,YAAM,aAAa,cAAc,SAAS,OAAO;AAAA,IACnD,SAAS,OAAO;AACd,aAAO,MAAM,EAAE,KAAK,OAAO,SAAS,MAAM,GAAG,GAAG,6CAA6C;AAC7F;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,iBAAiB,OAAkC;AACvE,MAAI,CAAC,UAAU,CAAC,aAAc;AAC9B,QAAM,UAAU,gBAAgB,MAAM,EAAE;AACxC,MAAI,CAAC,WAAW,QAAQ,UAAU,aAAa;AAC7C,WAAO,MAAM,EAAE,SAAS,MAAM,IAAI,OAAO,SAAS,MAAM,GAAG,8DAAyD;AACpH;AAAA,EACF;AACA,SAAO,KAAK,EAAE,SAAS,MAAM,IAAI,YAAY,QAAQ,WAAW,GAAG,8CAA8C;AACjH,QAAM,UAAU,oBAAI,IAAY;AAChC,MAAI;AACF,UAAM,EAAE,aAAa,IAAI,MAAM,OAAO,4BAA8B;AACpE,UAAM,aAAa,cAAc,SAAS,OAAO;AAAA,EACnD,SAAS,OAAO;AACd,WAAO,MAAM,EAAE,KAAK,OAAO,SAAS,MAAM,GAAG,GAAG,yCAAyC;AAAA,EAC3F;AACF;AAEA,eAAsB,gBAAkD;AACtE,SAAO,EAAE,MAAM,MAAM,SAAS,MAAM,QAAQ,KAAK;AACnD;","names":[]}
|
|
@@ -1,3 +1,252 @@
|
|
|
1
|
+
// src/concerns/constants.ts
|
|
2
|
+
import { basename, dirname, join, resolve } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { env, argv, cwd as getCwd } from "process";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
var __dirname = dirname(__filename);
|
|
8
|
+
var PACKAGE_ROOT = resolve(__dirname, "../..");
|
|
9
|
+
var CLI_ARGS = argv.slice(2);
|
|
10
|
+
function readArgValue(args, flag) {
|
|
11
|
+
const index = args.indexOf(flag);
|
|
12
|
+
if (index === -1) return void 0;
|
|
13
|
+
const value = args[index + 1];
|
|
14
|
+
if (!value || value.startsWith("--")) return void 0;
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function resolveInputPath(value) {
|
|
18
|
+
if (value.startsWith("~/")) {
|
|
19
|
+
return resolve(homedir(), value.slice(2));
|
|
20
|
+
}
|
|
21
|
+
return resolve(value);
|
|
22
|
+
}
|
|
23
|
+
function resolvePersistenceRoot(value) {
|
|
24
|
+
const resolved = value.startsWith("file://") ? fileURLToPath(value) : resolveInputPath(value);
|
|
25
|
+
return basename(resolved) === ".fifony" ? resolved : join(resolved, ".fifony");
|
|
26
|
+
}
|
|
27
|
+
var CLI_WORKSPACE_ROOT = readArgValue(CLI_ARGS, "--workspace");
|
|
28
|
+
var CLI_PERSISTENCE = readArgValue(CLI_ARGS, "--persistence");
|
|
29
|
+
var TARGET_ROOT = resolveInputPath(
|
|
30
|
+
env.FIFONY_WORKSPACE_ROOT ?? CLI_WORKSPACE_ROOT ?? getCwd()
|
|
31
|
+
);
|
|
32
|
+
var STATE_ROOT = resolvePersistenceRoot(
|
|
33
|
+
env.FIFONY_PERSISTENCE ?? CLI_PERSISTENCE ?? env.FIFONY_BOOTSTRAP_ROOT ?? TARGET_ROOT
|
|
34
|
+
);
|
|
35
|
+
var SOURCE_ROOT = `${STATE_ROOT}/source`;
|
|
36
|
+
var WORKSPACE_ROOT = `${STATE_ROOT}/workspaces`;
|
|
37
|
+
var SOURCE_MARKER = `${SOURCE_ROOT}/.fifony-local-source-ready`;
|
|
38
|
+
var ATTACHMENTS_ROOT = `${STATE_ROOT}/attachments`;
|
|
39
|
+
var S3DB_DATABASE_PATH = `${STATE_ROOT}/fifony.sqlite`;
|
|
40
|
+
var S3DB_RUNTIME_RESOURCE = "runtime_state";
|
|
41
|
+
var S3DB_ISSUE_RESOURCE = "issues";
|
|
42
|
+
var S3DB_ISSUE_PLAN_RESOURCE = "issue_plans";
|
|
43
|
+
var S3DB_EVENT_RESOURCE = "events";
|
|
44
|
+
var S3DB_SETTINGS_RESOURCE = "settings";
|
|
45
|
+
var S3DB_AGENT_SESSION_RESOURCE = "agent_sessions";
|
|
46
|
+
var S3DB_AGENT_PIPELINE_RESOURCE = "agent_pipelines";
|
|
47
|
+
var S3DB_RUNTIME_RECORD_ID = "current";
|
|
48
|
+
var S3DB_RUNTIME_SCHEMA_VERSION = 1;
|
|
49
|
+
var FRONTEND_DIR = `${PACKAGE_ROOT}/app/dist`;
|
|
50
|
+
var FRONTEND_INDEX = `${FRONTEND_DIR}/index.html`;
|
|
51
|
+
var FRONTEND_MANIFEST_JSON = `${FRONTEND_DIR}/manifest.webmanifest`;
|
|
52
|
+
var FRONTEND_SERVICE_WORKER_JS = `${FRONTEND_DIR}/service-worker.js`;
|
|
53
|
+
var FRONTEND_ICON_SVG = `${FRONTEND_DIR}/icon.svg`;
|
|
54
|
+
var FRONTEND_MASKABLE_ICON_SVG = `${FRONTEND_DIR}/icon-maskable.svg`;
|
|
55
|
+
var FRONTEND_OFFLINE_HTML = `${FRONTEND_DIR}/offline.html`;
|
|
56
|
+
var DEBUG_BOOT = env.FIFONY_DEBUG_BOOT === "1";
|
|
57
|
+
var ALLOWED_STATES = [
|
|
58
|
+
"Planning",
|
|
59
|
+
"Planned",
|
|
60
|
+
"Queued",
|
|
61
|
+
"Running",
|
|
62
|
+
"Reviewing",
|
|
63
|
+
"Reviewed",
|
|
64
|
+
"Blocked",
|
|
65
|
+
"Done",
|
|
66
|
+
"Cancelled"
|
|
67
|
+
];
|
|
68
|
+
var TERMINAL_STATES = /* @__PURE__ */ new Set(["Done", "Cancelled"]);
|
|
69
|
+
var EXECUTING_STATES = /* @__PURE__ */ new Set(["Running", "Reviewing"]);
|
|
70
|
+
var PERSIST_EVENTS_MAX = 500;
|
|
71
|
+
var FAST_BOOT = CLI_ARGS.includes("--fast-boot");
|
|
72
|
+
var SKIP_SOURCE = FAST_BOOT || CLI_ARGS.includes("--skip-source");
|
|
73
|
+
var SKIP_SCAN = FAST_BOOT || CLI_ARGS.includes("--skip-scan");
|
|
74
|
+
var SKIP_RECOVERY = FAST_BOOT || CLI_ARGS.includes("--skip-recovery");
|
|
75
|
+
|
|
76
|
+
// src/concerns/helpers.ts
|
|
77
|
+
import { env as env2 } from "process";
|
|
78
|
+
import { parse as parseYaml } from "yaml";
|
|
79
|
+
function now() {
|
|
80
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
81
|
+
}
|
|
82
|
+
function isoWeek(date) {
|
|
83
|
+
const d = date ? new Date(date) : /* @__PURE__ */ new Date();
|
|
84
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
85
|
+
const tmp = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
|
|
86
|
+
const dayNum = tmp.getUTCDay() || 7;
|
|
87
|
+
tmp.setUTCDate(tmp.getUTCDate() + 4 - dayNum);
|
|
88
|
+
const yearStart = new Date(Date.UTC(tmp.getUTCFullYear(), 0, 1));
|
|
89
|
+
const weekNo = Math.ceil(((tmp.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
90
|
+
return `${tmp.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
|
|
91
|
+
}
|
|
92
|
+
function sleep(ms) {
|
|
93
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
94
|
+
}
|
|
95
|
+
function resolveEnvVar(value) {
|
|
96
|
+
if (!value.startsWith("$")) return value;
|
|
97
|
+
const varName = value.slice(1);
|
|
98
|
+
const resolved = env2[varName];
|
|
99
|
+
return resolved && resolved.trim().length > 0 ? resolved.trim() : "";
|
|
100
|
+
}
|
|
101
|
+
function toStringValue(value, fallback = "") {
|
|
102
|
+
if (typeof value !== "string" || value.trim().length === 0) return fallback;
|
|
103
|
+
const trimmed = value.trim();
|
|
104
|
+
if (trimmed.startsWith("$") && /^\$[A-Za-z_][A-Za-z0-9_]*$/.test(trimmed)) {
|
|
105
|
+
const resolved = resolveEnvVar(trimmed);
|
|
106
|
+
return resolved.length > 0 ? resolved : fallback;
|
|
107
|
+
}
|
|
108
|
+
return trimmed;
|
|
109
|
+
}
|
|
110
|
+
function toNumberValue(value, fallback = 1) {
|
|
111
|
+
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
112
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : fallback;
|
|
113
|
+
}
|
|
114
|
+
function toStringArray(value) {
|
|
115
|
+
if (!Array.isArray(value)) return [];
|
|
116
|
+
return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => entry.trim());
|
|
117
|
+
}
|
|
118
|
+
function clamp(value, min, max) {
|
|
119
|
+
return Math.min(Math.max(value, min), max);
|
|
120
|
+
}
|
|
121
|
+
function parseIssueState(value) {
|
|
122
|
+
const raw = typeof value === "string" ? value.trim() : "";
|
|
123
|
+
if (ALLOWED_STATES.includes(raw)) {
|
|
124
|
+
return raw;
|
|
125
|
+
}
|
|
126
|
+
return void 0;
|
|
127
|
+
}
|
|
128
|
+
function normalizeState(value, fallback = "Planning") {
|
|
129
|
+
return parseIssueState(value) ?? fallback;
|
|
130
|
+
}
|
|
131
|
+
function parseEnvNumber(name, fallback) {
|
|
132
|
+
return toNumberValue(env2[name], fallback);
|
|
133
|
+
}
|
|
134
|
+
function parseIntArg(value, fallback) {
|
|
135
|
+
const parsed = Number.parseInt(value, 10);
|
|
136
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
137
|
+
}
|
|
138
|
+
function parsePositiveIntEnv(name, fallback) {
|
|
139
|
+
const source = env2[name];
|
|
140
|
+
if (!source) return fallback;
|
|
141
|
+
return parseIntArg(source, fallback);
|
|
142
|
+
}
|
|
143
|
+
function withRetryBackoff(attempt, baseDelayMs) {
|
|
144
|
+
return Math.min(baseDelayMs * 2 ** attempt, 5 * 60 * 1e3);
|
|
145
|
+
}
|
|
146
|
+
function idToSafePath(value) {
|
|
147
|
+
return value.toLowerCase().replace(/[^a-z0-9._-]/g, "-");
|
|
148
|
+
}
|
|
149
|
+
function appendFileTail(target, text, maxLength) {
|
|
150
|
+
const merged = `${target}
|
|
151
|
+
${text}`;
|
|
152
|
+
if (merged.length <= maxLength) return merged;
|
|
153
|
+
return `\u2026${merged.slice(-(maxLength - 1))}`;
|
|
154
|
+
}
|
|
155
|
+
function debugBoot(message) {
|
|
156
|
+
if (!DEBUG_BOOT) return;
|
|
157
|
+
console.error(`[FIFONY_DEBUG_BOOT] ${message}`);
|
|
158
|
+
}
|
|
159
|
+
function fail(message) {
|
|
160
|
+
console.error(message);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
function extractJsonObjects(text) {
|
|
164
|
+
const results = [];
|
|
165
|
+
let depth = 0;
|
|
166
|
+
let start = -1;
|
|
167
|
+
let inStr = false;
|
|
168
|
+
let esc = false;
|
|
169
|
+
for (let i = 0; i < text.length; i++) {
|
|
170
|
+
const ch = text[i];
|
|
171
|
+
if (inStr) {
|
|
172
|
+
if (esc) {
|
|
173
|
+
esc = false;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (ch === "\\") {
|
|
177
|
+
esc = true;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (ch === '"') {
|
|
181
|
+
inStr = false;
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (ch === '"') {
|
|
186
|
+
inStr = true;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (ch === "{") {
|
|
190
|
+
if (depth === 0) start = i;
|
|
191
|
+
depth++;
|
|
192
|
+
} else if (ch === "}") {
|
|
193
|
+
depth = Math.max(0, depth - 1);
|
|
194
|
+
if (depth === 0 && start >= 0) {
|
|
195
|
+
results.push(text.slice(start, i + 1));
|
|
196
|
+
start = -1;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return results;
|
|
201
|
+
}
|
|
202
|
+
function repairTruncatedJson(text) {
|
|
203
|
+
const firstBrace = text.indexOf("{");
|
|
204
|
+
if (firstBrace < 0) return null;
|
|
205
|
+
let json = text.slice(firstBrace);
|
|
206
|
+
let inStr = false;
|
|
207
|
+
let esc = false;
|
|
208
|
+
const stack = [];
|
|
209
|
+
for (let i = 0; i < json.length; i++) {
|
|
210
|
+
const ch = json[i];
|
|
211
|
+
if (inStr) {
|
|
212
|
+
if (esc) {
|
|
213
|
+
esc = false;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (ch === "\\") {
|
|
217
|
+
esc = true;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (ch === '"') {
|
|
221
|
+
inStr = false;
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (ch === '"') {
|
|
226
|
+
inStr = true;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (ch === "{") stack.push("{");
|
|
230
|
+
else if (ch === "[") stack.push("[");
|
|
231
|
+
else if (ch === "}") {
|
|
232
|
+
if (stack.length > 0 && stack[stack.length - 1] === "{") stack.pop();
|
|
233
|
+
} else if (ch === "]") {
|
|
234
|
+
if (stack.length > 0 && stack[stack.length - 1] === "[") stack.pop();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (!inStr && stack.length === 0) return json;
|
|
238
|
+
if (inStr) {
|
|
239
|
+
if (json.endsWith("\\")) json = json.slice(0, -1);
|
|
240
|
+
json += '"';
|
|
241
|
+
}
|
|
242
|
+
json = json.replace(/[,:\s]+$/, "");
|
|
243
|
+
while (stack.length > 0) {
|
|
244
|
+
const open = stack.pop();
|
|
245
|
+
json += open === "{" ? "}" : "]";
|
|
246
|
+
}
|
|
247
|
+
return json;
|
|
248
|
+
}
|
|
249
|
+
|
|
1
250
|
// src/routing/capability-resolver.ts
|
|
2
251
|
function tokenize(issue) {
|
|
3
252
|
const labels = (issue.labels ?? []).filter((label) => !label.startsWith("capability:") && !label.startsWith("overlay:"));
|
|
@@ -286,43 +535,147 @@ function mergeCapabilityProviders(baseProviders, resolution) {
|
|
|
286
535
|
});
|
|
287
536
|
}
|
|
288
537
|
|
|
289
|
-
// src/prompting.ts
|
|
538
|
+
// src/agents/prompting.ts
|
|
290
539
|
import { TemplateEngine } from "recker";
|
|
291
540
|
|
|
292
|
-
// src/generated/prompts.ts
|
|
541
|
+
// src/agents/generated/prompts.ts
|
|
293
542
|
var PROMPT_TEMPLATES = {
|
|
294
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',
|
|
295
|
-
// src/prompts/agent-provider-base.stub.md
|
|
544
|
+
// src/agents/prompts/agent-provider-base.stub.md
|
|
296
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',
|
|
297
|
-
// src/prompts/agent-turn.stub.md
|
|
546
|
+
// src/agents/prompts/agent-turn.stub.md
|
|
298
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',
|
|
299
|
-
// src/prompts/compile-execution-claude.stub.md
|
|
548
|
+
// src/agents/prompts/compile-execution-claude.stub.md
|
|
300
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',
|
|
301
|
-
// src/prompts/compile-execution-codex.stub.md
|
|
550
|
+
// src/agents/prompts/compile-execution-codex.stub.md
|
|
302
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",
|
|
303
|
-
// src/prompts/compile-review.stub.md
|
|
552
|
+
// src/agents/prompts/compile-review.stub.md
|
|
304
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',
|
|
305
|
-
// src/prompts/integrations-agency-agents.stub.md
|
|
554
|
+
// src/agents/prompts/integrations-agency-agents.stub.md
|
|
306
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',
|
|
307
|
-
// src/prompts/integrations-impeccable.stub.md
|
|
308
|
-
"issue-enhancer-description":
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
556
|
+
// src/agents/prompts/integrations-impeccable.stub.md
|
|
557
|
+
"issue-enhancer-description": `You are helping improve issue metadata for a software execution queue.
|
|
558
|
+
Rewrite the description to be clearer, complete, and directly actionable.
|
|
559
|
+
Return strict JSON only with this schema:
|
|
560
|
+
{ "field": "description", "value": "..." }
|
|
561
|
+
|
|
562
|
+
Issue type: {{issueType}}
|
|
563
|
+
Current title: {{title}}
|
|
564
|
+
Current description: {{description}}
|
|
565
|
+
{{#if images}}
|
|
566
|
+
Visual evidence (attached screenshots for context):
|
|
567
|
+
{{#each images}}
|
|
568
|
+
- {{this}}
|
|
569
|
+
{{/each}}
|
|
570
|
+
{{/if}}
|
|
571
|
+
|
|
572
|
+
Rules:
|
|
573
|
+
- Keep it concise but include meaningful acceptance criteria tailored to the issue type.
|
|
574
|
+
- For "bug": focus on problem description, expected behavior, and steps to reproduce.
|
|
575
|
+
- For "feature": focus on goal, acceptance criteria, and any relevant notes.
|
|
576
|
+
- For "refactor": describe current state, desired state, and scope.
|
|
577
|
+
- For "docs": describe what to document and target audience.
|
|
578
|
+
- For "chore": describe the task and why it's needed now.
|
|
579
|
+
- Use markdown formatting appropriate for the type (## headings, bullet points).
|
|
580
|
+
- Avoid extra wrappers, outer quotes, or extra explanation.
|
|
581
|
+
- The value should be in Portuguese if the input is in Portuguese; otherwise in English.
|
|
582
|
+
`,
|
|
583
|
+
// src/agents/prompts/issue-enhancer-description.stub.md
|
|
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',
|
|
585
|
+
// src/agents/prompts/issue-enhancer-title.stub.md
|
|
586
|
+
"issue-planner": `You are a senior technical execution planner.
|
|
587
|
+
Produce the best possible plan for the issue below, filling the JSON schema precisely.
|
|
588
|
+
{{#if fast}}
|
|
589
|
+
|
|
590
|
+
FAST MODE: Be brief and direct. Minimize reasoning depth.
|
|
591
|
+
- 2-4 steps maximum. Skip optional fields (unknowns, risks, alternatives).
|
|
592
|
+
- No tooling reflection needed \u2014 set shouldUseSkills: false, shouldUseSubagents: false.
|
|
593
|
+
- Focus only on: summary, steps, estimatedComplexity, suggestedPaths, suggestedLabels.
|
|
594
|
+
{{/if}}
|
|
595
|
+
|
|
596
|
+
Issue title: {{title}}
|
|
597
|
+
Issue description: {{description}}
|
|
598
|
+
{{#if images}}
|
|
599
|
+
Visual evidence (attached screenshots for context):
|
|
600
|
+
{{#each images}}
|
|
601
|
+
- {{this}}
|
|
602
|
+
{{/each}}
|
|
603
|
+
{{/if}}
|
|
604
|
+
{{#unless fast}}
|
|
605
|
+
|
|
606
|
+
Quality rules:
|
|
607
|
+
- Be concrete, not generic. No vague phrases like 'implement' or 'improve' without detail.
|
|
608
|
+
- Break work into actionable steps (2-8 steps). Each step describes WHAT, not HOW.
|
|
609
|
+
- Each step must have a clear 'doneWhen' acceptance criterion.
|
|
610
|
+
- Identify assumptions, constraints, unknowns, and risks.
|
|
611
|
+
- For unknowns, specify what question needs answering and how to resolve it.
|
|
612
|
+
- Suggest file paths that are likely relevant to the changes.
|
|
613
|
+
- Suggest labels: bug, feature, frontend, backend, docs, refactor, security, performance, etc.
|
|
614
|
+
|
|
615
|
+
Complexity estimation:
|
|
616
|
+
- trivial: < 5 min, single-file cosmetic change
|
|
617
|
+
- low: 5-15 min, small focused change
|
|
618
|
+
- medium: 15-60 min, multi-file change with testing
|
|
619
|
+
- high: > 1 hour, architectural change or new feature
|
|
620
|
+
|
|
621
|
+
Tooling reflection (REQUIRED):
|
|
622
|
+
- Evaluate whether the task benefits from using skills (specialized instructions for quality/consistency).
|
|
623
|
+
- Evaluate whether subtasks should use subagents (parallel work, isolated context, specialization).
|
|
624
|
+
- Only recommend skills/agents when there is a concrete justification.
|
|
625
|
+
- For each step, set ownerType: 'agent' for automated work, 'human' for manual review, 'skill' for specialized skills, 'subagent' for delegated work.
|
|
626
|
+
|
|
627
|
+
Effort suggestion:
|
|
628
|
+
- low: simple fixes, no deep reasoning needed
|
|
629
|
+
- medium: standard development work
|
|
630
|
+
- high: complex architecture, security, or cross-cutting changes
|
|
631
|
+
- Set per-role if different: planner, executor, reviewer
|
|
632
|
+
{{/unless}}
|
|
633
|
+
|
|
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.
|
|
636
|
+
|
|
637
|
+
\`\`\`json
|
|
638
|
+
{
|
|
639
|
+
"summary": "<YOUR one-line summary here>",
|
|
640
|
+
"estimatedComplexity": "trivial|low|medium|high",
|
|
641
|
+
"steps": [
|
|
642
|
+
{
|
|
643
|
+
"step": 1,
|
|
644
|
+
"action": "<YOUR concrete action here>",
|
|
645
|
+
"files": ["<real/path/to/file.ts>"],
|
|
646
|
+
"details": "<YOUR additional context>",
|
|
647
|
+
"ownerType": "agent|human|skill|subagent|tool",
|
|
648
|
+
"doneWhen": "<YOUR acceptance criterion>"
|
|
649
|
+
}
|
|
650
|
+
],
|
|
651
|
+
"assumptions": ["<YOUR assumptions>"],
|
|
652
|
+
"constraints": ["<YOUR constraints>"],
|
|
653
|
+
"unknowns": [
|
|
654
|
+
{ "question": "<YOUR question>", "whyItMatters": "<YOUR reason>", "howToResolve": "<YOUR approach>" }
|
|
655
|
+
],
|
|
656
|
+
"successCriteria": ["<YOUR criteria>"],
|
|
657
|
+
"risks": [
|
|
658
|
+
{ "risk": "<YOUR risk>", "impact": "<YOUR impact>", "mitigation": "<YOUR mitigation>" }
|
|
659
|
+
],
|
|
660
|
+
"suggestedPaths": ["<real/path/to/relevant/file.ts>"],
|
|
661
|
+
"suggestedLabels": ["frontend", "bug", "feature"],
|
|
662
|
+
"suggestedEffort": { "default": "medium", "planner": "low", "executor": "medium", "reviewer": "medium" }
|
|
663
|
+
}
|
|
664
|
+
\`\`\`
|
|
665
|
+
`,
|
|
666
|
+
// src/agents/prompts/issue-planner.stub.md
|
|
314
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",
|
|
315
|
-
// src/prompts/issue-planner-refine.stub.md
|
|
668
|
+
// src/agents/prompts/issue-planner-refine.stub.md
|
|
316
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",
|
|
317
|
-
// src/prompts/mcp-integrate-client.stub.md
|
|
670
|
+
// src/agents/prompts/mcp-integrate-client.stub.md
|
|
318
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',
|
|
319
|
-
// src/prompts/mcp-integration-guide.stub.md
|
|
320
|
-
"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-
|
|
321
|
-
// src/prompts/mcp-issue.stub.md
|
|
322
|
-
"mcp-review-workflow": "Review the
|
|
323
|
-
// src/prompts/mcp-review-workflow.stub.md
|
|
672
|
+
// src/agents/prompts/mcp-integration-guide.stub.md
|
|
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',
|
|
674
|
+
// src/agents/prompts/mcp-issue.stub.md
|
|
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",
|
|
676
|
+
// src/agents/prompts/mcp-review-workflow.stub.md
|
|
324
677
|
"mcp-route-task": "Use this routing decision as the execution plan for the task.\n\n{{resolutionJson}}\n",
|
|
325
|
-
// src/prompts/mcp-route-task.stub.md
|
|
678
|
+
// src/agents/prompts/mcp-route-task.stub.md
|
|
326
679
|
"project-analysis": `You are analyzing a software project to help configure an AI-powered development assistant.
|
|
327
680
|
|
|
328
681
|
Look at the project structure, source code, configuration files, and any documentation you can find. Pay special attention to:
|
|
@@ -348,14 +701,14 @@ For "suggestedAgents", choose from: frontend-developer, backend-architect, datab
|
|
|
348
701
|
|
|
349
702
|
Return ONLY the JSON object. No markdown fences, no explanation, no extra text.
|
|
350
703
|
`,
|
|
351
|
-
// src/prompts/project-analysis.stub.md
|
|
704
|
+
// src/agents/prompts/project-analysis.stub.md
|
|
352
705
|
"workflow-default": "You are working on {{issue.identifier}}.\n\nTitle: {{issue.title}}\nDescription:\n{{issue.description}}\n",
|
|
353
|
-
// src/prompts/workflow-default.stub.md
|
|
706
|
+
// src/agents/prompts/workflow-default.stub.md
|
|
354
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'
|
|
355
|
-
// src/prompts/workflow-plan-section.stub.md
|
|
708
|
+
// src/agents/prompts/workflow-plan-section.stub.md
|
|
356
709
|
};
|
|
357
710
|
|
|
358
|
-
// src/prompting.ts
|
|
711
|
+
// src/agents/prompting.ts
|
|
359
712
|
var engine = new TemplateEngine({
|
|
360
713
|
cache: true,
|
|
361
714
|
format: "raw",
|
|
@@ -373,11 +726,57 @@ async function renderPromptString(template, context = {}) {
|
|
|
373
726
|
}
|
|
374
727
|
|
|
375
728
|
export {
|
|
729
|
+
PACKAGE_ROOT,
|
|
730
|
+
CLI_ARGS,
|
|
731
|
+
TARGET_ROOT,
|
|
732
|
+
STATE_ROOT,
|
|
733
|
+
SOURCE_ROOT,
|
|
734
|
+
WORKSPACE_ROOT,
|
|
735
|
+
SOURCE_MARKER,
|
|
736
|
+
ATTACHMENTS_ROOT,
|
|
737
|
+
S3DB_DATABASE_PATH,
|
|
738
|
+
S3DB_RUNTIME_RESOURCE,
|
|
739
|
+
S3DB_ISSUE_RESOURCE,
|
|
740
|
+
S3DB_ISSUE_PLAN_RESOURCE,
|
|
741
|
+
S3DB_EVENT_RESOURCE,
|
|
742
|
+
S3DB_SETTINGS_RESOURCE,
|
|
743
|
+
S3DB_AGENT_SESSION_RESOURCE,
|
|
744
|
+
S3DB_AGENT_PIPELINE_RESOURCE,
|
|
745
|
+
S3DB_RUNTIME_RECORD_ID,
|
|
746
|
+
S3DB_RUNTIME_SCHEMA_VERSION,
|
|
747
|
+
FRONTEND_DIR,
|
|
748
|
+
FRONTEND_INDEX,
|
|
749
|
+
FRONTEND_MANIFEST_JSON,
|
|
750
|
+
FRONTEND_SERVICE_WORKER_JS,
|
|
751
|
+
FRONTEND_ICON_SVG,
|
|
752
|
+
FRONTEND_MASKABLE_ICON_SVG,
|
|
753
|
+
FRONTEND_OFFLINE_HTML,
|
|
754
|
+
ALLOWED_STATES,
|
|
755
|
+
TERMINAL_STATES,
|
|
756
|
+
EXECUTING_STATES,
|
|
757
|
+
PERSIST_EVENTS_MAX,
|
|
758
|
+
now,
|
|
759
|
+
isoWeek,
|
|
760
|
+
sleep,
|
|
761
|
+
toStringValue,
|
|
762
|
+
toNumberValue,
|
|
763
|
+
toStringArray,
|
|
764
|
+
clamp,
|
|
765
|
+
parseIssueState,
|
|
766
|
+
normalizeState,
|
|
767
|
+
parseEnvNumber,
|
|
768
|
+
parseIntArg,
|
|
769
|
+
parsePositiveIntEnv,
|
|
770
|
+
withRetryBackoff,
|
|
771
|
+
idToSafePath,
|
|
772
|
+
appendFileTail,
|
|
773
|
+
debugBoot,
|
|
774
|
+
fail,
|
|
775
|
+
extractJsonObjects,
|
|
776
|
+
repairTruncatedJson,
|
|
376
777
|
inferCapabilityPaths,
|
|
377
778
|
resolveTaskCapabilities,
|
|
378
779
|
mergeCapabilityProviders,
|
|
379
|
-
|
|
380
|
-
renderPrompt,
|
|
381
|
-
renderPromptString
|
|
780
|
+
renderPrompt
|
|
382
781
|
};
|
|
383
|
-
//# sourceMappingURL=chunk-
|
|
782
|
+
//# sourceMappingURL=chunk-DD5BE2W6.js.map
|