@dv.nghiem/flowdeck 0.2.2 → 0.2.4

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 (35) hide show
  1. package/dist/hooks/orchestrator-guard-hook.d.ts +2 -1
  2. package/dist/hooks/orchestrator-guard-hook.d.ts.map +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +209 -146
  5. package/dist/services/telemetry.d.ts +1 -1
  6. package/dist/services/telemetry.d.ts.map +1 -1
  7. package/dist/tools/run-parallel.d.ts.map +1 -1
  8. package/docs/configuration.md +2 -0
  9. package/docs/parallel-execution.md +28 -0
  10. package/docs/workflows.md +72 -320
  11. package/package.json +1 -2
  12. package/src/commands/fd-deploy-check.md +67 -32
  13. package/src/commands/fd-discuss.md +44 -6
  14. package/src/commands/fd-fix-bug.md +47 -20
  15. package/src/commands/fd-map-codebase.md +66 -18
  16. package/src/commands/fd-multi-repo.md +130 -6
  17. package/src/commands/fd-new-feature.md +164 -21
  18. package/src/commands/fd-plan.md +66 -44
  19. package/src/commands/fd-review-code.md +69 -35
  20. package/src/commands/fd-write-docs.md +55 -23
  21. package/src/workflows/debug-flow.md +0 -119
  22. package/src/workflows/deploy-check-flow.md +0 -98
  23. package/src/workflows/discuss-flow.md +0 -97
  24. package/src/workflows/execute-flow.md +0 -233
  25. package/src/workflows/execute-phase.md +0 -145
  26. package/src/workflows/fix-bug-flow.md +0 -210
  27. package/src/workflows/map-codebase-flow.md +0 -92
  28. package/src/workflows/multi-repo-flow.md +0 -226
  29. package/src/workflows/parallel-execution-flow.md +0 -236
  30. package/src/workflows/plan-flow.md +0 -126
  31. package/src/workflows/plan-phase.md +0 -101
  32. package/src/workflows/refactor-flow.md +0 -122
  33. package/src/workflows/review-code-flow.md +0 -105
  34. package/src/workflows/spec-driven-flow.md +0 -43
  35. package/src/workflows/write-docs-flow.md +0 -95
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import { readdirSync as readdirSync3, readFileSync as readFileSync22, existsSync as existsSync23 } from "fs";
3
- import { join as join22, basename } from "path";
3
+ import { join as join23, basename } from "path";
4
4
  import { dirname as dirname4 } from "path";
5
5
  import { fileURLToPath as fileURLToPath2 } from "url";
6
6
 
@@ -509,6 +509,33 @@ var workspaceStateTool = tool3({
509
509
 
510
510
  // src/tools/run-parallel.ts
511
511
  import { tool as tool4 } from "@opencode-ai/plugin";
512
+
513
+ // src/services/telemetry.ts
514
+ import { existsSync as existsSync5, readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync2 } from "fs";
515
+ import { join as join5 } from "path";
516
+ import { randomUUID } from "crypto";
517
+ function telemetryPath(dir) {
518
+ return join5(codebaseDir(dir), "TELEMETRY.jsonl");
519
+ }
520
+ function appendEvent(dir, partial) {
521
+ if (process.env.TELEMETRY_ENABLED !== "true")
522
+ return null;
523
+ const cd = codebaseDir(dir);
524
+ if (!existsSync5(cd))
525
+ mkdirSync2(cd, { recursive: true });
526
+ const event = {
527
+ id: randomUUID(),
528
+ ts: new Date().toISOString(),
529
+ ...partial
530
+ };
531
+ appendFileSync(telemetryPath(dir), JSON.stringify(event) + `
532
+ `, "utf-8");
533
+ return event;
534
+ }
535
+
536
+ // src/tools/run-parallel.ts
537
+ import { writeFileSync as writeFileSync5 } from "fs";
538
+ import { join as join6 } from "path";
512
539
  function extractText(parts) {
513
540
  return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join(`
514
541
  `);
@@ -534,6 +561,7 @@ function createRunParallelTool(client) {
534
561
  }).catch(() => {});
535
562
  }
536
563
  });
564
+ const dir = context.directory ?? process.cwd();
537
565
  const promises = args.tasks.map(async (task) => {
538
566
  const taskStart = Date.now();
539
567
  const createRes = await client.session.create({
@@ -550,6 +578,14 @@ function createRunParallelTool(client) {
550
578
  }
551
579
  const childId = createRes.data.id;
552
580
  childSessionIds.push(childId);
581
+ appendEvent(dir, {
582
+ session_id: context.sessionID,
583
+ run_id: process.env.OPENCODE_RUN_ID ?? "run-0",
584
+ event: "agent.dispatch",
585
+ agent: task.agent,
586
+ status: "ok",
587
+ meta: { child_session_id: childId, task_index: args.tasks.findIndex((t) => t.agent === task.agent) }
588
+ });
553
589
  const fullPrompt = task.context ? `${task.context}
554
590
 
555
591
  ---
@@ -565,6 +601,15 @@ ${task.prompt}` : task.prompt;
565
601
  query: { directory: context.directory }
566
602
  });
567
603
  if (promptRes.error) {
604
+ appendEvent(dir, {
605
+ session_id: context.sessionID,
606
+ run_id: process.env.OPENCODE_RUN_ID ?? "run-0",
607
+ event: "agent.complete",
608
+ agent: task.agent,
609
+ status: "error",
610
+ duration_ms: Date.now() - taskStart,
611
+ meta: { child_session_id: childId, error: `Prompt failed: ${promptRes.error?.detail ?? "unknown"}` }
612
+ });
568
613
  return {
569
614
  agent: task.agent,
570
615
  session_id: childId,
@@ -575,6 +620,15 @@ ${task.prompt}` : task.prompt;
575
620
  }
576
621
  const info = promptRes.data?.info;
577
622
  if (info?.error) {
623
+ appendEvent(dir, {
624
+ session_id: context.sessionID,
625
+ run_id: process.env.OPENCODE_RUN_ID ?? "run-0",
626
+ event: "agent.complete",
627
+ agent: task.agent,
628
+ status: "error",
629
+ duration_ms: Date.now() - taskStart,
630
+ meta: { child_session_id: childId, error: JSON.stringify(info.error) }
631
+ });
578
632
  return {
579
633
  agent: task.agent,
580
634
  session_id: childId,
@@ -584,6 +638,15 @@ ${task.prompt}` : task.prompt;
584
638
  };
585
639
  }
586
640
  const output = extractText(promptRes.data?.parts ?? []);
641
+ appendEvent(dir, {
642
+ session_id: context.sessionID,
643
+ run_id: process.env.OPENCODE_RUN_ID ?? "run-0",
644
+ event: "agent.complete",
645
+ agent: task.agent,
646
+ status: "ok",
647
+ duration_ms: Date.now() - taskStart,
648
+ meta: { child_session_id: childId, output_length: output?.length ?? 0 }
649
+ });
587
650
  return {
588
651
  agent: task.agent,
589
652
  session_id: childId,
@@ -603,6 +666,14 @@ ${task.prompt}` : task.prompt;
603
666
  duration_ms: Date.now() - startTime
604
667
  };
605
668
  });
669
+ const progress = {
670
+ total: args.tasks.length,
671
+ completed: results.filter((r) => r.success || r.error).length,
672
+ in_progress: childSessionIds.length,
673
+ results: results.map((r) => ({ agent: r.agent, success: r.success, duration_ms: r.duration_ms })),
674
+ total_duration_ms: Date.now() - startTime
675
+ };
676
+ writeFileSync5(join6(codebaseDir(dir), "parallel-progress.json"), JSON.stringify(progress, null, 2));
606
677
  return JSON.stringify({
607
678
  results,
608
679
  total_duration_ms: Date.now() - startTime,
@@ -798,31 +869,31 @@ ${args.prompt}` : args.prompt;
798
869
 
799
870
  // src/tools/repo-memory.ts
800
871
  import { tool as tool7 } from "@opencode-ai/plugin";
801
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
802
- import { join as join5 } from "path";
872
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
873
+ import { join as join7 } from "path";
803
874
  var MEMORY_FILE = "MEMORY.json";
804
875
  function memoryPath(directory) {
805
- return join5(codebaseDir(directory), MEMORY_FILE);
876
+ return join7(codebaseDir(directory), MEMORY_FILE);
806
877
  }
807
878
  function emptyMemory() {
808
879
  return { version: "1.0", last_updated: new Date().toISOString(), nodes: {} };
809
880
  }
810
881
  function readMemory(directory) {
811
882
  const p = memoryPath(directory);
812
- if (!existsSync5(p))
883
+ if (!existsSync6(p))
813
884
  return emptyMemory();
814
885
  try {
815
- return JSON.parse(readFileSync5(p, "utf-8"));
886
+ return JSON.parse(readFileSync6(p, "utf-8"));
816
887
  } catch {
817
888
  return emptyMemory();
818
889
  }
819
890
  }
820
891
  function writeMemory(directory, memory) {
821
892
  const base = codebaseDir(directory);
822
- if (!existsSync5(base))
823
- mkdirSync2(base, { recursive: true });
893
+ if (!existsSync6(base))
894
+ mkdirSync3(base, { recursive: true });
824
895
  memory.last_updated = new Date().toISOString();
825
- writeFileSync5(memoryPath(directory), JSON.stringify(memory, null, 2), "utf-8");
896
+ writeFileSync6(memoryPath(directory), JSON.stringify(memory, null, 2), "utf-8");
826
897
  }
827
898
  var repoMemoryTool = tool7({
828
899
  description: "Repo Memory Graph: read/write/query persistent architecture graph in .codebase/MEMORY.json (modules, dependencies, ownership, bug history, conventions)",
@@ -899,28 +970,28 @@ var repoMemoryTool = tool7({
899
970
 
900
971
  // src/tools/failure-replay.ts
901
972
  import { tool as tool8 } from "@opencode-ai/plugin";
902
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
903
- import { join as join6 } from "path";
973
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
974
+ import { join as join8 } from "path";
904
975
  var FAILURES_FILE = "FAILURES.json";
905
976
  function failuresPath(directory) {
906
- return join6(codebaseDir(directory), FAILURES_FILE);
977
+ return join8(codebaseDir(directory), FAILURES_FILE);
907
978
  }
908
979
  function readStore(directory) {
909
980
  const p = failuresPath(directory);
910
- if (!existsSync6(p))
981
+ if (!existsSync7(p))
911
982
  return { version: "1.0", last_updated: new Date().toISOString(), entries: [] };
912
983
  try {
913
- return JSON.parse(readFileSync6(p, "utf-8"));
984
+ return JSON.parse(readFileSync7(p, "utf-8"));
914
985
  } catch {
915
986
  return { version: "1.0", last_updated: new Date().toISOString(), entries: [] };
916
987
  }
917
988
  }
918
989
  function writeStore(directory, store) {
919
990
  const base = codebaseDir(directory);
920
- if (!existsSync6(base))
921
- mkdirSync3(base, { recursive: true });
991
+ if (!existsSync7(base))
992
+ mkdirSync4(base, { recursive: true });
922
993
  store.last_updated = new Date().toISOString();
923
- writeFileSync6(failuresPath(directory), JSON.stringify(store, null, 2), "utf-8");
994
+ writeFileSync7(failuresPath(directory), JSON.stringify(store, null, 2), "utf-8");
924
995
  }
925
996
  var failureReplayTool = tool8({
926
997
  description: "Failure Replay Engine: record and query past failures (reverted commits, failed deployments, flaky tests, bug fixes) in .codebase/FAILURES.json so the agent avoids repeating mistakes",
@@ -1004,17 +1075,17 @@ var failureReplayTool = tool8({
1004
1075
 
1005
1076
  // src/tools/decision-trace.ts
1006
1077
  import { tool as tool9 } from "@opencode-ai/plugin";
1007
- import { readFileSync as readFileSync7, existsSync as existsSync7, mkdirSync as mkdirSync4, appendFileSync } from "fs";
1008
- import { join as join7 } from "path";
1078
+ import { readFileSync as readFileSync8, existsSync as existsSync8, mkdirSync as mkdirSync5, appendFileSync as appendFileSync2 } from "fs";
1079
+ import { join as join9 } from "path";
1009
1080
  var DECISIONS_FILE = "DECISIONS.jsonl";
1010
1081
  function decisionsPath(directory) {
1011
- return join7(codebaseDir(directory), DECISIONS_FILE);
1082
+ return join9(codebaseDir(directory), DECISIONS_FILE);
1012
1083
  }
1013
1084
  function readDecisions(directory) {
1014
1085
  const p = decisionsPath(directory);
1015
- if (!existsSync7(p))
1086
+ if (!existsSync8(p))
1016
1087
  return [];
1017
- return readFileSync7(p, "utf-8").split(`
1088
+ return readFileSync8(p, "utf-8").split(`
1018
1089
  `).filter((l) => l.trim()).map((l) => {
1019
1090
  try {
1020
1091
  return JSON.parse(l);
@@ -1054,10 +1125,10 @@ var decisionTraceTool = tool9({
1054
1125
  case "record": {
1055
1126
  if (!args.entry)
1056
1127
  return JSON.stringify({ error: "entry required" });
1057
- if (!existsSync7(base))
1058
- mkdirSync4(base, { recursive: true });
1128
+ if (!existsSync8(base))
1129
+ mkdirSync5(base, { recursive: true });
1059
1130
  const entry = { ...args.entry, timestamp: new Date().toISOString() };
1060
- appendFileSync(decisionsPath(dir), JSON.stringify(entry) + `
1131
+ appendFileSync2(decisionsPath(dir), JSON.stringify(entry) + `
1061
1132
  `, "utf-8");
1062
1133
  return JSON.stringify({ success: true, id: args.entry.id });
1063
1134
  }
@@ -1089,28 +1160,28 @@ var decisionTraceTool = tool9({
1089
1160
 
1090
1161
  // src/tools/volatility-map.ts
1091
1162
  import { tool as tool10 } from "@opencode-ai/plugin";
1092
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
1093
- import { join as join8 } from "path";
1163
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
1164
+ import { join as join10 } from "path";
1094
1165
  var VOLATILITY_FILE = "VOLATILITY.json";
1095
1166
  function volatilityPath(directory) {
1096
- return join8(codebaseDir(directory), VOLATILITY_FILE);
1167
+ return join10(codebaseDir(directory), VOLATILITY_FILE);
1097
1168
  }
1098
1169
  function readStore2(directory) {
1099
1170
  const p = volatilityPath(directory);
1100
- if (!existsSync8(p))
1171
+ if (!existsSync9(p))
1101
1172
  return { version: "1.0", last_updated: new Date().toISOString(), generated_at: new Date().toISOString(), entries: [] };
1102
1173
  try {
1103
- return JSON.parse(readFileSync8(p, "utf-8"));
1174
+ return JSON.parse(readFileSync9(p, "utf-8"));
1104
1175
  } catch {
1105
1176
  return { version: "1.0", last_updated: new Date().toISOString(), generated_at: new Date().toISOString(), entries: [] };
1106
1177
  }
1107
1178
  }
1108
1179
  function writeStore2(directory, store) {
1109
1180
  const base = codebaseDir(directory);
1110
- if (!existsSync8(base))
1111
- mkdirSync5(base, { recursive: true });
1181
+ if (!existsSync9(base))
1182
+ mkdirSync6(base, { recursive: true });
1112
1183
  store.last_updated = new Date().toISOString();
1113
- writeFileSync8(volatilityPath(directory), JSON.stringify(store, null, 2), "utf-8");
1184
+ writeFileSync9(volatilityPath(directory), JSON.stringify(store, null, 2), "utf-8");
1114
1185
  }
1115
1186
  function stabilityLabel(churn, hotfixes, todos) {
1116
1187
  const score = churn + hotfixes * 10 + todos * 2;
@@ -1197,28 +1268,28 @@ var volatilityMapTool = tool10({
1197
1268
 
1198
1269
  // src/tools/policy-engine.ts
1199
1270
  import { tool as tool11 } from "@opencode-ai/plugin";
1200
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
1201
- import { join as join9 } from "path";
1271
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync10, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
1272
+ import { join as join11 } from "path";
1202
1273
  var POLICIES_FILE = "POLICIES.json";
1203
1274
  function policiesPath(directory) {
1204
- return join9(codebaseDir(directory), POLICIES_FILE);
1275
+ return join11(codebaseDir(directory), POLICIES_FILE);
1205
1276
  }
1206
1277
  function readStore3(directory) {
1207
1278
  const p = policiesPath(directory);
1208
- if (!existsSync9(p))
1279
+ if (!existsSync10(p))
1209
1280
  return { version: "1.0", last_updated: new Date().toISOString(), policies: [] };
1210
1281
  try {
1211
- return JSON.parse(readFileSync9(p, "utf-8"));
1282
+ return JSON.parse(readFileSync10(p, "utf-8"));
1212
1283
  } catch {
1213
1284
  return { version: "1.0", last_updated: new Date().toISOString(), policies: [] };
1214
1285
  }
1215
1286
  }
1216
1287
  function writeStore3(directory, store) {
1217
1288
  const base = codebaseDir(directory);
1218
- if (!existsSync9(base))
1219
- mkdirSync6(base, { recursive: true });
1289
+ if (!existsSync10(base))
1290
+ mkdirSync7(base, { recursive: true });
1220
1291
  store.last_updated = new Date().toISOString();
1221
- writeFileSync9(policiesPath(directory), JSON.stringify(store, null, 2), "utf-8");
1292
+ writeFileSync10(policiesPath(directory), JSON.stringify(store, null, 2), "utf-8");
1222
1293
  }
1223
1294
  var policyEngineTool = tool11({
1224
1295
  description: "Self-Healing Policy Engine: manage .codebase/POLICIES.json — add, list, query, toggle, and record violations of editing policies learned from past failures",
@@ -1300,7 +1371,7 @@ var policyEngineTool = tool11({
1300
1371
 
1301
1372
  // src/tools/hash-edit.ts
1302
1373
  import { tool as tool12 } from "@opencode-ai/plugin";
1303
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "fs";
1374
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync11 } from "fs";
1304
1375
  import { createHash } from "crypto";
1305
1376
  var hashEditTool = tool12({
1306
1377
  description: "Reliable file editing with content verification. Takes a target content, its expected MD5 hash, and replacement content. Only applies if the hash matches, preventing edits on stale file versions.",
@@ -1314,7 +1385,7 @@ var hashEditTool = tool12({
1314
1385
  const fullPath = args.filePath.startsWith("/") ? args.filePath : `${context.directory}/${args.filePath}`;
1315
1386
  let content;
1316
1387
  try {
1317
- content = readFileSync10(fullPath, "utf-8");
1388
+ content = readFileSync11(fullPath, "utf-8");
1318
1389
  } catch (e) {
1319
1390
  return `Error: Could not read file ${args.filePath}`;
1320
1391
  }
@@ -1328,7 +1399,7 @@ var hashEditTool = tool12({
1328
1399
  }
1329
1400
  }
1330
1401
  const newContent = content.replace(args.targetContent, args.replacementContent);
1331
- writeFileSync10(fullPath, newContent, "utf-8");
1402
+ writeFileSync11(fullPath, newContent, "utf-8");
1332
1403
  return `Successfully updated ${args.filePath} using hash-anchored edit.`;
1333
1404
  }
1334
1405
  });
@@ -1397,8 +1468,8 @@ Please synthesize these results. Identify areas of agreement, resolve conflicts,
1397
1468
 
1398
1469
  // src/tools/context-generator.ts
1399
1470
  import { tool as tool14 } from "@opencode-ai/plugin";
1400
- import { writeFileSync as writeFileSync11, existsSync as existsSync10, readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
1401
- import { join as join10 } from "path";
1471
+ import { writeFileSync as writeFileSync12, existsSync as existsSync11, readFileSync as readFileSync12, readdirSync as readdirSync2, statSync } from "fs";
1472
+ import { join as join12 } from "path";
1402
1473
  var contextGeneratorTool = tool14({
1403
1474
  description: "Auto-generate or update hierarchical context files (AGENTS.md, CLAUDE.md) throughout the project. These files provide critical grounding for AI agents.",
1404
1475
  args: {
@@ -1407,20 +1478,20 @@ var contextGeneratorTool = tool14({
1407
1478
  },
1408
1479
  async execute(args, context) {
1409
1480
  const root = context.directory;
1410
- const target = args.targetDir ? join10(root, args.targetDir) : root;
1411
- if (!existsSync10(target)) {
1481
+ const target = args.targetDir ? join12(root, args.targetDir) : root;
1482
+ if (!existsSync11(target)) {
1412
1483
  return `Error: Directory ${target} does not exist.`;
1413
1484
  }
1414
- const agentsMdPath = join10(target, "AGENTS.md");
1415
- if (existsSync10(agentsMdPath) && !args.force) {
1485
+ const agentsMdPath = join12(target, "AGENTS.md");
1486
+ if (existsSync11(agentsMdPath) && !args.force) {
1416
1487
  return `AGENTS.md already exists in ${target}. Use force: true to overwrite.`;
1417
1488
  }
1418
- const pkgPath = join10(root, "package.json");
1489
+ const pkgPath = join12(root, "package.json");
1419
1490
  let projectName = "Project";
1420
1491
  let techStack = "Unknown";
1421
- if (existsSync10(pkgPath)) {
1492
+ if (existsSync11(pkgPath)) {
1422
1493
  try {
1423
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
1494
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
1424
1495
  projectName = pkg.name || projectName;
1425
1496
  techStack = Object.keys(pkg.dependencies || {}).slice(0, 5).join(", ");
1426
1497
  } catch {}
@@ -1438,7 +1509,7 @@ var contextGeneratorTool = tool14({
1438
1509
 
1439
1510
  ## Directory Map
1440
1511
  ${readdirSync2(target).slice(0, 10).map((f) => {
1441
- const s = statSync(join10(target, f));
1512
+ const s = statSync(join12(target, f));
1442
1513
  return `- \`${f}\`${s.isDirectory() ? "/" : ""} : [Description]`;
1443
1514
  }).join(`
1444
1515
  `)}
@@ -1446,17 +1517,17 @@ ${readdirSync2(target).slice(0, 10).map((f) => {
1446
1517
  ---
1447
1518
  Generated by FlowDeck Context Generator.
1448
1519
  `;
1449
- writeFileSync11(agentsMdPath, content, "utf-8");
1520
+ writeFileSync12(agentsMdPath, content, "utf-8");
1450
1521
  return `Successfully generated AGENTS.md in ${target}.`;
1451
1522
  }
1452
1523
  });
1453
1524
 
1454
1525
  // src/tools/create-skill.ts
1455
1526
  import { tool as tool15 } from "@opencode-ai/plugin";
1456
- import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync12, existsSync as existsSync11 } from "fs";
1457
- import { join as join11, dirname as dirname3 } from "path";
1527
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync13, existsSync as existsSync12 } from "fs";
1528
+ import { join as join13, dirname as dirname3 } from "path";
1458
1529
  import { fileURLToPath } from "url";
1459
- var SKILLS_DIR = join11(dirname3(fileURLToPath(import.meta.url)), "..", "skills");
1530
+ var SKILLS_DIR = join13(dirname3(fileURLToPath(import.meta.url)), "..", "skills");
1460
1531
  var createSkillTool = tool15({
1461
1532
  description: "Create a new reusable skill in the FlowDeck skill library (src/skills/). " + "Use this when you discover a repeatable pattern, solve a novel problem with human guidance, " + "or want to capture domain knowledge for future sessions.",
1462
1533
  args: {
@@ -1466,9 +1537,9 @@ var createSkillTool = tool15({
1466
1537
  tags: tool15.schema.array(tool15.schema.string()).optional().describe("Optional tags for categorisation, e.g. ['performance', 'typescript']")
1467
1538
  },
1468
1539
  async execute(args) {
1469
- const skillDir = join11(SKILLS_DIR, args.name);
1470
- const skillFile = join11(skillDir, "SKILL.md");
1471
- if (existsSync11(skillFile)) {
1540
+ const skillDir = join13(SKILLS_DIR, args.name);
1541
+ const skillFile = join13(skillDir, "SKILL.md");
1542
+ if (existsSync12(skillFile)) {
1472
1543
  return `Skill '${args.name}' already exists at ${skillFile}.
1473
1544
  ` + `Use a different name or delete the existing skill directory first.`;
1474
1545
  }
@@ -1483,8 +1554,8 @@ origin: FlowDeck (self-learned)${tagLine}
1483
1554
  `;
1484
1555
  const fullContent = frontmatter + args.content.trimStart();
1485
1556
  try {
1486
- mkdirSync7(skillDir, { recursive: true });
1487
- writeFileSync12(skillFile, fullContent, "utf-8");
1557
+ mkdirSync8(skillDir, { recursive: true });
1558
+ writeFileSync13(skillFile, fullContent, "utf-8");
1488
1559
  return `✓ Skill '${args.name}' created at ${skillFile}
1489
1560
 
1490
1561
  ` + `The skill is now part of the FlowDeck library. Restart OpenCode to load it into the active session.`;
@@ -1496,8 +1567,8 @@ origin: FlowDeck (self-learned)${tagLine}
1496
1567
 
1497
1568
  // src/tools/reflect.ts
1498
1569
  import { tool as tool16 } from "@opencode-ai/plugin";
1499
- import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
1500
- import { join as join12 } from "path";
1570
+ import { existsSync as existsSync13, readFileSync as readFileSync13 } from "fs";
1571
+ import { join as join14 } from "path";
1501
1572
  var MAX_ARTIFACT_BYTES = 4000;
1502
1573
  function tail(text, maxBytes) {
1503
1574
  if (text.length <= maxBytes)
@@ -1526,11 +1597,11 @@ var reflectTool = tool16({
1526
1597
  ];
1527
1598
  let found = 0;
1528
1599
  for (const [rel, label] of ARTIFACT_PATHS) {
1529
- const full = join12(root, rel);
1530
- if (!existsSync12(full))
1600
+ const full = join14(root, rel);
1601
+ if (!existsSync13(full))
1531
1602
  continue;
1532
1603
  try {
1533
- const raw = readFileSync12(full, "utf-8").trim();
1604
+ const raw = readFileSync13(full, "utf-8").trim();
1534
1605
  if (!raw)
1535
1606
  continue;
1536
1607
  const count = raw.split(`
@@ -1550,15 +1621,15 @@ var reflectTool = tool16({
1550
1621
  });
1551
1622
 
1552
1623
  // src/hooks/guard-rails.ts
1553
- import { existsSync as existsSync13, readFileSync as readFileSync13 } from "fs";
1554
- import { join as join13 } from "path";
1624
+ import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
1625
+ import { join as join15 } from "path";
1555
1626
  var PLANNING_DIR2 = ".planning";
1556
1627
  var CONFIG_FILE = "config.json";
1557
1628
  var STATE_FILE2 = "STATE.md";
1558
1629
  function resolveExecutionMode(configPath, trustScore, volatility) {
1559
- if (existsSync13(configPath)) {
1630
+ if (existsSync14(configPath)) {
1560
1631
  try {
1561
- const config = JSON.parse(readFileSync13(configPath, "utf-8"));
1632
+ const config = JSON.parse(readFileSync14(configPath, "utf-8"));
1562
1633
  if (config.execution_mode === "review-only")
1563
1634
  return "review-only";
1564
1635
  if (config.execution_mode === "guarded")
@@ -1609,22 +1680,22 @@ var BUILD_DEPLOY_PATTERNS = [
1609
1680
  ];
1610
1681
  async function guardRailsHook(ctx, input, _output) {
1611
1682
  const dir = ctx.directory;
1612
- const planningDirPath = join13(dir, PLANNING_DIR2);
1683
+ const planningDirPath = join15(dir, PLANNING_DIR2);
1613
1684
  const codebaseDirectory = codebaseDir(dir);
1614
- const configPath = join13(planningDirPath, CONFIG_FILE);
1615
- const statePath2 = join13(planningDirPath, STATE_FILE2);
1685
+ const configPath = join15(planningDirPath, CONFIG_FILE);
1686
+ const statePath2 = join15(planningDirPath, STATE_FILE2);
1616
1687
  const workspaceRoot = findWorkspaceRoot(dir);
1617
1688
  if (workspaceRoot && dir !== workspaceRoot) {
1618
1689
  const config = getWorkspaceConfig(dir);
1619
- if (config && config.workspace_mode === "shared" && !existsSync13(planningDirPath)) {
1690
+ if (config && config.workspace_mode === "shared" && !existsSync14(planningDirPath)) {
1620
1691
  const msg = `No .planning/ in this sub-repo. Switch to workspace root: cd ${workspaceRoot}`;
1621
1692
  throw new Error(`[flowdeck] BLOCK: ${msg}`);
1622
1693
  }
1623
1694
  }
1624
1695
  if (input.tool === "write" || input.tool === "edit") {
1625
- if (!existsSync13(planningDirPath))
1696
+ if (!existsSync14(planningDirPath))
1626
1697
  return;
1627
- if (!existsSync13(codebaseDirectory)) {
1698
+ if (!existsSync14(codebaseDirectory)) {
1628
1699
  throw new Error(`[flowdeck] WARNING: .codebase/ not found. Run /map-codebase to map the codebase.`);
1629
1700
  }
1630
1701
  const execMode = resolveExecutionMode(configPath, null);
@@ -1659,9 +1730,9 @@ async function guardRailsHook(ctx, input, _output) {
1659
1730
  }
1660
1731
  }
1661
1732
  function effectiveSeverity(configPath, statePath2) {
1662
- if (existsSync13(configPath)) {
1733
+ if (existsSync14(configPath)) {
1663
1734
  try {
1664
- const configContent = readFileSync13(configPath, "utf-8");
1735
+ const configContent = readFileSync14(configPath, "utf-8");
1665
1736
  const config = JSON.parse(configContent);
1666
1737
  if (config.guard_enforcement === "warn")
1667
1738
  return "warn";
@@ -1677,10 +1748,10 @@ function getEffectiveSeverity(configPath, statePath2) {
1677
1748
  return effectiveSeverity(configPath, statePath2);
1678
1749
  }
1679
1750
  function getPlanConfirmed(statePath2) {
1680
- if (!existsSync13(statePath2))
1751
+ if (!existsSync14(statePath2))
1681
1752
  return false;
1682
1753
  try {
1683
- const content = readFileSync13(statePath2, "utf-8");
1754
+ const content = readFileSync14(statePath2, "utf-8");
1684
1755
  const match = content.match(/plan_confirmed:\s*(true|false)/i);
1685
1756
  return match ? match[1].toLowerCase() === "true" : false;
1686
1757
  } catch {
@@ -1688,21 +1759,21 @@ function getPlanConfirmed(statePath2) {
1688
1759
  }
1689
1760
  }
1690
1761
  function getWarningMessage(statePath2, planningDir3) {
1691
- if (!existsSync13(join13(planningDir3, STATE_FILE2))) {
1762
+ if (!existsSync14(join15(planningDir3, STATE_FILE2))) {
1692
1763
  return "No .planning/ found. Run /new-project first.";
1693
1764
  }
1694
1765
  return "Plan not confirmed. Run /plan and confirm to enable execution.";
1695
1766
  }
1696
1767
  function getBlockMessage(statePath2, planningDir3) {
1697
- if (!existsSync13(join13(planningDir3, STATE_FILE2))) {
1768
+ if (!existsSync14(join15(planningDir3, STATE_FILE2))) {
1698
1769
  return "No .planning/ found. Run /new-project first.";
1699
1770
  }
1700
1771
  return "Plan not confirmed. Run /plan and confirm to enable execution.";
1701
1772
  }
1702
1773
 
1703
1774
  // src/hooks/tool-guard.ts
1704
- import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
1705
- import { join as join14 } from "path";
1775
+ import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
1776
+ import { join as join16 } from "path";
1706
1777
  var BLOCKED_PATTERNS = {
1707
1778
  read: [".env", ".pem", ".key", ".secret"],
1708
1779
  write: ["node_modules"],
@@ -1748,11 +1819,11 @@ function isBlocked(tool17, args) {
1748
1819
  return null;
1749
1820
  }
1750
1821
  function checkArchConstraint(directory, filePath) {
1751
- const constraintsPath = join14(codebaseDir(directory), "CONSTRAINTS.md");
1752
- if (!existsSync14(constraintsPath))
1822
+ const constraintsPath = join16(codebaseDir(directory), "CONSTRAINTS.md");
1823
+ if (!existsSync15(constraintsPath))
1753
1824
  return null;
1754
1825
  try {
1755
- const content = readFileSync14(constraintsPath, "utf-8");
1826
+ const content = readFileSync15(constraintsPath, "utf-8");
1756
1827
  const match = content.match(/## Forbidden Paths\n([\s\S]*?)(?:\n##|$)/);
1757
1828
  if (!match)
1758
1829
  return null;
@@ -1797,18 +1868,18 @@ async function toolGuardHook(ctx, input, output) {
1797
1868
  }
1798
1869
 
1799
1870
  // src/hooks/session-start.ts
1800
- import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
1871
+ import { existsSync as existsSync16, readFileSync as readFileSync16 } from "fs";
1801
1872
  async function sessionStartHook(ctx) {
1802
1873
  const planningDir3 = ctx.directory + "/.planning";
1803
1874
  const codebaseDirectory = codebaseDir(ctx.directory);
1804
1875
  const workspaceRoot = findWorkspaceRoot(ctx.directory);
1805
1876
  const config = workspaceRoot ? getWorkspaceConfig(ctx.directory) : null;
1806
- if (!existsSync15(planningDir3)) {
1877
+ if (!existsSync16(planningDir3)) {
1807
1878
  return {
1808
1879
  flowdeck_phase: null,
1809
1880
  flowdeck_status: "no_plan",
1810
1881
  flowdeck_warning: "Run /new-project or /map-codebase to initialize.",
1811
- flowdeck_has_codebase: existsSync15(codebaseDirectory),
1882
+ flowdeck_has_codebase: existsSync16(codebaseDirectory),
1812
1883
  ...workspaceRoot && config?.sub_repos ? {
1813
1884
  flowdeck_workspace_root: workspaceRoot,
1814
1885
  flowdeck_sub_repos: config.sub_repos,
@@ -1819,7 +1890,7 @@ async function sessionStartHook(ctx) {
1819
1890
  }
1820
1891
  try {
1821
1892
  const stateFilePath = statePath(ctx.directory);
1822
- const content = readFileSync15(stateFilePath, "utf-8");
1893
+ const content = readFileSync16(stateFilePath, "utf-8");
1823
1894
  const state = parseState(content);
1824
1895
  const currentPhase = state["current_phase"] || {};
1825
1896
  const result = {
@@ -1827,7 +1898,7 @@ async function sessionStartHook(ctx) {
1827
1898
  flowdeck_status: currentPhase["status"] ?? null,
1828
1899
  flowdeck_steps_pending: currentPhase["steps_pending"] ?? null,
1829
1900
  flowdeck_last_action: currentPhase["last_action"] ?? null,
1830
- flowdeck_has_codebase: existsSync15(codebaseDirectory)
1901
+ flowdeck_has_codebase: existsSync16(codebaseDirectory)
1831
1902
  };
1832
1903
  if (workspaceRoot && config?.sub_repos && config.sub_repos.length > 0) {
1833
1904
  result.flowdeck_workspace_root = workspaceRoot;
@@ -1842,7 +1913,7 @@ async function sessionStartHook(ctx) {
1842
1913
  flowdeck_phase: null,
1843
1914
  flowdeck_status: "error",
1844
1915
  flowdeck_warning: "State file unreadable. Continuing without flowdeck context.",
1845
- flowdeck_has_codebase: existsSync15(codebaseDirectory)
1916
+ flowdeck_has_codebase: existsSync16(codebaseDirectory)
1846
1917
  };
1847
1918
  if (workspaceRoot && config?.sub_repos && config.sub_repos.length > 0) {
1848
1919
  result.flowdeck_workspace_root = workspaceRoot;
@@ -1908,8 +1979,8 @@ function notifyPermissionNeeded(tool17) {
1908
1979
  }
1909
1980
 
1910
1981
  // src/hooks/patch-trust.ts
1911
- import { existsSync as existsSync16, readFileSync as readFileSync16 } from "fs";
1912
- import { join as join15 } from "path";
1982
+ import { existsSync as existsSync17, readFileSync as readFileSync17 } from "fs";
1983
+ import { join as join17 } from "path";
1913
1984
  var HIGH_RISK_KEYWORDS = [
1914
1985
  "password",
1915
1986
  "secret",
@@ -1931,11 +2002,11 @@ var HIGH_RISK_KEYWORDS = [
1931
2002
  "privilege"
1932
2003
  ];
1933
2004
  function loadVolatility(directory) {
1934
- const p = join15(codebaseDir(directory), "VOLATILITY.json");
1935
- if (!existsSync16(p))
2005
+ const p = join17(codebaseDir(directory), "VOLATILITY.json");
2006
+ if (!existsSync17(p))
1936
2007
  return {};
1937
2008
  try {
1938
- const data = JSON.parse(readFileSync16(p, "utf-8"));
2009
+ const data = JSON.parse(readFileSync17(p, "utf-8"));
1939
2010
  const map = {};
1940
2011
  for (const entry of data.entries ?? [])
1941
2012
  map[entry.path] = entry.stability;
@@ -1945,11 +2016,11 @@ function loadVolatility(directory) {
1945
2016
  }
1946
2017
  }
1947
2018
  function loadFailedPaths(directory) {
1948
- const p = join15(codebaseDir(directory), "FAILURES.json");
1949
- if (!existsSync16(p))
2019
+ const p = join17(codebaseDir(directory), "FAILURES.json");
2020
+ if (!existsSync17(p))
1950
2021
  return [];
1951
2022
  try {
1952
- const data = JSON.parse(readFileSync16(p, "utf-8"));
2023
+ const data = JSON.parse(readFileSync17(p, "utf-8"));
1953
2024
  return (data.entries ?? []).flatMap((e) => e.affected_paths ?? []);
1954
2025
  } catch {
1955
2026
  return [];
@@ -2006,8 +2077,8 @@ async function patchTrustHook(ctx, input, output) {
2006
2077
  }
2007
2078
 
2008
2079
  // src/hooks/decision-trace-hook.ts
2009
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2 } from "fs";
2010
- import { join as join16 } from "path";
2080
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, appendFileSync as appendFileSync3 } from "fs";
2081
+ import { join as join18 } from "path";
2011
2082
  async function decisionTraceHook(ctx, input, output) {
2012
2083
  if (input.tool !== "write" && input.tool !== "edit")
2013
2084
  return;
@@ -2016,8 +2087,8 @@ async function decisionTraceHook(ctx, input, output) {
2016
2087
  return;
2017
2088
  const base = codebaseDir(ctx.directory);
2018
2089
  try {
2019
- if (!existsSync17(base))
2020
- mkdirSync8(base, { recursive: true });
2090
+ if (!existsSync18(base))
2091
+ mkdirSync9(base, { recursive: true });
2021
2092
  const entry = {
2022
2093
  timestamp: new Date().toISOString(),
2023
2094
  file_path: filePath,
@@ -2029,32 +2100,11 @@ async function decisionTraceHook(ctx, input, output) {
2029
2100
  risk_level: "unknown",
2030
2101
  auto_recorded: true
2031
2102
  };
2032
- appendFileSync2(join16(base, "DECISIONS.jsonl"), JSON.stringify(entry) + `
2103
+ appendFileSync3(join18(base, "DECISIONS.jsonl"), JSON.stringify(entry) + `
2033
2104
  `, "utf-8");
2034
2105
  } catch {}
2035
2106
  }
2036
2107
 
2037
- // src/services/telemetry.ts
2038
- import { existsSync as existsSync18, readFileSync as readFileSync17, appendFileSync as appendFileSync3, mkdirSync as mkdirSync9 } from "fs";
2039
- import { join as join17 } from "path";
2040
- import { randomUUID } from "crypto";
2041
- function telemetryPath(dir) {
2042
- return join17(codebaseDir(dir), "TELEMETRY.jsonl");
2043
- }
2044
- function appendEvent(dir, partial) {
2045
- const cd = codebaseDir(dir);
2046
- if (!existsSync18(cd))
2047
- mkdirSync9(cd, { recursive: true });
2048
- const event = {
2049
- id: randomUUID(),
2050
- ts: new Date().toISOString(),
2051
- ...partial
2052
- };
2053
- appendFileSync3(telemetryPath(dir), JSON.stringify(event) + `
2054
- `, "utf-8");
2055
- return event;
2056
- }
2057
-
2058
2108
  // src/hooks/telemetry-hook.ts
2059
2109
  async function telemetryHook(context, toolInput, output) {
2060
2110
  const dir = context.directory ?? process.cwd();
@@ -2081,8 +2131,8 @@ async function telemetryAfterHook(context, toolInput, _output) {
2081
2131
  }
2082
2132
 
2083
2133
  // src/services/approval-manager.ts
2084
- import { existsSync as existsSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync13, mkdirSync as mkdirSync10 } from "fs";
2085
- import { join as join18 } from "path";
2134
+ import { existsSync as existsSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync14, mkdirSync as mkdirSync10 } from "fs";
2135
+ import { join as join19 } from "path";
2086
2136
  var APPROVAL_TTL_MS = 30 * 60 * 1000;
2087
2137
  var SENSITIVE_PATTERNS = [
2088
2138
  /auth/i,
@@ -2119,7 +2169,7 @@ function isSensitivePath(filePath) {
2119
2169
  return SENSITIVE_PATTERNS.some((p) => p.test(filePath));
2120
2170
  }
2121
2171
  function approvalsPath(dir) {
2122
- return join18(codebaseDir(dir), "APPROVALS.json");
2172
+ return join19(codebaseDir(dir), "APPROVALS.json");
2123
2173
  }
2124
2174
  function loadStore(dir) {
2125
2175
  const p = approvalsPath(dir);
@@ -2219,7 +2269,7 @@ function createContextWindowMonitorHook() {
2219
2269
 
2220
2270
  // src/hooks/shell-env-hook.ts
2221
2271
  import { existsSync as existsSync20, readFileSync as readFileSync19 } from "fs";
2222
- import { join as join19 } from "path";
2272
+ import { join as join20 } from "path";
2223
2273
  import { createRequire } from "module";
2224
2274
  var _version;
2225
2275
  function getVersion() {
@@ -2255,7 +2305,7 @@ var MARKER_TO_LANG = {
2255
2305
  };
2256
2306
  function detectPackageManager(root) {
2257
2307
  for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {
2258
- if (existsSync20(join19(root, lockfile)))
2308
+ if (existsSync20(join20(root, lockfile)))
2259
2309
  return pm;
2260
2310
  }
2261
2311
  return;
@@ -2264,7 +2314,7 @@ function detectLanguages(root) {
2264
2314
  const langs = [];
2265
2315
  const seen = new Set;
2266
2316
  for (const [marker, lang] of Object.entries(MARKER_TO_LANG)) {
2267
- if (!seen.has(lang) && existsSync20(join19(root, marker))) {
2317
+ if (!seen.has(lang) && existsSync20(join20(root, marker))) {
2268
2318
  langs.push(lang);
2269
2319
  seen.add(lang);
2270
2320
  }
@@ -2272,7 +2322,7 @@ function detectLanguages(root) {
2272
2322
  return langs;
2273
2323
  }
2274
2324
  function readCurrentPhase(root) {
2275
- const statePath2 = join19(root, ".planning", "STATE.md");
2325
+ const statePath2 = join20(root, ".planning", "STATE.md");
2276
2326
  if (!existsSync20(statePath2))
2277
2327
  return;
2278
2328
  try {
@@ -2376,7 +2426,7 @@ function createSessionIdleHook(client, tracker) {
2376
2426
 
2377
2427
  // src/hooks/compaction-hook.ts
2378
2428
  import { existsSync as existsSync21, readFileSync as readFileSync20 } from "fs";
2379
- import { join as join20 } from "path";
2429
+ import { join as join21 } from "path";
2380
2430
  var STRUCTURED_SUMMARY_PROMPT = `
2381
2431
  When summarizing this session, you MUST include the following sections:
2382
2432
 
@@ -2415,7 +2465,7 @@ For each: agent name, status, description, session_id.
2415
2465
  **RESUME, DON'T RESTART.** Use session_id to continue existing sessions.
2416
2466
  `;
2417
2467
  function readPlanningState2(directory) {
2418
- const statePath2 = join20(directory, ".planning", "STATE.md");
2468
+ const statePath2 = join21(directory, ".planning", "STATE.md");
2419
2469
  if (!existsSync21(statePath2))
2420
2470
  return null;
2421
2471
  try {
@@ -2453,7 +2503,7 @@ function createCompactionHook(ctx, tracker) {
2453
2503
  }
2454
2504
 
2455
2505
  // src/hooks/orchestrator-guard-hook.ts
2456
- var DISABLED = process.env.FLOWDECK_ORCHESTRATOR_GUARD === "off";
2506
+ var DISABLED = process.env.FLOWDECK_ORCHESTRATOR_GUARD !== "on";
2457
2507
  var BLOCKED_TOOLS = new Set([
2458
2508
  "write_file",
2459
2509
  "write",
@@ -2509,7 +2559,7 @@ function blockMessage(toolName) {
2509
2559
  ` + ` delegate({ agent: "@researcher", prompt: "..." }) — research / file analysis
2510
2560
  ` + ` delegate({ agent: "@tester", prompt: "..." }) — tests / commands
2511
2561
 
2512
- ` + `To disable this guard: set FLOWDECK_ORCHESTRATOR_GUARD=off`;
2562
+ ` + `To enable this guard: set FLOWDECK_ORCHESTRATOR_GUARD=on`;
2513
2563
  }
2514
2564
 
2515
2565
  class OrchestratorGuard {
@@ -5514,18 +5564,18 @@ function getAgentConfigs(agentModels) {
5514
5564
 
5515
5565
  // src/config/loader.ts
5516
5566
  import { existsSync as existsSync22, readFileSync as readFileSync21 } from "fs";
5517
- import { join as join21 } from "path";
5567
+ import { join as join22 } from "path";
5518
5568
  import { homedir } from "os";
5519
5569
  var CONFIG_FILENAME = "flowdeck.json";
5520
5570
  function getGlobalConfigDir() {
5521
- return process.env.OPENCODE_CONFIG_DIR || (process.env.XDG_CONFIG_HOME ? join21(process.env.XDG_CONFIG_HOME, "opencode") : join21(homedir(), ".config", "opencode"));
5571
+ return process.env.OPENCODE_CONFIG_DIR || (process.env.XDG_CONFIG_HOME ? join22(process.env.XDG_CONFIG_HOME, "opencode") : join22(homedir(), ".config", "opencode"));
5522
5572
  }
5523
5573
  function loadFlowDeckConfig(directory) {
5524
5574
  const candidates = [];
5525
5575
  if (directory) {
5526
- candidates.push(join21(directory, ".opencode", CONFIG_FILENAME));
5576
+ candidates.push(join22(directory, ".opencode", CONFIG_FILENAME));
5527
5577
  }
5528
- candidates.push(join21(getGlobalConfigDir(), CONFIG_FILENAME));
5578
+ candidates.push(join22(getGlobalConfigDir(), CONFIG_FILENAME));
5529
5579
  for (const configPath of candidates) {
5530
5580
  if (existsSync22(configPath)) {
5531
5581
  try {
@@ -5541,7 +5591,7 @@ function loadFlowDeckConfig(directory) {
5541
5591
  // src/index.ts
5542
5592
  function loadCommands() {
5543
5593
  const __dir = dirname4(fileURLToPath2(import.meta.url));
5544
- const commandsDir = join22(__dir, "..", "src", "commands");
5594
+ const commandsDir = join23(__dir, "..", "src", "commands");
5545
5595
  if (!existsSync23(commandsDir))
5546
5596
  return {};
5547
5597
  const commands = {};
@@ -5550,7 +5600,7 @@ function loadCommands() {
5550
5600
  if (!file.endsWith(".md"))
5551
5601
  continue;
5552
5602
  const name = basename(file, ".md");
5553
- const raw = readFileSync22(join22(commandsDir, file), "utf-8");
5603
+ const raw = readFileSync22(join23(commandsDir, file), "utf-8");
5554
5604
  let description;
5555
5605
  let template = raw;
5556
5606
  const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
@@ -5628,6 +5678,19 @@ var plugin = async (input, _options) => {
5628
5678
  }
5629
5679
  }
5630
5680
  }
5681
+ const skillsDir = join23(dirname4(fileURLToPath2(import.meta.url)), "..", "src", "skills");
5682
+ if (existsSync23(skillsDir)) {
5683
+ const cfgAny = cfg;
5684
+ if (!cfgAny.skills || typeof cfgAny.skills !== "object") {
5685
+ cfgAny.skills = { paths: [] };
5686
+ }
5687
+ const cfgSkills = cfgAny.skills;
5688
+ if (!cfgSkills.paths)
5689
+ cfgSkills.paths = [];
5690
+ if (!cfgSkills.paths.includes(skillsDir)) {
5691
+ cfgSkills.paths.push(skillsDir);
5692
+ }
5693
+ }
5631
5694
  },
5632
5695
  tool: {
5633
5696
  "planning-state": planningStateTool,