fifony 0.1.39 → 0.1.40

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.
Files changed (27) hide show
  1. package/app/dist/assets/{CommandPalette--xFIYira.js → CommandPalette-dMSFpGLm.js} +1 -1
  2. package/app/dist/assets/{KeyboardShortcutsHelp-BtrclGbX.js → KeyboardShortcutsHelp-CH5aYlDe.js} +1 -1
  3. package/app/dist/assets/{OnboardingWizard-AdxAh1n0.js → OnboardingWizard-CdJHsRny.js} +1 -1
  4. package/app/dist/assets/{analytics.lazy-DaMIOiTI.js → analytics.lazy-CKu4136R.js} +1 -1
  5. package/app/dist/assets/{index-BWB0OQnx.css → index-BatA8x-K.css} +1 -1
  6. package/app/dist/assets/index-C13WwYFD.js +54 -0
  7. package/app/dist/index.html +2 -2
  8. package/app/dist/service-worker.js +1 -1
  9. package/dist/agent/run-local.js +5 -5
  10. package/dist/{agent-AAT7OHAL.js → agent-KDPOZCI5.js} +6 -6
  11. package/dist/{chunk-AVSRIV47.js → chunk-3QL4QAQ5.js} +22 -14
  12. package/dist/{chunk-T2YJOZ6N.js → chunk-EPY5TTQK.js} +6 -29
  13. package/dist/{chunk-DWMY2HBG.js → chunk-LYAI5RPK.js} +428 -145
  14. package/dist/{chunk-UNYIR5AK.js → chunk-O3FGX4J6.js} +29 -13
  15. package/dist/{chunk-WBOBY75G.js → chunk-UXXUTDGV.js} +3 -3
  16. package/dist/cli.js +5 -5
  17. package/dist/issue-runner-YZM6WQMY.js +15 -0
  18. package/dist/{issue-state-machine-TEIICCAA.js → issue-state-machine-KOZE5JWX.js} +4 -4
  19. package/dist/{issues-JPMKO2EE.js → issues-7HQC7OIN.js} +6 -6
  20. package/dist/mcp/server.js +1 -1
  21. package/dist/{queue-workers-5JOCROD6.js → queue-workers-ZEZHDX7M.js} +2 -2
  22. package/dist/{scheduler-GAO2MXGZ.js → scheduler-4R4ZAF25.js} +6 -6
  23. package/dist/{store-3YSID6N2.js → store-FNUWCFOX.js} +6 -6
  24. package/dist/{workspace-ZD5H6YOL.js → workspace-AOHHNWL5.js} +3 -3
  25. package/package.json +1 -1
  26. package/app/dist/assets/index-D5rsr1We.js +0 -54
  27. package/dist/issue-runner-JSHZGTKQ.js +0 -15
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  enqueue
3
- } from "./chunk-AVSRIV47.js";
3
+ } from "./chunk-3QL4QAQ5.js";
4
4
  import {
5
5
  ISSUE_STATE_MACHINE_ID,
6
6
  computeMetrics,
@@ -8,6 +8,7 @@ import {
8
8
  findIssueStateMachineTransitionPath,
9
9
  getDirtyEventIds,
10
10
  getDirtyIssueIds,
11
+ getIssueResourceStateApi,
11
12
  getIssueStateMachinePlugin,
12
13
  getMetrics,
13
14
  hasDirtyState,
@@ -24,7 +25,7 @@ import {
24
25
  snapshotAndClearDirtyEventIds,
25
26
  snapshotAndClearDirtyIssueIds,
26
27
  snapshotAndClearDirtyIssuePlanIds
27
- } from "./chunk-WBOBY75G.js";
28
+ } from "./chunk-UXXUTDGV.js";
28
29
  import {
29
30
  ADAPTERS,
30
31
  assertIssueHasGitWorktree,
@@ -52,9 +53,10 @@ import {
52
53
  parseDiffStats,
53
54
  prepareWorkspace,
54
55
  readCodexConfig,
56
+ rebaseWorktree,
55
57
  runCommandWithTimeout,
56
58
  runHook
57
- } from "./chunk-UNYIR5AK.js";
59
+ } from "./chunk-O3FGX4J6.js";
58
60
  import {
59
61
  logger
60
62
  } from "./chunk-DVU3CXWA.js";
@@ -79,7 +81,7 @@ import {
79
81
  toStringArray,
80
82
  toStringValue,
81
83
  withRetryBackoff
82
- } from "./chunk-T2YJOZ6N.js";
84
+ } from "./chunk-EPY5TTQK.js";
83
85
  import {
84
86
  ATTACHMENTS_ROOT,
85
87
  COMPLETED_STATES,
@@ -289,10 +291,10 @@ import {
289
291
  mkdirSync as mkdirSync8,
290
292
  readdirSync as readdirSync4,
291
293
  readFileSync as readFileSync11,
292
- writeFileSync as writeFileSync12
294
+ writeFileSync as writeFileSync13
293
295
  } from "fs";
294
296
  import { homedir as homedir3 } from "os";
295
- import { basename as basename4, dirname as dirname2, join as join17, relative as relativePath, resolve as resolve2 } from "path";
297
+ import { basename as basename4, dirname as dirname2, join as join18, relative as relativePath, resolve as resolve2 } from "path";
296
298
 
297
299
  // src/persistence/plugins/api-runtime-context.ts
298
300
  var context = null;
@@ -494,6 +496,19 @@ function tryParseJsonOutput(output) {
494
496
  }
495
497
  return null;
496
498
  }
499
+ function extractUsageArrays(obj) {
500
+ if (!obj) return {};
501
+ const toArr = (v) => {
502
+ if (!Array.isArray(v) || v.length === 0) return void 0;
503
+ return v.filter((s) => typeof s === "string" && s.length > 0);
504
+ };
505
+ return {
506
+ toolsUsed: toArr(obj.tools_used ?? obj.toolsUsed),
507
+ skillsUsed: toArr(obj.skills_used ?? obj.skillsUsed),
508
+ agentsUsed: toArr(obj.agents_used ?? obj.agentsUsed),
509
+ commandsRun: toArr(obj.commands_run ?? obj.commandsRun)
510
+ };
511
+ }
497
512
  function readAgentDirective(workspacePath, output, success) {
498
513
  const fallbackStatus = success ? "done" : "failed";
499
514
  const resultFile = join(workspacePath, "result.json");
@@ -512,9 +527,26 @@ function readAgentDirective(workspacePath, output, success) {
512
527
  status: normalizeAgentDirectiveStatus(jsonOutput.status, fallbackStatus),
513
528
  summary: toStringValue(jsonOutput.summary) || toStringValue(jsonOutput.message) || "",
514
529
  nextPrompt: toStringValue(jsonOutput.nextPrompt) || toStringValue(jsonOutput.next_prompt) || "",
515
- tokenUsage
530
+ tokenUsage,
531
+ ...extractUsageArrays(jsonOutput)
516
532
  };
517
533
  }
534
+ const codeBlocks = [...output.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi)].map((m) => m[1].trim()).reverse();
535
+ for (const block of codeBlocks) {
536
+ try {
537
+ const parsed = JSON.parse(block);
538
+ if (parsed?.status) {
539
+ return {
540
+ status: normalizeAgentDirectiveStatus(parsed.status, fallbackStatus),
541
+ summary: toStringValue(parsed.summary) || toStringValue(parsed.message) || "",
542
+ nextPrompt: toStringValue(parsed.nextPrompt) || toStringValue(parsed.next_prompt) || "",
543
+ tokenUsage,
544
+ ...extractUsageArrays(parsed)
545
+ };
546
+ }
547
+ } catch {
548
+ }
549
+ }
518
550
  if (existsSync(resultFile)) {
519
551
  try {
520
552
  const parsed = JSON.parse(readFileSync(resultFile, "utf8"));
@@ -531,7 +563,7 @@ function readAgentDirective(workspacePath, output, success) {
531
563
  );
532
564
  const summary = toStringValue(resultPayload.summary) || toStringValue(resultPayload.message) || extractOutputMarker(output, "FIFONY_SUMMARY");
533
565
  const nextPrompt = toStringValue(resultPayload.nextPrompt) || toStringValue(resultPayload.next_prompt) || "";
534
- return { status, summary, nextPrompt, tokenUsage };
566
+ return { status, summary, nextPrompt, tokenUsage, ...extractUsageArrays(resultPayload) };
535
567
  }
536
568
 
537
569
  // src/agents/pid-manager.ts
@@ -1348,6 +1380,10 @@ ${previousOutput.slice(-maxOutputChars)}` : previousOutput;
1348
1380
  }
1349
1381
  addEvent(state, issue.id, "info", parts.join(" "));
1350
1382
  }
1383
+ if (directive.toolsUsed?.length) issue.toolsUsed = [.../* @__PURE__ */ new Set([...issue.toolsUsed ?? [], ...directive.toolsUsed])];
1384
+ if (directive.skillsUsed?.length) issue.skillsUsed = [.../* @__PURE__ */ new Set([...issue.skillsUsed ?? [], ...directive.skillsUsed])];
1385
+ if (directive.agentsUsed?.length) issue.agentsUsed = [.../* @__PURE__ */ new Set([...issue.agentsUsed ?? [], ...directive.agentsUsed])];
1386
+ if (directive.commandsRun?.length) issue.commandsRun = [.../* @__PURE__ */ new Set([...issue.commandsRun ?? [], ...directive.commandsRun])];
1351
1387
  session.turns.push({
1352
1388
  turn: turnIndex,
1353
1389
  role: provider.role,
@@ -1362,7 +1398,11 @@ ${previousOutput.slice(-maxOutputChars)}` : previousOutput;
1362
1398
  directiveStatus: directive.status,
1363
1399
  directiveSummary: directive.summary,
1364
1400
  nextPrompt: directive.nextPrompt,
1365
- tokenUsage: directive.tokenUsage
1401
+ tokenUsage: directive.tokenUsage,
1402
+ toolsUsed: directive.toolsUsed,
1403
+ skillsUsed: directive.skillsUsed,
1404
+ agentsUsed: directive.agentsUsed,
1405
+ commandsRun: directive.commandsRun
1366
1406
  });
1367
1407
  session.lastCode = lastCode;
1368
1408
  session.lastOutput = lastOutput;
@@ -2453,7 +2493,7 @@ function generatePlanInBackground(issue, config, _workflowDefinition, callbacks,
2453
2493
  applyUsage(issue, usage);
2454
2494
  applySuggestions(issue, plan);
2455
2495
  try {
2456
- const { savePlanForIssue: savePlanForIssue2 } = await import("./store-3YSID6N2.js");
2496
+ const { savePlanForIssue: savePlanForIssue2 } = await import("./store-FNUWCFOX.js");
2457
2497
  await savePlanForIssue2(issue.id, plan, issue.planVersion);
2458
2498
  } catch {
2459
2499
  }
@@ -2490,7 +2530,7 @@ function refinePlanInBackground(issue, feedback, config, _workflowDefinition, ca
2490
2530
  applyUsage(issue, usage);
2491
2531
  applySuggestions(issue, plan);
2492
2532
  try {
2493
- const { savePlanForIssue: savePlanForIssue2 } = await import("./store-3YSID6N2.js");
2533
+ const { savePlanForIssue: savePlanForIssue2 } = await import("./store-FNUWCFOX.js");
2494
2534
  await savePlanForIssue2(issue.id, plan, issue.planVersion);
2495
2535
  } catch {
2496
2536
  }
@@ -2768,7 +2808,7 @@ async function runPlanningJob(state, issue) {
2768
2808
  issue.plan = plan;
2769
2809
  issue.planVersion = Math.max(issue.planVersion ?? 0, 1);
2770
2810
  try {
2771
- const { savePlanForIssue: savePlanForIssue2 } = await import("./store-3YSID6N2.js");
2811
+ const { savePlanForIssue: savePlanForIssue2 } = await import("./store-FNUWCFOX.js");
2772
2812
  await savePlanForIssue2(issue.id, plan, issue.planVersion);
2773
2813
  logger.debug({ issueId: issue.id, planVersion: issue.planVersion }, "[Agent] Plan saved to issue_plans resource");
2774
2814
  } catch (err) {
@@ -3004,7 +3044,7 @@ async function runIssueOnce(state, issue, running) {
3004
3044
  const { workspacePath, promptText, promptFile } = await prepareWorkspace(issue, state, state.config.defaultBranch);
3005
3045
  container.issueRepository.markDirty(issue.id);
3006
3046
  try {
3007
- const { getIssueStateResource: getIssueStateResource2 } = await import("./store-3YSID6N2.js");
3047
+ const { getIssueStateResource: getIssueStateResource2 } = await import("./store-FNUWCFOX.js");
3008
3048
  const res = getIssueStateResource2();
3009
3049
  if (res) {
3010
3050
  await res.patch(issue.id, {
@@ -3043,13 +3083,6 @@ async function runIssueOnce(state, issue, running) {
3043
3083
  state.metrics = computeMetrics(state.issues);
3044
3084
  state.updatedAt = now();
3045
3085
  await container.persistencePort.persistState(state);
3046
- if (issue.state === "Reviewing") {
3047
- try {
3048
- const { enqueue: enqueue2 } = await import("./queue-workers-5JOCROD6.js");
3049
- await enqueue2(issue, "review");
3050
- } catch {
3051
- }
3052
- }
3053
3086
  }
3054
3087
  }
3055
3088
 
@@ -3134,9 +3167,122 @@ async function cancelIssueCommand(input, deps) {
3134
3167
  deps.eventStore.addEvent(issue.id, "manual", `Manual cancel requested for ${issue.id}.`);
3135
3168
  }
3136
3169
 
3170
+ // src/commands/delete-issue.command.ts
3171
+ async function deleteIssueCommand(input) {
3172
+ const { issue, state } = input;
3173
+ const pidInfo = issue.workspacePath ? readAgentPid(issue.workspacePath) : null;
3174
+ if (pidInfo) {
3175
+ try {
3176
+ process.kill(-pidInfo.pid, "SIGTERM");
3177
+ logger.info({ pid: pidInfo.pid, issueId: issue.id }, "[Delete] Sent SIGTERM to agent process group");
3178
+ } catch {
3179
+ try {
3180
+ process.kill(pidInfo.pid, "SIGTERM");
3181
+ } catch {
3182
+ }
3183
+ }
3184
+ }
3185
+ try {
3186
+ await cleanWorkspace(issue.id, issue, state);
3187
+ } catch (error) {
3188
+ logger.warn({ issueId: issue.id, err: String(error) }, "[Delete] Workspace cleanup failed (continuing)");
3189
+ }
3190
+ const idx = state.issues.findIndex((i) => i.id === issue.id);
3191
+ if (idx >= 0) state.issues.splice(idx, 1);
3192
+ state.events = state.events.filter((e) => e.issueId !== issue.id);
3193
+ try {
3194
+ const fsmApi = getIssueResourceStateApi();
3195
+ if (fsmApi?.delete) await fsmApi.delete(issue.id);
3196
+ } catch (error) {
3197
+ logger.debug({ issueId: issue.id, err: String(error) }, "[Delete] FSM state cleanup (non-critical)");
3198
+ }
3199
+ try {
3200
+ const resource = getIssueStateResource();
3201
+ if (resource && typeof resource.delete === "function") {
3202
+ await resource.delete(issue.id);
3203
+ }
3204
+ } catch (error) {
3205
+ logger.debug({ issueId: issue.id, err: String(error) }, "[Delete] s3db record cleanup (non-critical)");
3206
+ }
3207
+ logger.info({ issueId: issue.id, identifier: issue.identifier }, "[Delete] Issue deleted");
3208
+ }
3209
+
3137
3210
  // src/commands/merge-workspace.command.ts
3138
3211
  import { existsSync as existsSync7 } from "fs";
3212
+ import { execSync as execSync3 } from "child_process";
3213
+
3214
+ // src/domains/merge-conflict-resolver.ts
3139
3215
  import { execSync as execSync2 } from "child_process";
3216
+ import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync8, rmSync as rmSync4 } from "fs";
3217
+ import { join as join12 } from "path";
3218
+ import { tmpdir as tmpdir3 } from "os";
3219
+ async function resolveConflictsWithAgent(options) {
3220
+ const { issue, conflictFiles, provider, model, targetRoot } = options;
3221
+ const startMs = Date.now();
3222
+ const adapter = ADAPTERS[provider];
3223
+ if (!adapter) {
3224
+ return { resolved: false, resolvedFiles: [], output: `No adapter for provider "${provider}"`, provider, durationMs: 0, resolvedAt: now() };
3225
+ }
3226
+ const command = adapter.buildCommand({ model });
3227
+ if (!command) {
3228
+ return { resolved: false, resolvedFiles: [], output: `Adapter returned empty command for "${provider}"`, provider, durationMs: 0, resolvedAt: now() };
3229
+ }
3230
+ const prompt = await renderPrompt("merge-conflict-resolver", {
3231
+ issueIdentifier: issue.identifier,
3232
+ title: issue.title,
3233
+ description: issue.description || "",
3234
+ baseBranch: issue.baseBranch || "main",
3235
+ featureBranch: issue.branchName || "unknown",
3236
+ conflictFiles
3237
+ });
3238
+ const tempDir = mkdtempSync3(join12(tmpdir3(), "fifony-conflict-"));
3239
+ const promptFile = join12(tempDir, "fifony-conflict-prompt.md");
3240
+ writeFileSync8(promptFile, `${prompt}
3241
+ `, "utf8");
3242
+ let output = "";
3243
+ try {
3244
+ output = await runPlanningProcess({
3245
+ command,
3246
+ tempDir: targetRoot,
3247
+ // agent runs in TARGET_ROOT where conflict markers exist
3248
+ promptFile,
3249
+ provider
3250
+ });
3251
+ } catch (err) {
3252
+ output = String(err);
3253
+ logger.error({ err, issueId: issue.id }, "[ConflictResolver] Agent process failed");
3254
+ } finally {
3255
+ rmSync4(tempDir, { recursive: true, force: true });
3256
+ }
3257
+ let remainingConflicts = [];
3258
+ try {
3259
+ const unmerged = execSync2("git diff --name-only --diff-filter=U", { cwd: targetRoot, encoding: "utf8" }).trim();
3260
+ remainingConflicts = unmerged ? unmerged.split("\n").filter(Boolean) : [];
3261
+ } catch {
3262
+ }
3263
+ const resolved = remainingConflicts.length === 0;
3264
+ const resolvedFiles = resolved ? conflictFiles : conflictFiles.filter((f) => !remainingConflicts.includes(f));
3265
+ const durationMs = Date.now() - startMs;
3266
+ logger.info({
3267
+ issueId: issue.id,
3268
+ resolved,
3269
+ resolvedFiles,
3270
+ remainingConflicts,
3271
+ durationMs,
3272
+ provider
3273
+ }, "[ConflictResolver] Resolution attempt complete");
3274
+ return {
3275
+ resolved,
3276
+ resolvedFiles,
3277
+ output: output.slice(-2e3),
3278
+ // tail for debugging
3279
+ provider,
3280
+ durationMs,
3281
+ resolvedAt: now()
3282
+ };
3283
+ }
3284
+
3285
+ // src/commands/merge-workspace.command.ts
3140
3286
  async function mergeWorkspaceCommand(input, deps) {
3141
3287
  const { issue, state, squashAlreadyApplied } = input;
3142
3288
  if (!["Approved", "Reviewing", "PendingDecision"].includes(issue.state)) {
@@ -3155,7 +3301,7 @@ async function mergeWorkspaceCommand(input, deps) {
3155
3301
  }
3156
3302
  if (issue.branchName && issue.baseBranch) {
3157
3303
  try {
3158
- const stat = execSync2(
3304
+ const stat = execSync3(
3159
3305
  `git diff --stat "${issue.baseBranch}"..."${issue.branchName}"`,
3160
3306
  { encoding: "utf8", cwd: TARGET_ROOT, stdio: "pipe", maxBuffer: 512e3, timeout: 1e4 }
3161
3307
  );
@@ -3172,11 +3318,28 @@ async function mergeWorkspaceCommand(input, deps) {
3172
3318
  throw new Error(`Validation gate failed (${validation.command}): ${validation.output.slice(0, 500)}`);
3173
3319
  }
3174
3320
  }
3321
+ if (!squashAlreadyApplied && issue.worktreePath && issue.baseBranch) {
3322
+ try {
3323
+ const rebase = rebaseWorktree(issue);
3324
+ issue.rebaseResult = { success: rebase.success, conflictFiles: rebase.conflictFiles, rebasedAt: now() };
3325
+ deps.issueRepository.markDirty(issue.id);
3326
+ if (rebase.success) {
3327
+ deps.eventStore.addEvent(issue.id, "info", `Auto-rebase onto ${issue.baseBranch} succeeded \u2014 branch is up to date.`);
3328
+ logger.info({ issueId: issue.id, baseBranch: issue.baseBranch }, "[Merge] Auto-rebase succeeded");
3329
+ } else {
3330
+ const files = rebase.conflictFiles.join(", ");
3331
+ deps.eventStore.addEvent(issue.id, "error", `Auto-rebase onto ${issue.baseBranch} failed \u2014 ${rebase.conflictFiles.length} conflict(s): ${files}. Proceeding with direct merge attempt.`);
3332
+ logger.warn({ issueId: issue.id, conflictFiles: rebase.conflictFiles }, "[Merge] Auto-rebase failed, will attempt direct merge");
3333
+ }
3334
+ } catch (err) {
3335
+ logger.warn({ issueId: issue.id, err: String(err) }, "[Merge] Auto-rebase threw unexpectedly, skipping");
3336
+ }
3337
+ }
3175
3338
  let result;
3176
3339
  if (squashAlreadyApplied) {
3177
3340
  try {
3178
- execSync2("git add -A", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 1e4 });
3179
- execSync2(
3341
+ execSync3("git add -A", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 1e4 });
3342
+ execSync3(
3180
3343
  `git commit -m "fifony: merge ${issue.identifier}"`,
3181
3344
  { cwd: TARGET_ROOT, stdio: "pipe", timeout: 1e4 }
3182
3345
  );
@@ -3188,26 +3351,89 @@ async function mergeWorkspaceCommand(input, deps) {
3188
3351
  result = { copied: [], deleted: [], skipped: [], conflicts: [] };
3189
3352
  } else {
3190
3353
  try {
3191
- const indexStatus = execSync2("git diff --cached --name-only", { cwd: TARGET_ROOT, encoding: "utf8", stdio: "pipe" }).trim();
3192
- const wtStatus = execSync2("git diff --name-only", { cwd: TARGET_ROOT, encoding: "utf8", stdio: "pipe" }).trim();
3354
+ const indexStatus = execSync3("git diff --cached --name-only", { cwd: TARGET_ROOT, encoding: "utf8", stdio: "pipe" }).trim();
3355
+ const wtStatus = execSync3("git diff --name-only", { cwd: TARGET_ROOT, encoding: "utf8", stdio: "pipe" }).trim();
3193
3356
  if (indexStatus && !wtStatus) {
3194
- execSync2("git reset --hard HEAD", { cwd: TARGET_ROOT, stdio: "pipe" });
3357
+ execSync3("git reset --hard HEAD", { cwd: TARGET_ROOT, stdio: "pipe" });
3195
3358
  logger.info({ issueId: issue.id }, "[Command] Cleared residual squash from index before merge");
3196
3359
  }
3197
3360
  } catch {
3198
3361
  }
3199
- const mergeResult = mergeWorkspace(issue);
3362
+ const mergeResult = mergeWorkspace(
3363
+ issue,
3364
+ /* abortOnConflict */
3365
+ false
3366
+ );
3200
3367
  result = mergeResult;
3368
+ if (result.conflicts.length > 0) {
3369
+ deps.eventStore.addEvent(issue.id, "info", `Merge conflicts in ${result.conflicts.length} file(s): ${result.conflicts.join(", ")}. Attempting agent-based resolution...`);
3370
+ logger.info({ issueId: issue.id, conflicts: result.conflicts }, "[Merge] Conflicts detected \u2014 spawning agent to resolve");
3371
+ try {
3372
+ const { provider, model } = await resolvePlanStageConfig(state.config);
3373
+ const resolution = await resolveConflictsWithAgent({
3374
+ issue,
3375
+ conflictFiles: result.conflicts,
3376
+ provider,
3377
+ model,
3378
+ targetRoot: TARGET_ROOT
3379
+ });
3380
+ if (resolution.resolved) {
3381
+ try {
3382
+ execSync3("git add -A", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 1e4 });
3383
+ execSync3(
3384
+ `git commit --no-edit`,
3385
+ { cwd: TARGET_ROOT, stdio: "pipe", timeout: 1e4 }
3386
+ );
3387
+ result.conflicts = [];
3388
+ deps.eventStore.addEvent(issue.id, "info", `Agent (${resolution.provider}) resolved ${resolution.resolvedFiles.length} conflict(s) in ${Math.round(resolution.durationMs / 1e3)}s.`);
3389
+ logger.info({ issueId: issue.id, provider: resolution.provider, durationMs: resolution.durationMs }, "[Merge] Agent resolved all conflicts \u2014 merge committed");
3390
+ } catch (commitErr) {
3391
+ try {
3392
+ execSync3("git merge --abort", { cwd: TARGET_ROOT, stdio: "pipe" });
3393
+ } catch {
3394
+ }
3395
+ deps.eventStore.addEvent(issue.id, "error", `Agent resolved conflicts but merge commit failed: ${String(commitErr)}`);
3396
+ logger.error({ issueId: issue.id, err: String(commitErr) }, "[Merge] Commit after conflict resolution failed");
3397
+ }
3398
+ } else {
3399
+ try {
3400
+ execSync3("git merge --abort", { cwd: TARGET_ROOT, stdio: "pipe" });
3401
+ } catch {
3402
+ }
3403
+ deps.eventStore.addEvent(issue.id, "error", `Agent-based conflict resolution failed (${resolution.provider}, ${Math.round(resolution.durationMs / 1e3)}s). ${resolution.resolvedFiles.length}/${result.conflicts.length} files resolved.`);
3404
+ logger.warn({ issueId: issue.id, resolvedFiles: resolution.resolvedFiles, provider: resolution.provider }, "[Merge] Agent failed to resolve all conflicts");
3405
+ }
3406
+ issue.mergeResult = {
3407
+ ...issue.mergeResult,
3408
+ conflictResolution: {
3409
+ resolved: resolution.resolved,
3410
+ provider: resolution.provider,
3411
+ resolvedFiles: resolution.resolvedFiles,
3412
+ durationMs: resolution.durationMs,
3413
+ output: resolution.output.slice(-500),
3414
+ resolvedAt: resolution.resolvedAt
3415
+ }
3416
+ };
3417
+ } catch (err) {
3418
+ try {
3419
+ execSync3("git merge --abort", { cwd: TARGET_ROOT, stdio: "pipe" });
3420
+ } catch {
3421
+ }
3422
+ deps.eventStore.addEvent(issue.id, "error", `Agent conflict resolution threw: ${String(err)}`);
3423
+ logger.error({ issueId: issue.id, err: String(err) }, "[Merge] Conflict resolution threw unexpectedly");
3424
+ }
3425
+ }
3201
3426
  }
3202
3427
  issue.mergeResult = {
3203
3428
  copied: result.copied.length,
3204
3429
  deleted: result.deleted.length,
3205
3430
  skipped: result.skipped.length,
3206
3431
  conflicts: result.conflicts.length,
3207
- conflictFiles: result.conflicts.length > 0 ? result.conflicts : void 0
3432
+ conflictFiles: result.conflicts.length > 0 ? result.conflicts : void 0,
3433
+ ...issue.mergeResult?.conflictResolution ? { conflictResolution: issue.mergeResult.conflictResolution } : {}
3208
3434
  };
3209
3435
  if (result.conflicts.length > 0) {
3210
- deps.eventStore.addEvent(issue.id, "error", `Merge conflicts: ${result.conflicts.join(", ")}`);
3436
+ deps.eventStore.addEvent(issue.id, "error", `Merge aborted \u2014 ${result.conflicts.length} conflict(s) remain: ${result.conflicts.join(", ")}`);
3211
3437
  await deps.persistencePort.persistState(state);
3212
3438
  return result;
3213
3439
  }
@@ -3229,7 +3455,7 @@ async function mergeWorkspaceCommand(input, deps) {
3229
3455
  }
3230
3456
 
3231
3457
  // src/commands/push-workspace.command.ts
3232
- import { execFileSync, execSync as execSync3 } from "child_process";
3458
+ import { execFileSync, execSync as execSync4 } from "child_process";
3233
3459
  function isGhAvailable() {
3234
3460
  try {
3235
3461
  execFileSync("gh", ["--version"], { stdio: "pipe", timeout: 5e3 });
@@ -3240,7 +3466,7 @@ function isGhAvailable() {
3240
3466
  }
3241
3467
  function getCompareUrl(branchName, baseBranch) {
3242
3468
  try {
3243
- const remote = execSync3("git remote get-url origin", { cwd: TARGET_ROOT, encoding: "utf8", stdio: "pipe" }).trim();
3469
+ const remote = execSync4("git remote get-url origin", { cwd: TARGET_ROOT, encoding: "utf8", stdio: "pipe" }).trim();
3244
3470
  const cleanRemote = remote.replace(/\.git$/, "");
3245
3471
  return `${cleanRemote}/compare/${baseBranch}...${branchName}`;
3246
3472
  } catch {
@@ -3275,8 +3501,8 @@ async function pushWorkspaceCommand(input, deps) {
3275
3501
  assertIssueHasGitWorktree(issue, "push");
3276
3502
  if (issue.testApplied) {
3277
3503
  try {
3278
- execSync3("git reset --hard HEAD", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 15e3 });
3279
- execSync3("git clean -fd", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 15e3 });
3504
+ execSync4("git reset --hard HEAD", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 15e3 });
3505
+ execSync4("git clean -fd", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 15e3 });
3280
3506
  issue.testApplied = false;
3281
3507
  deps.eventStore.addEvent(issue.id, "info", "Auto-reverted test squash before push.");
3282
3508
  logger.info({ issueId: issue.id }, "[Push] Auto-reverted test squash before push");
@@ -3303,7 +3529,7 @@ async function pushWorkspaceCommand(input, deps) {
3303
3529
  const planSummary = issue.plan?.summary ?? issue.title;
3304
3530
  let diffStat = "";
3305
3531
  try {
3306
- diffStat = execSync3(
3532
+ diffStat = execSync4(
3307
3533
  `git diff --stat "${issue.baseBranch}"..."${issue.branchName}"`,
3308
3534
  { cwd: TARGET_ROOT, encoding: "utf8", maxBuffer: 512e3, timeout: 1e4, stdio: "pipe" }
3309
3535
  ).trim();
@@ -3318,7 +3544,7 @@ ${diffStat || "No diff stats available"}
3318
3544
  \`\`\`
3319
3545
 
3320
3546
  *Automated by fifony*`;
3321
- execSync3(`git push -u origin "${issue.branchName}"`, { cwd: TARGET_ROOT, stdio: "pipe" });
3547
+ execSync4(`git push -u origin "${issue.branchName}"`, { cwd: TARGET_ROOT, stdio: "pipe" });
3322
3548
  const prBase = state.config.prBaseBranch || issue.baseBranch;
3323
3549
  const ghAvailable = isGhAvailable();
3324
3550
  let prUrl;
@@ -3506,6 +3732,27 @@ async function cancelIssue(c) {
3506
3732
  await persistState(context2.state);
3507
3733
  return { body: { ok: true, issue } };
3508
3734
  }
3735
+ async function deleteIssue(c) {
3736
+ const context2 = getApiRuntimeContextOrThrow();
3737
+ const issueId = getIssueId(c);
3738
+ if (!issueId) {
3739
+ return { status: 400, body: { ok: false, error: "Issue id is required." } };
3740
+ }
3741
+ const issue = findIssue(context2.state, issueId);
3742
+ if (!issue) {
3743
+ return { status: 404, body: { ok: false, error: "Issue not found" } };
3744
+ }
3745
+ if (issue.state === "Running" || issue.state === "Reviewing") {
3746
+ return { status: 409, body: { ok: false, error: `Cannot delete issue in state ${issue.state}. Cancel it first.` } };
3747
+ }
3748
+ try {
3749
+ await deleteIssueCommand({ issue, state: context2.state });
3750
+ await persistState(context2.state);
3751
+ return { body: { ok: true, id: issueId } };
3752
+ } catch (error) {
3753
+ return { status: 500, body: { ok: false, error: error instanceof Error ? error.message : String(error) } };
3754
+ }
3755
+ }
3509
3756
  async function approveAndMerge(c) {
3510
3757
  const context2 = getApiRuntimeContextOrThrow();
3511
3758
  const issueId = getIssueId(c);
@@ -3668,6 +3915,13 @@ var issues_resource_default = {
3668
3915
  return c.json(result.body, result.status);
3669
3916
  }
3670
3917
  return result.body;
3918
+ },
3919
+ "POST /:id/delete": async (c) => {
3920
+ const result = await deleteIssue(c);
3921
+ if (result.status) {
3922
+ return c.json(result.body, result.status);
3923
+ }
3924
+ return result.body;
3671
3925
  }
3672
3926
  }
3673
3927
  };
@@ -4064,7 +4318,7 @@ function hasTerminalQueue(state) {
4064
4318
  import { execFile as execFile2 } from "child_process";
4065
4319
  import { promisify } from "util";
4066
4320
  import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync2, realpathSync } from "fs";
4067
- import { join as join12, dirname } from "path";
4321
+ import { join as join13, dirname } from "path";
4068
4322
  import { homedir as homedir2 } from "os";
4069
4323
  import { env } from "process";
4070
4324
  var execFileAsync = promisify(execFile2);
@@ -4096,7 +4350,7 @@ function resolveCodexHomeCandidates() {
4096
4350
  ]);
4097
4351
  const candidates = [...homePaths, ...direct].filter(Boolean).flatMap((candidate) => {
4098
4352
  if (candidate.endsWith("/.codex") || candidate.endsWith("/codex")) return [candidate];
4099
- return [join12(candidate, ".codex"), join12(candidate, "codex")];
4353
+ return [join13(candidate, ".codex"), join13(candidate, "codex")];
4100
4354
  });
4101
4355
  return [...new Set(candidates)];
4102
4356
  }
@@ -4109,11 +4363,11 @@ function resolveCodexDir() {
4109
4363
  return null;
4110
4364
  }
4111
4365
  function findLatestCodexDb(codexDir) {
4112
- const explicit = join12(codexDir, "state_5.sqlite");
4366
+ const explicit = join13(codexDir, "state_5.sqlite");
4113
4367
  if (existsSync8(explicit)) return explicit;
4114
4368
  const candidates = readdirSync2(codexDir).filter((name) => name.startsWith("state_") && name.endsWith(".sqlite")).sort().reverse();
4115
4369
  if (candidates.length === 0) return null;
4116
- return join12(codexDir, candidates[0]);
4370
+ return join13(codexDir, candidates[0]);
4117
4371
  }
4118
4372
  function computeNextMonday() {
4119
4373
  const now2 = /* @__PURE__ */ new Date();
@@ -4174,10 +4428,10 @@ function resolveClaudePlanKey(displayName) {
4174
4428
  }
4175
4429
  async function collectClaudeUsage() {
4176
4430
  const home = homedir2();
4177
- const claudeDir = join12(home, ".claude");
4431
+ const claudeDir = join13(home, ".claude");
4178
4432
  if (!existsSync8(claudeDir)) return null;
4179
4433
  const available = await whichExists("claude");
4180
- const projectsDir = join12(claudeDir, "projects");
4434
+ const projectsDir = join13(claudeDir, "projects");
4181
4435
  let totalInputTokens = 0;
4182
4436
  let totalOutputTokens = 0;
4183
4437
  let totalSessions = 0;
@@ -4201,7 +4455,7 @@ async function collectClaudeUsage() {
4201
4455
  const projectDirs = readdirSync2(projectsDir, { withFileTypes: true });
4202
4456
  for (const dir of projectDirs) {
4203
4457
  if (!dir.isDirectory()) continue;
4204
- const projectPath = join12(projectsDir, dir.name);
4458
+ const projectPath = join13(projectsDir, dir.name);
4205
4459
  let sessionFiles;
4206
4460
  try {
4207
4461
  sessionFiles = readdirSync2(projectPath).filter((f) => f.endsWith(".jsonl"));
@@ -4209,7 +4463,7 @@ async function collectClaudeUsage() {
4209
4463
  continue;
4210
4464
  }
4211
4465
  for (const file of sessionFiles) {
4212
- const filePath = join12(projectPath, file);
4466
+ const filePath = join13(projectPath, file);
4213
4467
  let content;
4214
4468
  try {
4215
4469
  content = readFileSync6(filePath, "utf8");
@@ -4277,7 +4531,7 @@ async function collectClaudeUsage() {
4277
4531
  let plan = "pro";
4278
4532
  let resetInfo = "Weekly reset (every Monday 00:00 UTC)";
4279
4533
  let currentModel = "";
4280
- const settingsPath = join12(claudeDir, "settings.json");
4534
+ const settingsPath = join13(claudeDir, "settings.json");
4281
4535
  if (existsSync8(settingsPath)) {
4282
4536
  try {
4283
4537
  const settings = JSON.parse(readFileSync6(settingsPath, "utf8"));
@@ -4401,7 +4655,7 @@ function aggregateCodexSessionUsageFromJsonl(lines) {
4401
4655
  };
4402
4656
  }
4403
4657
  function collectCodexSessionUsagesFromJsonl(codexDir) {
4404
- const sessionsDir = join12(codexDir, "sessions");
4658
+ const sessionsDir = join13(codexDir, "sessions");
4405
4659
  if (!existsSync8(sessionsDir)) return [];
4406
4660
  const stack = [sessionsDir];
4407
4661
  const usageByFile = [];
@@ -4417,7 +4671,7 @@ function collectCodexSessionUsagesFromJsonl(codexDir) {
4417
4671
  continue;
4418
4672
  }
4419
4673
  for (const entry of entries) {
4420
- const next = join12(current, entry.name);
4674
+ const next = join13(current, entry.name);
4421
4675
  if (entry.isDirectory()) {
4422
4676
  stack.push(next);
4423
4677
  continue;
@@ -4440,7 +4694,7 @@ async function collectCodexUsage() {
4440
4694
  if (!codexDir) return null;
4441
4695
  const available = await whichExists("codex");
4442
4696
  const models = [];
4443
- const modelsCachePath = join12(codexDir, "models_cache.json");
4697
+ const modelsCachePath = join13(codexDir, "models_cache.json");
4444
4698
  let currentModel = "";
4445
4699
  if (existsSync8(modelsCachePath)) {
4446
4700
  try {
@@ -4455,7 +4709,7 @@ async function collectCodexUsage() {
4455
4709
  } catch {
4456
4710
  }
4457
4711
  }
4458
- const configPath = join12(codexDir, "config.toml");
4712
+ const configPath = join13(codexDir, "config.toml");
4459
4713
  if (existsSync8(configPath)) {
4460
4714
  try {
4461
4715
  const configContent = readFileSync6(configPath, "utf8");
@@ -4718,7 +4972,7 @@ function aggregateGeminiSessionUsageFromJson(content) {
4718
4972
  };
4719
4973
  }
4720
4974
  function collectGeminiSessionUsages() {
4721
- const geminiTmp = join12(homedir2(), ".gemini", "tmp");
4975
+ const geminiTmp = join13(homedir2(), ".gemini", "tmp");
4722
4976
  if (!existsSync8(geminiTmp)) return [];
4723
4977
  const usages = [];
4724
4978
  let entries = [];
@@ -4729,7 +4983,7 @@ function collectGeminiSessionUsages() {
4729
4983
  }
4730
4984
  for (const profile of entries) {
4731
4985
  if (!profile.isDirectory()) continue;
4732
- const chatsDir = join12(geminiTmp, profile.name, "chats");
4986
+ const chatsDir = join13(geminiTmp, profile.name, "chats");
4733
4987
  if (!existsSync8(chatsDir)) continue;
4734
4988
  let sessions = [];
4735
4989
  try {
@@ -4738,7 +4992,7 @@ function collectGeminiSessionUsages() {
4738
4992
  continue;
4739
4993
  }
4740
4994
  for (const sessionFile of sessions) {
4741
- const sessionPath = join12(chatsDir, sessionFile);
4995
+ const sessionPath = join13(chatsDir, sessionFile);
4742
4996
  try {
4743
4997
  const usage = aggregateGeminiSessionUsageFromJson(readFileSync6(sessionPath, "utf8"));
4744
4998
  if (usage.totalTokens > 0) usages.push(usage);
@@ -4759,7 +5013,7 @@ async function collectGeminiUsage() {
4759
5013
  } catch {
4760
5014
  }
4761
5015
  let account = null;
4762
- const accountsPath = join12(homedir2(), ".gemini", "google_accounts.json");
5016
+ const accountsPath = join13(homedir2(), ".gemini", "google_accounts.json");
4763
5017
  if (existsSync8(accountsPath)) {
4764
5018
  try {
4765
5019
  const data = JSON.parse(readFileSync6(accountsPath, "utf8"));
@@ -4777,7 +5031,7 @@ async function collectGeminiUsage() {
4777
5031
  try {
4778
5032
  const { stdout: binPath } = await execFileAsync("which", ["gemini"], { encoding: "utf8", timeout: 3e3 });
4779
5033
  const realBin = realpathSync(binPath.trim());
4780
- const modelsPath = join12(dirname(dirname(realBin)), "node_modules", "@google", "gemini-cli-core", "dist", "src", "config", "models.js");
5034
+ const modelsPath = join13(dirname(dirname(realBin)), "node_modules", "@google", "gemini-cli-core", "dist", "src", "config", "models.js");
4781
5035
  if (existsSync8(modelsPath)) {
4782
5036
  const content = readFileSync6(modelsPath, "utf8");
4783
5037
  const regex = /export const ([A-Z0-9_]+)\s*=\s*'(gemini-[^']+)';/g;
@@ -4797,7 +5051,7 @@ async function collectGeminiUsage() {
4797
5051
  } catch {
4798
5052
  }
4799
5053
  let currentModel = "";
4800
- const settingsPath = join12(homedir2(), ".gemini", "settings.json");
5054
+ const settingsPath = join13(homedir2(), ".gemini", "settings.json");
4801
5055
  if (existsSync8(settingsPath)) {
4802
5056
  try {
4803
5057
  const settings = JSON.parse(readFileSync6(settingsPath, "utf8"));
@@ -4930,10 +5184,10 @@ async function collectProviderUsage(providerName) {
4930
5184
  }
4931
5185
 
4932
5186
  // src/routes/state.ts
4933
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8 } from "fs";
5187
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync9 } from "fs";
4934
5188
  import { randomUUID } from "crypto";
4935
- import { execSync as execSync4 } from "child_process";
4936
- import { basename as basename2, extname, join as join13 } from "path";
5189
+ import { execSync as execSync5 } from "child_process";
5190
+ import { basename as basename2, extname, join as join14 } from "path";
4937
5191
 
4938
5192
  // src/commands/approve-plan.command.ts
4939
5193
  async function approvePlanCommand(input, deps) {
@@ -5112,7 +5366,7 @@ function registerStateRoutes(app, state) {
5112
5366
  if (!issueId) return c.json({ ok: false, error: "Issue id is required." }, 400);
5113
5367
  const issue = findIssue(state, issueId);
5114
5368
  if (!issue) return c.json({ ok: false, error: "Issue not found." }, 404);
5115
- const { dryMerge } = await import("./workspace-ZD5H6YOL.js");
5369
+ const { dryMerge } = await import("./workspace-AOHHNWL5.js");
5116
5370
  const result = dryMerge(issue);
5117
5371
  return c.json({ ok: true, ...result });
5118
5372
  } catch (error) {
@@ -5127,8 +5381,8 @@ function registerStateRoutes(app, state) {
5127
5381
  if (!issueId) return c.json({ ok: false, error: "Issue id is required." }, 400);
5128
5382
  const issue = findIssue(state, issueId);
5129
5383
  if (!issue) return c.json({ ok: false, error: "Issue not found." }, 404);
5130
- const { rebaseWorktree } = await import("./workspace-ZD5H6YOL.js");
5131
- const result = rebaseWorktree(issue);
5384
+ const { rebaseWorktree: rebaseWorktree2 } = await import("./workspace-AOHHNWL5.js");
5385
+ const result = rebaseWorktree2(issue);
5132
5386
  if (result.success) {
5133
5387
  addEvent(state, issue.id, "info", `Branch ${issue.branchName} rebased onto ${issue.baseBranch}.`);
5134
5388
  }
@@ -5149,7 +5403,7 @@ function registerStateRoutes(app, state) {
5149
5403
  throw new Error("No branch name found for this issue.");
5150
5404
  }
5151
5405
  try {
5152
- execSync4(
5406
+ execSync5(
5153
5407
  `git merge --squash "${issue.branchName}"`,
5154
5408
  { encoding: "utf8", cwd: TARGET_ROOT, stdio: "pipe", timeout: 3e4 }
5155
5409
  );
@@ -5166,8 +5420,8 @@ function registerStateRoutes(app, state) {
5166
5420
  logger.info({ issueId: parseIssue(c) }, "[API] POST /api/issues/:id/revert-try");
5167
5421
  return mutateIssueState(state, c, async (issue) => {
5168
5422
  try {
5169
- execSync4("git reset --hard HEAD", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 15e3 });
5170
- execSync4("git clean -fd", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 15e3 });
5423
+ execSync5("git reset --hard HEAD", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 15e3 });
5424
+ execSync5("git clean -fd", { cwd: TARGET_ROOT, stdio: "pipe", timeout: 15e3 });
5171
5425
  } catch (err) {
5172
5426
  const msg = err.stderr || err.stdout || String(err);
5173
5427
  throw new Error(`git reset/clean failed: ${msg}`);
@@ -5210,15 +5464,15 @@ function registerStateRoutes(app, state) {
5210
5464
  if (!Array.isArray(payload.files) || payload.files.length === 0) {
5211
5465
  return c.json({ ok: false, error: "No files provided." }, 400);
5212
5466
  }
5213
- const issueAttachDir = join13(ATTACHMENTS_ROOT, issue.id);
5467
+ const issueAttachDir = join14(ATTACHMENTS_ROOT, issue.id);
5214
5468
  mkdirSync6(issueAttachDir, { recursive: true });
5215
5469
  const newPaths = [];
5216
5470
  for (const file of payload.files) {
5217
5471
  if (typeof file.data !== "string" || !file.name) continue;
5218
5472
  const safeExt = extname(file.name).replace(/[^a-z0-9.]/gi, "").slice(0, 10) || ".bin";
5219
5473
  const safeName = `${randomUUID()}${safeExt}`;
5220
- const dest = join13(issueAttachDir, safeName);
5221
- writeFileSync8(dest, Buffer.from(file.data, "base64"));
5474
+ const dest = join14(issueAttachDir, safeName);
5475
+ writeFileSync9(dest, Buffer.from(file.data, "base64"));
5222
5476
  newPaths.push(dest);
5223
5477
  }
5224
5478
  issue.images = [...issue.images ?? [], ...newPaths];
@@ -5238,7 +5492,7 @@ function registerStateRoutes(app, state) {
5238
5492
  const filename = c.req.param?.("filename") ?? c.req.params?.filename ?? "";
5239
5493
  if (!filename) return c.json({ ok: false, error: "Filename is required." }, 400);
5240
5494
  const safeName = basename2(filename);
5241
- const filePath = join13(ATTACHMENTS_ROOT, issueId, safeName);
5495
+ const filePath = join14(ATTACHMENTS_ROOT, issueId, safeName);
5242
5496
  if (!existsSync9(filePath)) return c.json({ ok: false, error: "Image not found." }, 404);
5243
5497
  const ext = extname(safeName).toLowerCase();
5244
5498
  const mimeMap = {
@@ -5263,7 +5517,7 @@ function registerStateRoutes(app, state) {
5263
5517
  const issue = findIssue(state, issueId);
5264
5518
  if (!issue) return c.json({ ok: false, error: "Issue not found." }, 404);
5265
5519
  try {
5266
- const { getIssueTransitionHistory } = await import("./issue-state-machine-TEIICCAA.js");
5520
+ const { getIssueTransitionHistory } = await import("./issue-state-machine-KOZE5JWX.js");
5267
5521
  const limit = parseInt(c.req.query("limit") ?? "50", 10);
5268
5522
  const offset = parseInt(c.req.query("offset") ?? "0", 10);
5269
5523
  const transitions = await getIssueTransitionHistory(issue.id, { limit, offset });
@@ -5274,7 +5528,7 @@ function registerStateRoutes(app, state) {
5274
5528
  });
5275
5529
  app.get("/api/state-machine/transitions", async (c) => {
5276
5530
  try {
5277
- const { getStateMachineTransitions } = await import("./issue-state-machine-TEIICCAA.js");
5531
+ const { getStateMachineTransitions } = await import("./issue-state-machine-KOZE5JWX.js");
5278
5532
  return c.json({ ok: true, transitions: getStateMachineTransitions() });
5279
5533
  } catch (error) {
5280
5534
  return c.json({ ok: false, error: error instanceof Error ? error.message : String(error) }, 500);
@@ -5282,7 +5536,7 @@ function registerStateRoutes(app, state) {
5282
5536
  });
5283
5537
  app.get("/api/state-machine/visualize", async (c) => {
5284
5538
  try {
5285
- const { visualizeStateMachine } = await import("./issue-state-machine-TEIICCAA.js");
5539
+ const { visualizeStateMachine } = await import("./issue-state-machine-KOZE5JWX.js");
5286
5540
  const dot = visualizeStateMachine();
5287
5541
  if (!dot) return c.json({ ok: false, error: "Visualization not available." }, 404);
5288
5542
  return c.json({ ok: true, dot });
@@ -5299,10 +5553,10 @@ function registerStateRoutes(app, state) {
5299
5553
 
5300
5554
  // src/agents/planning/issue-enhancer.ts
5301
5555
  import { env as env2 } from "process";
5302
- import { existsSync as existsSync10, mkdtempSync as mkdtempSync3, readFileSync as readFileSync7, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
5556
+ import { existsSync as existsSync10, mkdtempSync as mkdtempSync4, readFileSync as readFileSync7, rmSync as rmSync5, writeFileSync as writeFileSync10 } from "fs";
5303
5557
  import { spawn as spawn2 } from "child_process";
5304
- import { tmpdir as tmpdir3 } from "os";
5305
- import { join as join14 } from "path";
5558
+ import { tmpdir as tmpdir4 } from "os";
5559
+ import { join as join15 } from "path";
5306
5560
  async function buildPrompt2(field, title, description, issueType, images) {
5307
5561
  const context2 = {
5308
5562
  title: title || "(empty)",
@@ -5320,13 +5574,29 @@ function parseEnhancerOutput(raw, expectedField) {
5320
5574
  if (!text) {
5321
5575
  throw new Error("AI provider returned an empty response.");
5322
5576
  }
5323
- const candidates = extractJsonObjects(
5324
- text.match(/```(?:json)?\s*([\s\S]*?)\s*```/i)?.[1]?.trim() ?? text
5325
- );
5577
+ const codeBlocks = [...text.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi)].map((m) => m[1].trim()).reverse();
5578
+ for (const block of codeBlocks) {
5579
+ for (const candidate of extractJsonObjects(block)) {
5580
+ const value = parseCandidate(candidate, expectedField);
5581
+ if (value) return value;
5582
+ }
5583
+ }
5584
+ const sourceText = codeBlocks[0] ?? text;
5585
+ const candidates = extractJsonObjects(sourceText);
5326
5586
  for (const candidate of candidates) {
5327
5587
  const value = parseCandidate(candidate, expectedField);
5328
5588
  if (value) return value;
5329
5589
  }
5590
+ const fieldRe = /\{\s*"field"/g;
5591
+ const starts = [];
5592
+ let fm;
5593
+ while ((fm = fieldRe.exec(sourceText)) !== null) starts.push(fm.index);
5594
+ for (let i = starts.length - 1; i >= 0; i--) {
5595
+ for (const candidate of extractJsonObjects(sourceText.slice(starts[i]))) {
5596
+ const value = parseCandidate(candidate, expectedField);
5597
+ if (value) return value;
5598
+ }
5599
+ }
5330
5600
  const cleanedRaw = text.trim();
5331
5601
  const trimmed = cleanedRaw.replace(/^\"|\"$/g, "").trim();
5332
5602
  if (trimmed) {
@@ -5349,7 +5619,7 @@ function parseCandidate(raw, expectedField) {
5349
5619
  const parsed = JSON.parse(candidate);
5350
5620
  const value = typeof parsed.value === "string" ? parsed.value.trim() : typeof parsed.text === "string" ? parsed.text.trim() : "";
5351
5621
  const field = parsed.field;
5352
- const isPlaceholder = /^\.{2,}$/.test(value);
5622
+ const isPlaceholder = /^\.{2,}$/.test(value) || /^<REPLACE_/.test(value) || /^your improved /i.test(value);
5353
5623
  if (value && !isPlaceholder && (!field || field === expectedField)) {
5354
5624
  return value;
5355
5625
  }
@@ -5378,13 +5648,13 @@ function readProviderOutput(resultFile, fallback) {
5378
5648
  return fallback;
5379
5649
  }
5380
5650
  async function runProviderCommand(command, provider, prompt, title, description, field, timeoutMs, images) {
5381
- const tempDir = mkdtempSync3(join14(tmpdir3(), "fifony-enhance-"));
5382
- const promptFile = join14(tempDir, "fifony-enhance-prompt.md");
5383
- const issuePayloadFile = join14(tempDir, "fifony-issue.json");
5384
- const resultFile = join14(tempDir, "fifony-result.txt");
5385
- writeFileSync9(promptFile, `${prompt}
5651
+ const tempDir = mkdtempSync4(join15(tmpdir4(), "fifony-enhance-"));
5652
+ const promptFile = join15(tempDir, "fifony-enhance-prompt.md");
5653
+ const issuePayloadFile = join15(tempDir, "fifony-issue.json");
5654
+ const resultFile = join15(tempDir, "fifony-result.txt");
5655
+ writeFileSync10(promptFile, `${prompt}
5386
5656
  `, "utf8");
5387
- writeFileSync9(issuePayloadFile, JSON.stringify({ title, description, field }, null, 2), "utf8");
5657
+ writeFileSync10(issuePayloadFile, JSON.stringify({ title, description, field }, null, 2), "utf8");
5388
5658
  let effectiveCommand = command;
5389
5659
  if (provider === "codex" && images?.length) {
5390
5660
  const imageFlags = images.map((p) => `--image "${p}"`).join(" ");
@@ -5408,7 +5678,7 @@ async function runProviderCommand(command, provider, prompt, title, description,
5408
5678
  let timeout = false;
5409
5679
  const child = spawn2(effectiveCommand, {
5410
5680
  shell: true,
5411
- cwd: tempDir,
5681
+ cwd: TARGET_ROOT,
5412
5682
  env: spawnEnv
5413
5683
  });
5414
5684
  if (child.stdin) child.stdin.end();
@@ -5424,22 +5694,25 @@ async function runProviderCommand(command, provider, prompt, title, description,
5424
5694
  }, Math.max(timeoutMs, 1e3));
5425
5695
  child.on("error", () => {
5426
5696
  clearTimeout(timer);
5427
- rmSync4(tempDir, { recursive: true, force: true });
5697
+ rmSync5(tempDir, { recursive: true, force: true });
5428
5698
  reject(new Error("Could not execute AI command."));
5429
5699
  });
5430
5700
  child.on("close", (code) => {
5431
5701
  clearTimeout(timer);
5432
5702
  if (timeout) {
5433
- rmSync4(tempDir, { recursive: true, force: true });
5703
+ rmSync5(tempDir, { recursive: true, force: true });
5434
5704
  reject(new Error(`Enhance command timeout after ${Date.now() - startedAt}ms.`));
5435
5705
  return;
5436
5706
  }
5437
5707
  const commandOutput = readProviderOutput(resultFile, output);
5438
- rmSync4(tempDir, { recursive: true, force: true });
5708
+ rmSync5(tempDir, { recursive: true, force: true });
5439
5709
  if (code !== 0) {
5440
- const providerOutput = appendFileTail(commandOutput, "", 12e3);
5441
- const reason = providerOutput.trim() ? ` Enhance command output: ${providerOutput.slice(0, 1200)}` : "";
5442
- reject(new Error(`Enhance command failed (exit ${code ?? "unknown"}).${reason}`));
5710
+ if (commandOutput.trim()) {
5711
+ logger.warn({ exitCode: code, provider }, `[Enhance] Provider exited ${code} but produced output \u2014 attempting to use it`);
5712
+ resolve3(commandOutput);
5713
+ return;
5714
+ }
5715
+ reject(new Error(`Enhance command failed (exit ${code ?? "unknown"}) with no output.`));
5443
5716
  return;
5444
5717
  }
5445
5718
  resolve3(commandOutput);
@@ -5452,7 +5725,7 @@ async function enhanceIssueField(payload, config, _workflowDefinition) {
5452
5725
  const description = typeof payload.description === "string" ? payload.description.trim() : "";
5453
5726
  const issueType = typeof payload.issueType === "string" ? payload.issueType.trim() : void 0;
5454
5727
  const images = Array.isArray(payload.images) ? payload.images.filter((p) => typeof p === "string") : void 0;
5455
- const { provider: selectedProvider, model: planModel } = await resolvePlanStageConfig(config);
5728
+ const { provider: selectedProvider, model: selectedModel } = await resolvePlanStageConfig(config);
5456
5729
  const providers = detectAvailableProviders();
5457
5730
  const isAvailable = providers.some((p) => p.name === selectedProvider && p.available);
5458
5731
  if (!isAvailable) {
@@ -5473,7 +5746,7 @@ async function enhanceIssueField(payload, config, _workflowDefinition) {
5473
5746
  additionalProperties: false
5474
5747
  });
5475
5748
  const command = adapter.buildCommand({
5476
- model: planModel,
5749
+ model: selectedModel,
5477
5750
  imagePaths: images,
5478
5751
  jsonSchema: selectedProvider === "claude" ? ENHANCE_JSON_SCHEMA : void 0,
5479
5752
  noToolAccess: selectedProvider === "claude"
@@ -5492,7 +5765,7 @@ async function enhanceIssueField(payload, config, _workflowDefinition) {
5492
5765
  config.commandTimeoutMs,
5493
5766
  images
5494
5767
  );
5495
- logger.info({ provider: selectedProvider, model: planModel, field, rawOutput: output.slice(0, 2e3) }, "Enhance raw output");
5768
+ logger.info({ provider: selectedProvider, model: selectedModel, field, rawOutput: output.slice(0, 2e3) }, "Enhance raw output");
5496
5769
  const value = parseEnhancerOutput(output, field);
5497
5770
  logger.info({ provider: selectedProvider, field, parsedValue: value.slice(0, 500) }, "Enhance parsed value");
5498
5771
  return { field, value, provider: selectedProvider };
@@ -5815,8 +6088,8 @@ function registerScanningRoutes(app, state) {
5815
6088
  }
5816
6089
 
5817
6090
  // src/agents/claude-md-manager.ts
5818
- import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as writeFileSync10 } from "fs";
5819
- import { join as join15 } from "path";
6091
+ import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as writeFileSync11 } from "fs";
6092
+ import { join as join16 } from "path";
5820
6093
  var BLOCK_START = "<!-- FIFONY:START \u2014 managed by fifony, do not edit manually -->";
5821
6094
  var BLOCK_END = "<!-- FIFONY:END -->";
5822
6095
  var BLOCK_PATTERN = /<!-- FIFONY:START[^>]*-->[\s\S]*?<!-- FIFONY:END -->/;
@@ -5847,7 +6120,7 @@ function buildManagedBlock(skills, agents, commands) {
5847
6120
  }
5848
6121
  function updateClaudeMdManagedBlock(targetRoot, skills, agents, commands) {
5849
6122
  if (skills.length === 0 && agents.length === 0 && commands.length === 0) return;
5850
- const claudeMdPath = join15(targetRoot, "CLAUDE.md");
6123
+ const claudeMdPath = join16(targetRoot, "CLAUDE.md");
5851
6124
  const newBlock = buildManagedBlock(skills, agents, commands);
5852
6125
  let existing = "";
5853
6126
  if (existsSync11(claudeMdPath)) {
@@ -5866,7 +6139,7 @@ ${newBlock}
5866
6139
  `;
5867
6140
  }
5868
6141
  if (updated === existing) return;
5869
- writeFileSync10(claudeMdPath, updated, "utf8");
6142
+ writeFileSync11(claudeMdPath, updated, "utf8");
5870
6143
  }
5871
6144
 
5872
6145
  // src/routes/catalog.ts
@@ -5995,7 +6268,7 @@ function registerReferenceRepositoryRoutes(app) {
5995
6268
  }
5996
6269
 
5997
6270
  // src/routes/misc.ts
5998
- import { execSync as execSync5 } from "child_process";
6271
+ import { execSync as execSync6 } from "child_process";
5999
6272
  import {
6000
6273
  appendFileSync,
6001
6274
  closeSync,
@@ -6006,13 +6279,13 @@ import {
6006
6279
  readFileSync as readFileSync9,
6007
6280
  readSync,
6008
6281
  statSync,
6009
- writeFileSync as writeFileSync11
6282
+ writeFileSync as writeFileSync12
6010
6283
  } from "fs";
6011
6284
  import { randomUUID as randomUUID2 } from "crypto";
6012
- import { basename as basename3, extname as extname2, join as join16 } from "path";
6285
+ import { basename as basename3, extname as extname2, join as join17 } from "path";
6013
6286
  function registerMiscRoutes(app, state) {
6014
6287
  app.get("/api/queue/stats", async (c) => {
6015
- const { getQueueStats } = await import("./queue-workers-5JOCROD6.js");
6288
+ const { getQueueStats } = await import("./queue-workers-ZEZHDX7M.js");
6016
6289
  return c.json(await getQueueStats());
6017
6290
  });
6018
6291
  app.post("/api/issues/:id/push", async (c) => {
@@ -6178,7 +6451,7 @@ function registerMiscRoutes(app, state) {
6178
6451
  let raw = "";
6179
6452
  if (issue.branchName && issue.baseBranch) {
6180
6453
  try {
6181
- raw = execSync5(
6454
+ raw = execSync6(
6182
6455
  `git diff --no-color "${issue.baseBranch}"..."${issue.branchName}"`,
6183
6456
  { encoding: "utf8", maxBuffer: 4 * 1024 * 1024, timeout: 15e3, cwd: TARGET_ROOT, stdio: "pipe" }
6184
6457
  );
@@ -6190,7 +6463,7 @@ function registerMiscRoutes(app, state) {
6190
6463
  return c.json({ ok: true, files: [], diff: "", message: "Source root not found." });
6191
6464
  }
6192
6465
  try {
6193
- raw = execSync5(
6466
+ raw = execSync6(
6194
6467
  `git diff --no-index --no-color -- "${SOURCE_ROOT}" "${wp}"`,
6195
6468
  { encoding: "utf8", maxBuffer: 4 * 1024 * 1024, timeout: 15e3 }
6196
6469
  );
@@ -6259,7 +6532,7 @@ function registerMiscRoutes(app, state) {
6259
6532
  if (!branchName || !/^[a-zA-Z0-9/_.-]+$/.test(branchName)) {
6260
6533
  return c.json({ ok: false, error: "Invalid branch name." }, 400);
6261
6534
  }
6262
- execSync5(`git checkout -b "${branchName}"`, { cwd: TARGET_ROOT, stdio: "pipe" });
6535
+ execSync6(`git checkout -b "${branchName}"`, { cwd: TARGET_ROOT, stdio: "pipe" });
6263
6536
  state.config.defaultBranch = branchName;
6264
6537
  await persistState(state);
6265
6538
  return c.json({ ok: true, defaultBranch: branchName });
@@ -6275,9 +6548,9 @@ function registerMiscRoutes(app, state) {
6275
6548
  }
6276
6549
  let created = false;
6277
6550
  try {
6278
- execSync5(`git checkout "${branchName}"`, { cwd: TARGET_ROOT, stdio: "pipe" });
6551
+ execSync6(`git checkout "${branchName}"`, { cwd: TARGET_ROOT, stdio: "pipe" });
6279
6552
  } catch {
6280
- execSync5(`git checkout -b "${branchName}"`, { cwd: TARGET_ROOT, stdio: "pipe" });
6553
+ execSync6(`git checkout -b "${branchName}"`, { cwd: TARGET_ROOT, stdio: "pipe" });
6281
6554
  created = true;
6282
6555
  }
6283
6556
  state.config.defaultBranch = branchName;
@@ -6300,7 +6573,7 @@ function registerMiscRoutes(app, state) {
6300
6573
  });
6301
6574
  app.get("/api/gitignore/status", async (c) => {
6302
6575
  try {
6303
- const gitignorePath = join16(TARGET_ROOT, ".gitignore");
6576
+ const gitignorePath = join17(TARGET_ROOT, ".gitignore");
6304
6577
  if (!existsSync12(gitignorePath)) {
6305
6578
  return c.json({ exists: false, hasFifony: false });
6306
6579
  }
@@ -6315,9 +6588,9 @@ function registerMiscRoutes(app, state) {
6315
6588
  });
6316
6589
  app.post("/api/gitignore/add", async (c) => {
6317
6590
  try {
6318
- const gitignorePath = join16(TARGET_ROOT, ".gitignore");
6591
+ const gitignorePath = join17(TARGET_ROOT, ".gitignore");
6319
6592
  if (!existsSync12(gitignorePath)) {
6320
- writeFileSync11(gitignorePath, "# Fifony state directory\n.fifony/\n", "utf-8");
6593
+ writeFileSync12(gitignorePath, "# Fifony state directory\n.fifony/\n", "utf-8");
6321
6594
  return c.json({ ok: true, created: true });
6322
6595
  }
6323
6596
  const content = readFileSync9(gitignorePath, "utf-8");
@@ -6345,11 +6618,11 @@ function registerMiscRoutes(app, state) {
6345
6618
  if (!issue) return c.json({ ok: false, error: "Issue not found." }, 404);
6346
6619
  const wp = issue.workspacePath;
6347
6620
  if (!wp) return c.json({ ok: true, files: [] });
6348
- const outputsDir = join16(wp, "outputs");
6621
+ const outputsDir = join17(wp, "outputs");
6349
6622
  if (!existsSync12(outputsDir)) return c.json({ ok: true, files: [] });
6350
6623
  const entries = readdirSync3(outputsDir).filter((f) => f.endsWith(".stdout.log")).map((f) => {
6351
6624
  try {
6352
- const s = statSync(join16(outputsDir, f));
6625
+ const s = statSync(join17(outputsDir, f));
6353
6626
  return { name: f, size: s.size };
6354
6627
  } catch {
6355
6628
  return { name: f, size: 0 };
@@ -6374,7 +6647,7 @@ function registerMiscRoutes(app, state) {
6374
6647
  }
6375
6648
  const wp = issue.workspacePath;
6376
6649
  if (!wp) return c.json({ ok: false, error: "No workspace found." }, 404);
6377
- const filePath = join16(wp, "outputs", safeName);
6650
+ const filePath = join17(wp, "outputs", safeName);
6378
6651
  if (!existsSync12(filePath)) return c.json({ ok: false, error: "Output file not found." }, 404);
6379
6652
  const content = readFileSync9(filePath, "utf8");
6380
6653
  return new Response(content, { headers: { "Content-Type": "text/plain; charset=utf-8" } });
@@ -6389,15 +6662,15 @@ function registerMiscRoutes(app, state) {
6389
6662
  return c.json({ ok: false, error: "No files provided." }, 400);
6390
6663
  }
6391
6664
  const uploadId = randomUUID2();
6392
- const uploadDir = join16(ATTACHMENTS_ROOT, "temp", uploadId);
6665
+ const uploadDir = join17(ATTACHMENTS_ROOT, "temp", uploadId);
6393
6666
  mkdirSync7(uploadDir, { recursive: true });
6394
6667
  const paths = [];
6395
6668
  for (const file of payload.files) {
6396
6669
  if (typeof file.data !== "string" || !file.name) continue;
6397
6670
  const safeExt = extname2(file.name).replace(/[^a-z0-9.]/gi, "").slice(0, 10) || ".bin";
6398
6671
  const safeName = `${randomUUID2()}${safeExt}`;
6399
- const dest = join16(uploadDir, safeName);
6400
- writeFileSync11(dest, Buffer.from(file.data, "base64"));
6672
+ const dest = join17(uploadDir, safeName);
6673
+ writeFileSync12(dest, Buffer.from(file.data, "base64"));
6401
6674
  paths.push(dest);
6402
6675
  }
6403
6676
  return c.json({ ok: true, paths, uploadId });
@@ -6781,6 +7054,16 @@ async function loadPersistedState() {
6781
7054
  if (record2?.state && typeof record2.state === "object") {
6782
7055
  const state = record2.state;
6783
7056
  if (Array.isArray(state.issues) && state.issues.length > 0) {
7057
+ for (const issue of state.issues) {
7058
+ try {
7059
+ const current = await getCurrentPlanForIssue(issue.id);
7060
+ if (current) {
7061
+ issue.plan = current.plan;
7062
+ }
7063
+ } catch (err) {
7064
+ logger.warn({ issueId: issue.id, err: String(err) }, "[Store] Failed to hydrate plan on load");
7065
+ }
7066
+ }
6784
7067
  return state;
6785
7068
  }
6786
7069
  logger.warn("Runtime state blob has no issues, attempting recovery from issue resource...");
@@ -7051,7 +7334,7 @@ function resolveProjectMetadata(settings, targetRoot) {
7051
7334
  };
7052
7335
  }
7053
7336
  function scanProjectFiles(targetRoot) {
7054
- const check = (rel) => existsSync14(join17(targetRoot, rel));
7337
+ const check = (rel) => existsSync14(join18(targetRoot, rel));
7055
7338
  const files = {
7056
7339
  claudeMd: check("CLAUDE.md"),
7057
7340
  claudeDir: check(".claude"),
@@ -7072,7 +7355,7 @@ function scanProjectFiles(targetRoot) {
7072
7355
  };
7073
7356
  const existingAgents = [];
7074
7357
  for (const agentDir of [".claude/agents", ".codex/agents"]) {
7075
- const fullPath = join17(targetRoot, agentDir);
7358
+ const fullPath = join18(targetRoot, agentDir);
7076
7359
  if (!existsSync14(fullPath)) continue;
7077
7360
  try {
7078
7361
  const entries = readdirSync4(fullPath);
@@ -7086,12 +7369,12 @@ function scanProjectFiles(targetRoot) {
7086
7369
  }
7087
7370
  const existingSkills = [];
7088
7371
  for (const skillDir of [".claude/skills", ".codex/skills"]) {
7089
- const fullPath = join17(targetRoot, skillDir);
7372
+ const fullPath = join18(targetRoot, skillDir);
7090
7373
  if (!existsSync14(fullPath)) continue;
7091
7374
  try {
7092
7375
  const entries = readdirSync4(fullPath);
7093
7376
  for (const entry of entries) {
7094
- const skillFile = join17(fullPath, entry, "SKILL.md");
7377
+ const skillFile = join18(fullPath, entry, "SKILL.md");
7095
7378
  if (existsSync14(skillFile)) {
7096
7379
  existingSkills.push(entry);
7097
7380
  }
@@ -7100,7 +7383,7 @@ function scanProjectFiles(targetRoot) {
7100
7383
  }
7101
7384
  }
7102
7385
  let readmeExcerpt = "";
7103
- const readmePath = join17(targetRoot, "README.md");
7386
+ const readmePath = join18(targetRoot, "README.md");
7104
7387
  if (existsSync14(readmePath)) {
7105
7388
  try {
7106
7389
  const content = readFileSync11(readmePath, "utf8");
@@ -7110,7 +7393,7 @@ function scanProjectFiles(targetRoot) {
7110
7393
  }
7111
7394
  let packageName = "";
7112
7395
  let packageDescription = "";
7113
- const pkgPath = join17(targetRoot, "package.json");
7396
+ const pkgPath = join18(targetRoot, "package.json");
7114
7397
  if (existsSync14(pkgPath)) {
7115
7398
  try {
7116
7399
  const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
@@ -7255,9 +7538,9 @@ function collectAgentArtifacts(agentsDir, usedNames, out) {
7255
7538
  const parent = slugify(basename4(dirname2(agentsDir)));
7256
7539
  const entries = collectDirectoryEntries(agentsDir);
7257
7540
  for (const entry of entries) {
7258
- const itemPath = join17(agentsDir, entry.name);
7541
+ const itemPath = join18(agentsDir, entry.name);
7259
7542
  if (entry.isDirectory()) {
7260
- const nestedAgentSpec = join17(itemPath, "AGENT.md");
7543
+ const nestedAgentSpec = join18(itemPath, "AGENT.md");
7261
7544
  if (existsSync14(nestedAgentSpec)) {
7262
7545
  const name2 = uniqueSuffix(`${parent}__${slugify(entry.name)}`, usedNames);
7263
7546
  out.push({ kind: "agent", sourcePath: nestedAgentSpec, targetName: name2 });
@@ -7279,9 +7562,9 @@ function collectSkillArtifacts(skillsDir, usedNames, out) {
7279
7562
  const parent = slugify(basename4(dirname2(skillsDir)));
7280
7563
  const entries = collectDirectoryEntries(skillsDir);
7281
7564
  for (const entry of entries) {
7282
- const itemPath = join17(skillsDir, entry.name);
7565
+ const itemPath = join18(skillsDir, entry.name);
7283
7566
  if (entry.isDirectory()) {
7284
- const skillFile = join17(itemPath, "SKILL.md");
7567
+ const skillFile = join18(itemPath, "SKILL.md");
7285
7568
  if (existsSync14(skillFile)) {
7286
7569
  const name = uniqueSuffix(`${parent}__${slugify(entry.name)}`, usedNames);
7287
7570
  out.push({ kind: "skill", sourcePath: skillFile, targetName: name });
@@ -7309,7 +7592,7 @@ function collectStandardArtifacts(repoPath) {
7309
7592
  for (const entry of entries) {
7310
7593
  if (!entry.isDirectory()) continue;
7311
7594
  if (SKIP_DIRS.has(entry.name)) continue;
7312
- const childPath = join17(state.path, entry.name);
7595
+ const childPath = join18(state.path, entry.name);
7313
7596
  if (entry.name === "agents") {
7314
7597
  collectAgentArtifacts(childPath, agentsUsed, artifacts);
7315
7598
  }
@@ -7337,13 +7620,13 @@ function collectAgencyArtifacts(repoPath) {
7337
7620
  if (SKIP_DIRS.has(entry.name) || AGENCY_AGENTS_EXCLUDED_DIRS.has(entry.name)) {
7338
7621
  continue;
7339
7622
  }
7340
- queue.push({ path: join17(state.path, entry.name), depth: state.depth + 1 });
7623
+ queue.push({ path: join18(state.path, entry.name), depth: state.depth + 1 });
7341
7624
  continue;
7342
7625
  }
7343
- if (!isMarkdownFile(entry.name, "readme.md") || !isReferenceFrontMatterFile(join17(state.path, entry.name))) {
7626
+ if (!isMarkdownFile(entry.name, "readme.md") || !isReferenceFrontMatterFile(join18(state.path, entry.name))) {
7344
7627
  continue;
7345
7628
  }
7346
- const itemPath = join17(state.path, entry.name);
7629
+ const itemPath = join18(state.path, entry.name);
7347
7630
  const targetName = uniqueSuffix(buildRelativeArtifactName(repoPath, itemPath), agentsUsed);
7348
7631
  artifacts.push({
7349
7632
  kind: "agent",
@@ -7357,12 +7640,12 @@ function collectAgencyArtifacts(repoPath) {
7357
7640
  function collectImpeccableArtifacts(repoPath) {
7358
7641
  const skillsUsed = /* @__PURE__ */ new Set();
7359
7642
  const artifacts = [];
7360
- const sourceSkills = join17(repoPath, "source", "skills");
7643
+ const sourceSkills = join18(repoPath, "source", "skills");
7361
7644
  if (existsSync14(sourceSkills)) {
7362
7645
  collectSkillArtifacts(sourceSkills, skillsUsed, artifacts);
7363
7646
  return artifacts;
7364
7647
  }
7365
- const claudeSkills = join17(repoPath, ".claude", "skills");
7648
+ const claudeSkills = join18(repoPath, ".claude", "skills");
7366
7649
  if (existsSync14(claudeSkills)) {
7367
7650
  collectSkillArtifacts(claudeSkills, skillsUsed, artifacts);
7368
7651
  }
@@ -7389,7 +7672,7 @@ function getReferenceRepositoriesRoot() {
7389
7672
  }
7390
7673
  function listReferenceRepositories() {
7391
7674
  return DEFAULT_REFERENCE_REPOSITORIES.map((repo) => {
7392
- const path = join17(REPOSITORY_ROOT, repo.id);
7675
+ const path = join18(REPOSITORY_ROOT, repo.id);
7393
7676
  const status = {
7394
7677
  id: repo.id,
7395
7678
  name: repo.name,
@@ -7401,7 +7684,7 @@ function listReferenceRepositories() {
7401
7684
  if (!status.present) {
7402
7685
  return status;
7403
7686
  }
7404
- if (!existsSync14(join17(path, ".git"))) {
7687
+ if (!existsSync14(join18(path, ".git"))) {
7405
7688
  status.error = "Path exists but is not a git repo";
7406
7689
  return status;
7407
7690
  }
@@ -7433,7 +7716,7 @@ function syncReferenceRepositories(repositoryId) {
7433
7716
  }
7434
7717
  const results = [];
7435
7718
  for (const repo of selected) {
7436
- const target = join17(root, repo.id);
7719
+ const target = join18(root, repo.id);
7437
7720
  const candidates = [repo.url, ...repo.fallbackUrls ?? []];
7438
7721
  if (!existsSync14(target)) {
7439
7722
  let cloneError;
@@ -7462,7 +7745,7 @@ function syncReferenceRepositories(repositoryId) {
7462
7745
  }
7463
7746
  continue;
7464
7747
  }
7465
- if (!existsSync14(join17(target, ".git"))) {
7748
+ if (!existsSync14(join18(target, ".git"))) {
7466
7749
  results.push({
7467
7750
  id: repo.id,
7468
7751
  path: target,
@@ -7497,14 +7780,14 @@ function importReferenceArtifacts(repositoryId, workspaceRoot, options) {
7497
7780
  if (!repository) {
7498
7781
  throw new Error(`Unknown reference repository: ${repositoryId}`);
7499
7782
  }
7500
- const localPath = join17(REPOSITORY_ROOT, repository.id);
7783
+ const localPath = join18(REPOSITORY_ROOT, repository.id);
7501
7784
  if (!existsSync14(localPath)) {
7502
7785
  throw new Error(`Repository not synced yet: ${repository.id}. Run 'fifony onboarding sync --repository ${repository.id}' first.`);
7503
7786
  }
7504
7787
  const basePath = resolve2(workspaceRoot);
7505
- const targetBase = options.importToGlobal ? join17(homedir3(), ".codex") : join17(basePath, ".codex");
7506
- const agentsDir = join17(targetBase, "agents");
7507
- const skillsDir = join17(targetBase, "skills");
7788
+ const targetBase = options.importToGlobal ? join18(homedir3(), ".codex") : join18(basePath, ".codex");
7789
+ const agentsDir = join18(targetBase, "agents");
7790
+ const skillsDir = join18(targetBase, "skills");
7508
7791
  const artifacts = collectArtifacts(localPath, repository.id);
7509
7792
  const filtered = options.kind === "all" ? artifacts : artifacts.filter((artifact) => artifact.kind === options.kind.slice(0, -1));
7510
7793
  const summary = {
@@ -7530,7 +7813,7 @@ function importReferenceArtifacts(repositoryId, workspaceRoot, options) {
7530
7813
  try {
7531
7814
  const source = readFileSync11(artifact.sourcePath, "utf8");
7532
7815
  if (artifact.kind === "agent") {
7533
- const target = join17(agentsDir, `${artifact.targetName}.md`);
7816
+ const target = join18(agentsDir, `${artifact.targetName}.md`);
7534
7817
  if (!options.overwrite && existsSync14(target)) {
7535
7818
  summary.skippedAgents.push(artifact.targetName);
7536
7819
  continue;
@@ -7539,11 +7822,11 @@ function importReferenceArtifacts(repositoryId, workspaceRoot, options) {
7539
7822
  summary.importedAgents.push(artifact.targetName);
7540
7823
  continue;
7541
7824
  }
7542
- writeFileSync12(target, source, "utf8");
7825
+ writeFileSync13(target, source, "utf8");
7543
7826
  summary.importedAgents.push(artifact.targetName);
7544
7827
  } else {
7545
- const targetDir = join17(skillsDir, artifact.targetName);
7546
- const target = join17(targetDir, "SKILL.md");
7828
+ const targetDir = join18(skillsDir, artifact.targetName);
7829
+ const target = join18(targetDir, "SKILL.md");
7547
7830
  if (!options.overwrite && existsSync14(target)) {
7548
7831
  summary.skippedSkills.push(artifact.targetName);
7549
7832
  continue;
@@ -7553,7 +7836,7 @@ function importReferenceArtifacts(repositoryId, workspaceRoot, options) {
7553
7836
  continue;
7554
7837
  }
7555
7838
  mkdirSync8(targetDir, { recursive: true });
7556
- writeFileSync12(target, source, "utf8");
7839
+ writeFileSync13(target, source, "utf8");
7557
7840
  summary.importedSkills.push(artifact.targetName);
7558
7841
  }
7559
7842
  } catch (error) {
@@ -7939,4 +8222,4 @@ export {
7939
8222
  syncReferenceRepositories,
7940
8223
  importReferenceArtifacts
7941
8224
  };
7942
- //# sourceMappingURL=chunk-DWMY2HBG.js.map
8225
+ //# sourceMappingURL=chunk-LYAI5RPK.js.map