@dv.nghiem/flowdeck 0.3.3 → 0.3.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 (33) hide show
  1. package/README.md +7 -6
  2. package/dist/hooks/orchestrator-guard-hook.d.ts +4 -1
  3. package/dist/hooks/orchestrator-guard-hook.d.ts.map +1 -1
  4. package/dist/hooks/session-idle-hook.d.ts.map +1 -1
  5. package/dist/hooks/telemetry-hook.d.ts +14 -1
  6. package/dist/hooks/telemetry-hook.d.ts.map +1 -1
  7. package/dist/hooks/telemetry-hook.test.d.ts +2 -0
  8. package/dist/hooks/telemetry-hook.test.d.ts.map +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +466 -202
  11. package/dist/tools/council.d.ts.map +1 -1
  12. package/dist/tools/delegate.d.ts.map +1 -1
  13. package/dist/tools/dispatch-routing.d.ts +6 -0
  14. package/dist/tools/dispatch-routing.d.ts.map +1 -0
  15. package/dist/tools/run-pipeline.d.ts.map +1 -1
  16. package/docs/installation.md +6 -17
  17. package/docs/intelligence.md +18 -33
  18. package/docs/optimization-baseline.md +21 -0
  19. package/docs/rules.md +9 -36
  20. package/docs/workflows.md +8 -9
  21. package/package.json +4 -2
  22. package/src/rules/README.md +10 -0
  23. package/src/rules/common/coding-style.md +2 -2
  24. package/src/rules/typescript/patterns.md +1 -1
  25. package/src/skills/backend-patterns/SKILL.md +6 -0
  26. package/src/skills/clean-architecture/SKILL.md +6 -0
  27. package/src/skills/cqrs/SKILL.md +6 -0
  28. package/src/skills/ddd-architecture/SKILL.md +6 -0
  29. package/src/skills/event-driven-architecture/SKILL.md +6 -0
  30. package/src/skills/hexagonal-architecture/SKILL.md +6 -0
  31. package/src/skills/layered-architecture/SKILL.md +6 -0
  32. package/src/skills/postgres-patterns/SKILL.md +6 -0
  33. package/src/skills/saga-architecture/SKILL.md +6 -0
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
- import { readdirSync as readdirSync3, readFileSync as readFileSync22, existsSync as existsSync25 } from "fs";
3
- import { join as join24, basename } from "path";
2
+ import { readdirSync as readdirSync3, readFileSync as readFileSync24, existsSync as existsSync28 } from "fs";
3
+ import { join as join27, basename } from "path";
4
4
  import { dirname as dirname4 } from "path";
5
5
  import { fileURLToPath as fileURLToPath2 } from "url";
6
6
 
@@ -509,6 +509,154 @@ var workspaceStateTool = tool3({
509
509
 
510
510
  // src/tools/run-pipeline.ts
511
511
  import { tool as tool4 } from "@opencode-ai/plugin";
512
+
513
+ // src/services/agent-performance.ts
514
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync2 } from "fs";
515
+ import { join as join5 } from "path";
516
+ function perfPath(dir) {
517
+ return join5(codebaseDir(dir), "AGENT_PERF.json");
518
+ }
519
+ function loadStore(dir) {
520
+ const p = perfPath(dir);
521
+ if (!existsSync5(p))
522
+ return { entries: [], updated_at: new Date().toISOString() };
523
+ try {
524
+ return JSON.parse(readFileSync5(p, "utf-8"));
525
+ } catch {
526
+ return { entries: [], updated_at: new Date().toISOString() };
527
+ }
528
+ }
529
+ function saveStore(dir, store) {
530
+ const cd = codebaseDir(dir);
531
+ if (!existsSync5(cd))
532
+ mkdirSync2(cd, { recursive: true });
533
+ writeFileSync5(perfPath(dir), JSON.stringify(store, null, 2), "utf-8");
534
+ }
535
+ function makeKey(agent, model, task_type) {
536
+ return `${agent}::${model}::${task_type}`;
537
+ }
538
+ function recordRun(dir, agent, model, task_type, success, duration_ms, cost = 0) {
539
+ const store = loadStore(dir);
540
+ const key = makeKey(agent, model, task_type);
541
+ const existing = store.entries.find((e) => makeKey(e.agent, e.model, e.task_type) === key);
542
+ if (existing) {
543
+ existing.runs++;
544
+ if (success)
545
+ existing.successes++;
546
+ else
547
+ existing.failures++;
548
+ existing.total_duration_ms += duration_ms;
549
+ existing.total_cost += cost;
550
+ existing.last_run = new Date().toISOString();
551
+ existing.last_status = success ? "success" : "failure";
552
+ } else {
553
+ store.entries.push({
554
+ agent,
555
+ model,
556
+ task_type,
557
+ runs: 1,
558
+ successes: success ? 1 : 0,
559
+ failures: success ? 0 : 1,
560
+ total_duration_ms: duration_ms,
561
+ total_cost: cost,
562
+ last_run: new Date().toISOString(),
563
+ last_status: success ? "success" : "failure"
564
+ });
565
+ }
566
+ store.updated_at = new Date().toISOString();
567
+ saveStore(dir, store);
568
+ }
569
+
570
+ // src/services/model-router.ts
571
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
572
+ import { join as join6 } from "path";
573
+ var DEFAULT_ROUTING = {
574
+ planning: { primary: "claude-sonnet-4-5", temperature: 0.3, reasoning_effort: "medium" },
575
+ implementation: { primary: "claude-opus-4-5", fallback: "claude-sonnet-4-5", high_risk_override: "claude-opus-4-5", temperature: 0.2, reasoning_effort: "high" },
576
+ debugging: { primary: "claude-sonnet-4-5", high_risk_override: "claude-opus-4-5", temperature: 0.2, reasoning_effort: "high" },
577
+ review: { primary: "gemini-2.5-flash", fallback: "claude-haiku-4-5", temperature: 0.1, reasoning_effort: "medium" },
578
+ testing: { primary: "claude-haiku-4-5", fallback: "gemini-2.5-flash", temperature: 0.1, reasoning_effort: "low" },
579
+ documentation: { primary: "claude-sonnet-4-5", fallback: "gemini-2.5-flash", temperature: 0.3, reasoning_effort: "low" },
580
+ analysis: { primary: "claude-sonnet-4-5", temperature: 0.3, reasoning_effort: "medium" },
581
+ security: { primary: "claude-opus-4-5", high_risk_override: "claude-opus-4-5", temperature: 0.1, reasoning_effort: "high" },
582
+ orchestration: { primary: "claude-sonnet-4-5", temperature: 0.3, reasoning_effort: "medium" }
583
+ };
584
+ function getRouterConfig(dir) {
585
+ const p = join6(codebaseDir(dir), "MODEL_ROUTER.json");
586
+ if (!existsSync6(p))
587
+ return DEFAULT_ROUTING;
588
+ try {
589
+ const overrides = JSON.parse(readFileSync6(p, "utf-8"));
590
+ return { ...DEFAULT_ROUTING, ...overrides };
591
+ } catch {
592
+ return DEFAULT_ROUTING;
593
+ }
594
+ }
595
+ function routeModel(dir, task_type, risk_score = 100) {
596
+ const config = getRouterConfig(dir);
597
+ const route = config[task_type] ?? DEFAULT_ROUTING.implementation;
598
+ const is_high_risk = risk_score < 40;
599
+ let model = route.primary;
600
+ let is_override = false;
601
+ if (is_high_risk && route.high_risk_override) {
602
+ model = route.high_risk_override;
603
+ is_override = true;
604
+ }
605
+ return {
606
+ model,
607
+ temperature: route.temperature ?? 0.3,
608
+ reasoning_effort: route.reasoning_effort,
609
+ task_type,
610
+ is_high_risk,
611
+ is_override
612
+ };
613
+ }
614
+
615
+ // src/tools/dispatch-routing.ts
616
+ function shouldRetry(promptRes) {
617
+ if (!promptRes)
618
+ return false;
619
+ const detail = promptRes.error?.detail;
620
+ if (isTransientError(detail))
621
+ return true;
622
+ const infoError = promptRes.data?.info?.error;
623
+ const text = typeof infoError === "string" ? infoError : JSON.stringify(infoError ?? "");
624
+ return isTransientError(text);
625
+ }
626
+ function isTransientError(text) {
627
+ if (!text)
628
+ return false;
629
+ const haystack = text.toLowerCase();
630
+ return haystack.includes("overload") || haystack.includes("rate limit") || haystack.includes("timeout") || haystack.includes("temporar") || haystack.includes("econnreset");
631
+ }
632
+ function normalizeTaskType(taskType, agent) {
633
+ const normalized = (taskType ?? "").trim().toLowerCase();
634
+ if (isTaskType(normalized))
635
+ return normalized;
636
+ const a = agent.toLowerCase();
637
+ if (a.includes("review"))
638
+ return "review";
639
+ if (a.includes("test"))
640
+ return "testing";
641
+ if (a.includes("debug"))
642
+ return "debugging";
643
+ if (a.includes("security"))
644
+ return "security";
645
+ if (a.includes("doc"))
646
+ return "documentation";
647
+ if (a.includes("architect") || a.includes("planner"))
648
+ return "planning";
649
+ if (a.includes("orchestrator") || a.includes("coordinator"))
650
+ return "orchestration";
651
+ if (a.includes("analyst") || a.includes("research"))
652
+ return "analysis";
653
+ return "implementation";
654
+ }
655
+ function isTaskType(value) {
656
+ return value === "planning" || value === "implementation" || value === "debugging" || value === "review" || value === "testing" || value === "documentation" || value === "analysis" || value === "security" || value === "orchestration";
657
+ }
658
+
659
+ // src/tools/run-pipeline.ts
512
660
  function extractText(parts) {
513
661
  return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join(`
514
662
  `);
@@ -519,16 +667,20 @@ function createRunPipelineTool(client) {
519
667
  args: {
520
668
  steps: tool4.schema.array(tool4.schema.object({
521
669
  agent: tool4.schema.string(),
522
- prompt: tool4.schema.string()
670
+ prompt: tool4.schema.string(),
671
+ task_type: tool4.schema.string().optional()
523
672
  })),
524
673
  initial_context: tool4.schema.string().optional(),
525
- abort_on_failure: tool4.schema.boolean().optional().default(true)
674
+ abort_on_failure: tool4.schema.boolean().optional().default(true),
675
+ retry_attempts: tool4.schema.number().optional().default(1)
526
676
  },
527
677
  async execute(args, context) {
528
678
  const startTime = Date.now();
529
679
  const trace = [];
530
680
  let carryContext = args.initial_context ?? "";
531
681
  let aborted = false;
682
+ const retryAttempts = typeof args.retry_attempts === "number" ? args.retry_attempts : 1;
683
+ const maxRetries = Math.max(0, Math.floor(retryAttempts));
532
684
  let inflightChildId = null;
533
685
  const abortHandler = () => {
534
686
  if (inflightChildId) {
@@ -546,6 +698,8 @@ function createRunPipelineTool(client) {
546
698
  break;
547
699
  }
548
700
  const stepStart = Date.now();
701
+ const taskType = normalizeTaskType(step.task_type, step.agent);
702
+ const routing = routeModel(context.directory, taskType);
549
703
  const stepInput = carryContext ? `${carryContext}
550
704
 
551
705
  ---
@@ -557,28 +711,37 @@ ${step.prompt}` : step.prompt;
557
711
  });
558
712
  if (createRes.error || !createRes.data?.id) {
559
713
  const errMsg = `Failed to create session: ${createRes.error?.detail ?? "unknown"}`;
560
- trace.push({ agent: step.agent, input: stepInput, output: errMsg, duration_ms: Date.now() - stepStart, success: false });
714
+ trace.push({ agent: step.agent, task_type: taskType, model: routing.model, input: stepInput, output: errMsg, duration_ms: Date.now() - stepStart, success: false });
561
715
  aborted = true;
562
716
  break;
563
717
  }
564
718
  inflightChildId = createRes.data.id;
565
- const promptRes = await client.session.prompt({
566
- path: { id: inflightChildId },
567
- body: {
568
- agent: step.agent,
569
- parts: [{ type: "text", text: stepInput }],
570
- tools: { question: false }
571
- },
572
- query: { directory: context.directory }
573
- });
719
+ let promptRes = null;
720
+ let retriesUsed = 0;
721
+ for (let attempt = 0;attempt <= maxRetries; attempt++) {
722
+ promptRes = await client.session.prompt({
723
+ path: { id: inflightChildId },
724
+ body: {
725
+ agent: step.agent,
726
+ model: routing.model,
727
+ parts: [{ type: "text", text: stepInput }],
728
+ tools: { question: false }
729
+ },
730
+ query: { directory: context.directory }
731
+ });
732
+ if (!shouldRetry(promptRes) || attempt === maxRetries)
733
+ break;
734
+ retriesUsed++;
735
+ }
574
736
  inflightChildId = null;
575
737
  if (context.abort.aborted) {
576
738
  aborted = true;
577
739
  break;
578
740
  }
579
- if (promptRes.error) {
580
- const errMsg = `Prompt failed: ${promptRes.error?.detail ?? "unknown"}`;
581
- trace.push({ agent: step.agent, session_id: createRes.data.id, input: stepInput, output: errMsg, duration_ms: Date.now() - stepStart, success: false });
741
+ if (!promptRes || promptRes.error) {
742
+ const errMsg = `Prompt failed: ${promptRes?.error?.detail ?? "unknown"}`;
743
+ trace.push({ agent: step.agent, session_id: createRes.data.id, task_type: taskType, model: routing.model, input: stepInput, output: `${errMsg}${retriesUsed > 0 ? ` (retries: ${retriesUsed})` : ""}`, duration_ms: Date.now() - stepStart, success: false });
744
+ recordRun(context.directory, step.agent, routing.model, taskType, false, Date.now() - stepStart);
582
745
  if (args.abort_on_failure) {
583
746
  aborted = true;
584
747
  break;
@@ -588,7 +751,8 @@ ${step.prompt}` : step.prompt;
588
751
  const info = promptRes.data?.info;
589
752
  if (info?.error) {
590
753
  const errMsg = `Agent error: ${JSON.stringify(info.error)}`;
591
- trace.push({ agent: step.agent, session_id: createRes.data.id, input: stepInput, output: errMsg, duration_ms: Date.now() - stepStart, success: false });
754
+ trace.push({ agent: step.agent, session_id: createRes.data.id, task_type: taskType, model: routing.model, input: stepInput, output: `${errMsg}${retriesUsed > 0 ? ` (retries: ${retriesUsed})` : ""}`, duration_ms: Date.now() - stepStart, success: false });
755
+ recordRun(context.directory, step.agent, routing.model, taskType, false, Date.now() - stepStart);
592
756
  if (args.abort_on_failure) {
593
757
  aborted = true;
594
758
  break;
@@ -596,7 +760,8 @@ ${step.prompt}` : step.prompt;
596
760
  continue;
597
761
  }
598
762
  const output = extractText(promptRes.data?.parts ?? []);
599
- trace.push({ agent: step.agent, session_id: createRes.data.id, input: stepInput, output: output || "(no text output)", duration_ms: Date.now() - stepStart, success: true });
763
+ trace.push({ agent: step.agent, session_id: createRes.data.id, task_type: taskType, model: routing.model, input: stepInput, output: output || "(no text output)", duration_ms: Date.now() - stepStart, success: true });
764
+ recordRun(context.directory, step.agent, routing.model, taskType, true, Date.now() - stepStart);
600
765
  carryContext = output;
601
766
  }
602
767
  } finally {
@@ -623,10 +788,16 @@ function createDelegateTool(client) {
623
788
  args: {
624
789
  agent: tool5.schema.string(),
625
790
  prompt: tool5.schema.string(),
626
- context: tool5.schema.string().optional()
791
+ context: tool5.schema.string().optional(),
792
+ task_type: tool5.schema.string().optional(),
793
+ retry_attempts: tool5.schema.number().optional().default(1)
627
794
  },
628
795
  async execute(args, context) {
629
796
  const startTime = Date.now();
797
+ const taskType = normalizeTaskType(args.task_type, args.agent);
798
+ const routing = routeModel(context.directory, taskType);
799
+ const retryAttempts = typeof args.retry_attempts === "number" ? args.retry_attempts : 1;
800
+ const maxRetries = Math.max(0, Math.floor(retryAttempts));
630
801
  const createRes = await client.session.create({
631
802
  body: { parentID: context.sessionID, title: `${args.agent}-delegate` },
632
803
  query: { directory: context.directory }
@@ -651,40 +822,60 @@ function createDelegateTool(client) {
651
822
  ---
652
823
 
653
824
  ${args.prompt}` : args.prompt;
654
- const promptRes = await client.session.prompt({
655
- path: { id: childId },
656
- body: {
657
- agent: args.agent,
658
- parts: [{ type: "text", text: fullPrompt }],
659
- tools: { question: false }
660
- },
661
- query: { directory: context.directory }
662
- });
663
- if (promptRes.error) {
825
+ let promptRes = null;
826
+ let retriesUsed = 0;
827
+ for (let attempt = 0;attempt <= maxRetries; attempt++) {
828
+ promptRes = await client.session.prompt({
829
+ path: { id: childId },
830
+ body: {
831
+ agent: args.agent,
832
+ model: routing.model,
833
+ parts: [{ type: "text", text: fullPrompt }],
834
+ tools: { question: false }
835
+ },
836
+ query: { directory: context.directory }
837
+ });
838
+ if (!shouldRetry(promptRes) || attempt === maxRetries)
839
+ break;
840
+ retriesUsed++;
841
+ }
842
+ if (!promptRes || promptRes.error) {
843
+ recordRun(context.directory, args.agent, routing.model, taskType, false, Date.now() - startTime);
664
844
  return JSON.stringify({
665
845
  agent: args.agent,
666
846
  session_id: childId,
667
847
  success: false,
668
- error: `Prompt failed: ${promptRes.error?.detail ?? "unknown"}`,
848
+ error: `Prompt failed: ${promptRes?.error?.detail ?? "unknown"}`,
849
+ task_type: taskType,
850
+ model: routing.model,
851
+ retries_used: retriesUsed,
669
852
  duration_ms: Date.now() - startTime
670
853
  });
671
854
  }
672
855
  const info = promptRes.data?.info;
673
856
  if (info?.error) {
857
+ recordRun(context.directory, args.agent, routing.model, taskType, false, Date.now() - startTime);
674
858
  return JSON.stringify({
675
859
  agent: args.agent,
676
860
  session_id: childId,
677
861
  success: false,
678
862
  error: `Agent error: ${JSON.stringify(info.error)}`,
863
+ task_type: taskType,
864
+ model: routing.model,
865
+ retries_used: retriesUsed,
679
866
  duration_ms: Date.now() - startTime
680
867
  });
681
868
  }
682
869
  const output = extractText2(promptRes.data?.parts ?? []);
870
+ recordRun(context.directory, args.agent, routing.model, taskType, true, Date.now() - startTime);
683
871
  return JSON.stringify({
684
872
  agent: args.agent,
685
873
  session_id: childId,
686
874
  success: true,
687
875
  output: output || "(no text output)",
876
+ task_type: taskType,
877
+ model: routing.model,
878
+ retries_used: retriesUsed,
688
879
  duration_ms: Date.now() - startTime
689
880
  });
690
881
  }
@@ -693,31 +884,31 @@ ${args.prompt}` : args.prompt;
693
884
 
694
885
  // src/tools/repo-memory.ts
695
886
  import { tool as tool6 } from "@opencode-ai/plugin";
696
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
697
- import { join as join5 } from "path";
887
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
888
+ import { join as join7 } from "path";
698
889
  var MEMORY_FILE = "MEMORY.json";
699
890
  function memoryPath(directory) {
700
- return join5(codebaseDir(directory), MEMORY_FILE);
891
+ return join7(codebaseDir(directory), MEMORY_FILE);
701
892
  }
702
893
  function emptyMemory() {
703
894
  return { version: "1.0", last_updated: new Date().toISOString(), nodes: {} };
704
895
  }
705
896
  function readMemory(directory) {
706
897
  const p = memoryPath(directory);
707
- if (!existsSync5(p))
898
+ if (!existsSync7(p))
708
899
  return emptyMemory();
709
900
  try {
710
- return JSON.parse(readFileSync5(p, "utf-8"));
901
+ return JSON.parse(readFileSync7(p, "utf-8"));
711
902
  } catch {
712
903
  return emptyMemory();
713
904
  }
714
905
  }
715
906
  function writeMemory(directory, memory) {
716
907
  const base = codebaseDir(directory);
717
- if (!existsSync5(base))
718
- mkdirSync2(base, { recursive: true });
908
+ if (!existsSync7(base))
909
+ mkdirSync3(base, { recursive: true });
719
910
  memory.last_updated = new Date().toISOString();
720
- writeFileSync5(memoryPath(directory), JSON.stringify(memory, null, 2), "utf-8");
911
+ writeFileSync6(memoryPath(directory), JSON.stringify(memory, null, 2), "utf-8");
721
912
  }
722
913
  var repoMemoryTool = tool6({
723
914
  description: "Repo Memory Graph: read/write/query persistent architecture graph in .codebase/MEMORY.json (modules, dependencies, ownership, bug history, conventions)",
@@ -794,28 +985,28 @@ var repoMemoryTool = tool6({
794
985
 
795
986
  // src/tools/failure-replay.ts
796
987
  import { tool as tool7 } from "@opencode-ai/plugin";
797
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
798
- import { join as join6 } from "path";
988
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
989
+ import { join as join8 } from "path";
799
990
  var FAILURES_FILE = "FAILURES.json";
800
991
  function failuresPath(directory) {
801
- return join6(codebaseDir(directory), FAILURES_FILE);
992
+ return join8(codebaseDir(directory), FAILURES_FILE);
802
993
  }
803
994
  function readStore(directory) {
804
995
  const p = failuresPath(directory);
805
- if (!existsSync6(p))
996
+ if (!existsSync8(p))
806
997
  return { version: "1.0", last_updated: new Date().toISOString(), entries: [] };
807
998
  try {
808
- return JSON.parse(readFileSync6(p, "utf-8"));
999
+ return JSON.parse(readFileSync8(p, "utf-8"));
809
1000
  } catch {
810
1001
  return { version: "1.0", last_updated: new Date().toISOString(), entries: [] };
811
1002
  }
812
1003
  }
813
1004
  function writeStore(directory, store) {
814
1005
  const base = codebaseDir(directory);
815
- if (!existsSync6(base))
816
- mkdirSync3(base, { recursive: true });
1006
+ if (!existsSync8(base))
1007
+ mkdirSync4(base, { recursive: true });
817
1008
  store.last_updated = new Date().toISOString();
818
- writeFileSync6(failuresPath(directory), JSON.stringify(store, null, 2), "utf-8");
1009
+ writeFileSync7(failuresPath(directory), JSON.stringify(store, null, 2), "utf-8");
819
1010
  }
820
1011
  var failureReplayTool = tool7({
821
1012
  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",
@@ -899,17 +1090,17 @@ var failureReplayTool = tool7({
899
1090
 
900
1091
  // src/tools/decision-trace.ts
901
1092
  import { tool as tool8 } from "@opencode-ai/plugin";
902
- import { readFileSync as readFileSync7, existsSync as existsSync7, mkdirSync as mkdirSync4, appendFileSync } from "fs";
903
- import { join as join7 } from "path";
1093
+ import { readFileSync as readFileSync9, existsSync as existsSync9, mkdirSync as mkdirSync5, appendFileSync } from "fs";
1094
+ import { join as join9 } from "path";
904
1095
  var DECISIONS_FILE = "DECISIONS.jsonl";
905
1096
  function decisionsPath(directory) {
906
- return join7(codebaseDir(directory), DECISIONS_FILE);
1097
+ return join9(codebaseDir(directory), DECISIONS_FILE);
907
1098
  }
908
1099
  function readDecisions(directory) {
909
1100
  const p = decisionsPath(directory);
910
- if (!existsSync7(p))
1101
+ if (!existsSync9(p))
911
1102
  return [];
912
- return readFileSync7(p, "utf-8").split(`
1103
+ return readFileSync9(p, "utf-8").split(`
913
1104
  `).filter((l) => l.trim()).map((l) => {
914
1105
  try {
915
1106
  return JSON.parse(l);
@@ -949,8 +1140,8 @@ var decisionTraceTool = tool8({
949
1140
  case "record": {
950
1141
  if (!args.entry)
951
1142
  return JSON.stringify({ error: "entry required" });
952
- if (!existsSync7(base))
953
- mkdirSync4(base, { recursive: true });
1143
+ if (!existsSync9(base))
1144
+ mkdirSync5(base, { recursive: true });
954
1145
  const entry = { ...args.entry, timestamp: new Date().toISOString() };
955
1146
  appendFileSync(decisionsPath(dir), JSON.stringify(entry) + `
956
1147
  `, "utf-8");
@@ -984,28 +1175,28 @@ var decisionTraceTool = tool8({
984
1175
 
985
1176
  // src/tools/volatility-map.ts
986
1177
  import { tool as tool9 } from "@opencode-ai/plugin";
987
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
988
- import { join as join8 } from "path";
1178
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync6 } from "fs";
1179
+ import { join as join10 } from "path";
989
1180
  var VOLATILITY_FILE = "VOLATILITY.json";
990
1181
  function volatilityPath(directory) {
991
- return join8(codebaseDir(directory), VOLATILITY_FILE);
1182
+ return join10(codebaseDir(directory), VOLATILITY_FILE);
992
1183
  }
993
1184
  function readStore2(directory) {
994
1185
  const p = volatilityPath(directory);
995
- if (!existsSync8(p))
1186
+ if (!existsSync10(p))
996
1187
  return { version: "1.0", last_updated: new Date().toISOString(), generated_at: new Date().toISOString(), entries: [] };
997
1188
  try {
998
- return JSON.parse(readFileSync8(p, "utf-8"));
1189
+ return JSON.parse(readFileSync10(p, "utf-8"));
999
1190
  } catch {
1000
1191
  return { version: "1.0", last_updated: new Date().toISOString(), generated_at: new Date().toISOString(), entries: [] };
1001
1192
  }
1002
1193
  }
1003
1194
  function writeStore2(directory, store) {
1004
1195
  const base = codebaseDir(directory);
1005
- if (!existsSync8(base))
1006
- mkdirSync5(base, { recursive: true });
1196
+ if (!existsSync10(base))
1197
+ mkdirSync6(base, { recursive: true });
1007
1198
  store.last_updated = new Date().toISOString();
1008
- writeFileSync8(volatilityPath(directory), JSON.stringify(store, null, 2), "utf-8");
1199
+ writeFileSync9(volatilityPath(directory), JSON.stringify(store, null, 2), "utf-8");
1009
1200
  }
1010
1201
  function stabilityLabel(churn, hotfixes, todos) {
1011
1202
  const score = churn + hotfixes * 10 + todos * 2;
@@ -1092,28 +1283,28 @@ var volatilityMapTool = tool9({
1092
1283
 
1093
1284
  // src/tools/policy-engine.ts
1094
1285
  import { tool as tool10 } from "@opencode-ai/plugin";
1095
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
1096
- import { join as join9 } from "path";
1286
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, existsSync as existsSync11, mkdirSync as mkdirSync7 } from "fs";
1287
+ import { join as join11 } from "path";
1097
1288
  var POLICIES_FILE = "POLICIES.json";
1098
1289
  function policiesPath(directory) {
1099
- return join9(codebaseDir(directory), POLICIES_FILE);
1290
+ return join11(codebaseDir(directory), POLICIES_FILE);
1100
1291
  }
1101
1292
  function readStore3(directory) {
1102
1293
  const p = policiesPath(directory);
1103
- if (!existsSync9(p))
1294
+ if (!existsSync11(p))
1104
1295
  return { version: "1.0", last_updated: new Date().toISOString(), policies: [] };
1105
1296
  try {
1106
- return JSON.parse(readFileSync9(p, "utf-8"));
1297
+ return JSON.parse(readFileSync11(p, "utf-8"));
1107
1298
  } catch {
1108
1299
  return { version: "1.0", last_updated: new Date().toISOString(), policies: [] };
1109
1300
  }
1110
1301
  }
1111
1302
  function writeStore3(directory, store) {
1112
1303
  const base = codebaseDir(directory);
1113
- if (!existsSync9(base))
1114
- mkdirSync6(base, { recursive: true });
1304
+ if (!existsSync11(base))
1305
+ mkdirSync7(base, { recursive: true });
1115
1306
  store.last_updated = new Date().toISOString();
1116
- writeFileSync9(policiesPath(directory), JSON.stringify(store, null, 2), "utf-8");
1307
+ writeFileSync10(policiesPath(directory), JSON.stringify(store, null, 2), "utf-8");
1117
1308
  }
1118
1309
  var policyEngineTool = tool10({
1119
1310
  description: "Self-Healing Policy Engine: manage .codebase/POLICIES.json — add, list, query, toggle, and record violations of editing policies learned from past failures",
@@ -1195,7 +1386,7 @@ var policyEngineTool = tool10({
1195
1386
 
1196
1387
  // src/tools/hash-edit.ts
1197
1388
  import { tool as tool11 } from "@opencode-ai/plugin";
1198
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "fs";
1389
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
1199
1390
  import { createHash } from "crypto";
1200
1391
  var hashEditTool = tool11({
1201
1392
  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.",
@@ -1209,7 +1400,7 @@ var hashEditTool = tool11({
1209
1400
  const fullPath = args.filePath.startsWith("/") ? args.filePath : `${context.directory}/${args.filePath}`;
1210
1401
  let content;
1211
1402
  try {
1212
- content = readFileSync10(fullPath, "utf-8");
1403
+ content = readFileSync12(fullPath, "utf-8");
1213
1404
  } catch (e) {
1214
1405
  return `Error: Could not read file ${args.filePath}`;
1215
1406
  }
@@ -1223,13 +1414,15 @@ var hashEditTool = tool11({
1223
1414
  }
1224
1415
  }
1225
1416
  const newContent = content.replace(args.targetContent, args.replacementContent);
1226
- writeFileSync10(fullPath, newContent, "utf-8");
1417
+ writeFileSync11(fullPath, newContent, "utf-8");
1227
1418
  return `Successfully updated ${args.filePath} using hash-anchored edit.`;
1228
1419
  }
1229
1420
  });
1230
1421
 
1231
1422
  // src/tools/council.ts
1232
1423
  import { tool as tool12 } from "@opencode-ai/plugin";
1424
+ import { appendFileSync as appendFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync8 } from "fs";
1425
+ import { join as join12 } from "path";
1233
1426
  function createCouncilTool(client) {
1234
1427
  return tool12({
1235
1428
  description: "Run an ensemble of agents (Council) on the same task to reach consensus or compare approaches. Runs 3 specialized agents in parallel and returns their synthesized outputs.",
@@ -1284,16 +1477,34 @@ Please synthesize these results. Identify areas of agreement, resolve conflicts,
1284
1477
  },
1285
1478
  query: { directory: context.directory }
1286
1479
  });
1287
- return (finalRes.data?.parts ?? []).filter((p) => p.type === "text").map((p) => p.text).join(`
1480
+ const synthesis = (finalRes.data?.parts ?? []).filter((p) => p.type === "text").map((p) => p.text).join(`
1288
1481
  `);
1482
+ persistCouncilResult(context.directory, {
1483
+ task: args.task,
1484
+ agents,
1485
+ results,
1486
+ synthesis,
1487
+ created_at: new Date().toISOString()
1488
+ });
1489
+ return synthesis;
1289
1490
  }
1290
1491
  });
1291
1492
  }
1493
+ function persistCouncilResult(directory, payload) {
1494
+ try {
1495
+ const base = codebaseDir(directory);
1496
+ if (!existsSync12(base))
1497
+ mkdirSync8(base, { recursive: true });
1498
+ const path = join12(base, "COUNCILS.jsonl");
1499
+ appendFileSync2(path, JSON.stringify(payload) + `
1500
+ `, "utf-8");
1501
+ } catch {}
1502
+ }
1292
1503
 
1293
1504
  // src/tools/context-generator.ts
1294
1505
  import { tool as tool13 } from "@opencode-ai/plugin";
1295
- import { writeFileSync as writeFileSync11, existsSync as existsSync10, readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
1296
- import { join as join10 } from "path";
1506
+ import { writeFileSync as writeFileSync12, existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync2, statSync } from "fs";
1507
+ import { join as join13 } from "path";
1297
1508
  var contextGeneratorTool = tool13({
1298
1509
  description: "Auto-generate or update hierarchical context files (AGENTS.md, CLAUDE.md) throughout the project. These files provide critical grounding for AI agents.",
1299
1510
  args: {
@@ -1302,20 +1513,20 @@ var contextGeneratorTool = tool13({
1302
1513
  },
1303
1514
  async execute(args, context) {
1304
1515
  const root = context.directory;
1305
- const target = args.targetDir ? join10(root, args.targetDir) : root;
1306
- if (!existsSync10(target)) {
1516
+ const target = args.targetDir ? join13(root, args.targetDir) : root;
1517
+ if (!existsSync13(target)) {
1307
1518
  return `Error: Directory ${target} does not exist.`;
1308
1519
  }
1309
- const agentsMdPath = join10(target, "AGENTS.md");
1310
- if (existsSync10(agentsMdPath) && !args.force) {
1520
+ const agentsMdPath = join13(target, "AGENTS.md");
1521
+ if (existsSync13(agentsMdPath) && !args.force) {
1311
1522
  return `AGENTS.md already exists in ${target}. Use force: true to overwrite.`;
1312
1523
  }
1313
- const pkgPath = join10(root, "package.json");
1524
+ const pkgPath = join13(root, "package.json");
1314
1525
  let projectName = "Project";
1315
1526
  let techStack = "Unknown";
1316
- if (existsSync10(pkgPath)) {
1527
+ if (existsSync13(pkgPath)) {
1317
1528
  try {
1318
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
1529
+ const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
1319
1530
  projectName = pkg.name || projectName;
1320
1531
  techStack = Object.keys(pkg.dependencies || {}).slice(0, 5).join(", ");
1321
1532
  } catch {}
@@ -1333,7 +1544,7 @@ var contextGeneratorTool = tool13({
1333
1544
 
1334
1545
  ## Directory Map
1335
1546
  ${readdirSync2(target).slice(0, 10).map((f) => {
1336
- const s = statSync(join10(target, f));
1547
+ const s = statSync(join13(target, f));
1337
1548
  return `- \`${f}\`${s.isDirectory() ? "/" : ""} : [Description]`;
1338
1549
  }).join(`
1339
1550
  `)}
@@ -1341,17 +1552,17 @@ ${readdirSync2(target).slice(0, 10).map((f) => {
1341
1552
  ---
1342
1553
  Generated by FlowDeck Context Generator.
1343
1554
  `;
1344
- writeFileSync11(agentsMdPath, content, "utf-8");
1555
+ writeFileSync12(agentsMdPath, content, "utf-8");
1345
1556
  return `Successfully generated AGENTS.md in ${target}.`;
1346
1557
  }
1347
1558
  });
1348
1559
 
1349
1560
  // src/tools/create-skill.ts
1350
1561
  import { tool as tool14 } from "@opencode-ai/plugin";
1351
- import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync12, existsSync as existsSync11 } from "fs";
1352
- import { join as join11, dirname as dirname3 } from "path";
1562
+ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync13, existsSync as existsSync14 } from "fs";
1563
+ import { join as join14, dirname as dirname3 } from "path";
1353
1564
  import { fileURLToPath } from "url";
1354
- var SKILLS_DIR = join11(dirname3(fileURLToPath(import.meta.url)), "..", "skills");
1565
+ var SKILLS_DIR = join14(dirname3(fileURLToPath(import.meta.url)), "..", "skills");
1355
1566
  var createSkillTool = tool14({
1356
1567
  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.",
1357
1568
  args: {
@@ -1361,9 +1572,9 @@ var createSkillTool = tool14({
1361
1572
  tags: tool14.schema.array(tool14.schema.string()).optional().describe("Optional tags for categorisation, e.g. ['performance', 'typescript']")
1362
1573
  },
1363
1574
  async execute(args) {
1364
- const skillDir = join11(SKILLS_DIR, args.name);
1365
- const skillFile = join11(skillDir, "SKILL.md");
1366
- if (existsSync11(skillFile)) {
1575
+ const skillDir = join14(SKILLS_DIR, args.name);
1576
+ const skillFile = join14(skillDir, "SKILL.md");
1577
+ if (existsSync14(skillFile)) {
1367
1578
  return `Skill '${args.name}' already exists at ${skillFile}.
1368
1579
  ` + `Use a different name or delete the existing skill directory first.`;
1369
1580
  }
@@ -1378,8 +1589,8 @@ origin: FlowDeck (self-learned)${tagLine}
1378
1589
  `;
1379
1590
  const fullContent = frontmatter + args.content.trimStart();
1380
1591
  try {
1381
- mkdirSync7(skillDir, { recursive: true });
1382
- writeFileSync12(skillFile, fullContent, "utf-8");
1592
+ mkdirSync9(skillDir, { recursive: true });
1593
+ writeFileSync13(skillFile, fullContent, "utf-8");
1383
1594
  return `✓ Skill '${args.name}' created at ${skillFile}
1384
1595
 
1385
1596
  ` + `The skill is now part of the FlowDeck library. Restart OpenCode to load it into the active session.`;
@@ -1391,8 +1602,8 @@ origin: FlowDeck (self-learned)${tagLine}
1391
1602
 
1392
1603
  // src/tools/reflect.ts
1393
1604
  import { tool as tool15 } from "@opencode-ai/plugin";
1394
- import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
1395
- import { join as join12 } from "path";
1605
+ import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
1606
+ import { join as join15 } from "path";
1396
1607
  var MAX_ARTIFACT_BYTES = 4000;
1397
1608
  function tail(text, maxBytes) {
1398
1609
  if (text.length <= maxBytes)
@@ -1421,11 +1632,11 @@ var reflectTool = tool15({
1421
1632
  ];
1422
1633
  let found = 0;
1423
1634
  for (const [rel, label] of ARTIFACT_PATHS) {
1424
- const full = join12(root, rel);
1425
- if (!existsSync12(full))
1635
+ const full = join15(root, rel);
1636
+ if (!existsSync15(full))
1426
1637
  continue;
1427
1638
  try {
1428
- const raw = readFileSync12(full, "utf-8").trim();
1639
+ const raw = readFileSync14(full, "utf-8").trim();
1429
1640
  if (!raw)
1430
1641
  continue;
1431
1642
  const count = raw.split(`
@@ -1449,14 +1660,14 @@ import { tool as tool16 } from "@opencode-ai/plugin";
1449
1660
 
1450
1661
  // src/services/memory-store.ts
1451
1662
  import { Database } from "bun:sqlite";
1452
- import { existsSync as existsSync13, mkdirSync as mkdirSync8 } from "fs";
1453
- import { join as join13 } from "path";
1663
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10 } from "fs";
1664
+ import { join as join16 } from "path";
1454
1665
  import { homedir } from "os";
1455
- var MEMORY_DIR = join13(homedir(), ".flowdeck-memory");
1456
- var DB_PATH = join13(MEMORY_DIR, "memory.db");
1666
+ var MEMORY_DIR = join16(homedir(), ".flowdeck-memory");
1667
+ var DB_PATH = join16(MEMORY_DIR, "memory.db");
1457
1668
  function ensureDir() {
1458
- if (!existsSync13(MEMORY_DIR)) {
1459
- mkdirSync8(MEMORY_DIR, { recursive: true });
1669
+ if (!existsSync16(MEMORY_DIR)) {
1670
+ mkdirSync10(MEMORY_DIR, { recursive: true });
1460
1671
  }
1461
1672
  }
1462
1673
  var db = null;
@@ -1734,16 +1945,16 @@ var memorySearchTool = tool16({
1734
1945
  // src/tools/memory-status.ts
1735
1946
  import { tool as tool17 } from "@opencode-ai/plugin";
1736
1947
  import { Database as Database2 } from "bun:sqlite";
1737
- import { existsSync as existsSync14 } from "fs";
1738
- import { join as join14 } from "path";
1948
+ import { existsSync as existsSync17 } from "fs";
1949
+ import { join as join17 } from "path";
1739
1950
  import { homedir as homedir2 } from "os";
1740
- var DB_PATH2 = join14(homedir2(), ".flowdeck-memory", "memory.db");
1951
+ var DB_PATH2 = join17(homedir2(), ".flowdeck-memory", "memory.db");
1741
1952
  var memoryStatusTool = tool17({
1742
1953
  description: "Check FlowDeck memory database status, statistics, and recent sessions",
1743
1954
  args: {},
1744
1955
  async execute(_args, _context) {
1745
1956
  try {
1746
- const exists = existsSync14(DB_PATH2);
1957
+ const exists = existsSync17(DB_PATH2);
1747
1958
  const result = {
1748
1959
  database_exists: exists,
1749
1960
  path: DB_PATH2,
@@ -1900,15 +2111,15 @@ var memoryHook = {
1900
2111
  };
1901
2112
 
1902
2113
  // src/hooks/guard-rails.ts
1903
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
1904
- import { join as join15 } from "path";
2114
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "fs";
2115
+ import { join as join18 } from "path";
1905
2116
  var PLANNING_DIR2 = ".planning";
1906
2117
  var CONFIG_FILE = "config.json";
1907
2118
  var STATE_FILE2 = "STATE.md";
1908
2119
  function resolveExecutionMode(configPath, trustScore, volatility) {
1909
- if (existsSync15(configPath)) {
2120
+ if (existsSync18(configPath)) {
1910
2121
  try {
1911
- const config = JSON.parse(readFileSync13(configPath, "utf-8"));
2122
+ const config = JSON.parse(readFileSync15(configPath, "utf-8"));
1912
2123
  if (config.execution_mode === "review-only")
1913
2124
  return "review-only";
1914
2125
  if (config.execution_mode === "guarded")
@@ -1962,22 +2173,22 @@ async function guardRailsHook(ctx, input, _output) {
1962
2173
  if (!ENABLED)
1963
2174
  return;
1964
2175
  const dir = ctx.directory;
1965
- const planningDirPath = join15(dir, PLANNING_DIR2);
2176
+ const planningDirPath = join18(dir, PLANNING_DIR2);
1966
2177
  const codebaseDirectory = codebaseDir(dir);
1967
- const configPath = join15(planningDirPath, CONFIG_FILE);
1968
- const statePath2 = join15(planningDirPath, STATE_FILE2);
2178
+ const configPath = join18(planningDirPath, CONFIG_FILE);
2179
+ const statePath2 = join18(planningDirPath, STATE_FILE2);
1969
2180
  const workspaceRoot = findWorkspaceRoot(dir);
1970
2181
  if (workspaceRoot && dir !== workspaceRoot) {
1971
2182
  const config = getWorkspaceConfig(dir);
1972
- if (config && config.workspace_mode === "shared" && !existsSync15(planningDirPath)) {
2183
+ if (config && config.workspace_mode === "shared" && !existsSync18(planningDirPath)) {
1973
2184
  const msg = `No .planning/ in this sub-repo. Switch to workspace root: cd ${workspaceRoot}`;
1974
2185
  throw new Error(`[flowdeck] BLOCK: ${msg}`);
1975
2186
  }
1976
2187
  }
1977
2188
  if (input.tool === "write" || input.tool === "edit") {
1978
- if (!existsSync15(planningDirPath))
2189
+ if (!existsSync18(planningDirPath))
1979
2190
  return;
1980
- if (!existsSync15(codebaseDirectory)) {
2191
+ if (!existsSync18(codebaseDirectory)) {
1981
2192
  throw new Error(`[flowdeck] WARNING: .codebase/ not found. Run /map-codebase to map the codebase.`);
1982
2193
  }
1983
2194
  const execMode = resolveExecutionMode(configPath, null);
@@ -2010,9 +2221,9 @@ async function guardRailsHook(ctx, input, _output) {
2010
2221
  }
2011
2222
  }
2012
2223
  function effectiveSeverity(configPath, statePath2) {
2013
- if (existsSync15(configPath)) {
2224
+ if (existsSync18(configPath)) {
2014
2225
  try {
2015
- const configContent = readFileSync13(configPath, "utf-8");
2226
+ const configContent = readFileSync15(configPath, "utf-8");
2016
2227
  const config = JSON.parse(configContent);
2017
2228
  if (config.guard_enforcement === "warn")
2018
2229
  return "warn";
@@ -2028,10 +2239,10 @@ function getEffectiveSeverity(configPath, statePath2) {
2028
2239
  return effectiveSeverity(configPath, statePath2);
2029
2240
  }
2030
2241
  function getPlanConfirmed(statePath2) {
2031
- if (!existsSync15(statePath2))
2242
+ if (!existsSync18(statePath2))
2032
2243
  return false;
2033
2244
  try {
2034
- const content = readFileSync13(statePath2, "utf-8");
2245
+ const content = readFileSync15(statePath2, "utf-8");
2035
2246
  const match = content.match(/plan_confirmed:\s*(true|false)/i);
2036
2247
  return match ? match[1].toLowerCase() === "true" : false;
2037
2248
  } catch {
@@ -2039,21 +2250,21 @@ function getPlanConfirmed(statePath2) {
2039
2250
  }
2040
2251
  }
2041
2252
  function getWarningMessage(planningDir2) {
2042
- if (!existsSync15(join15(planningDir2, STATE_FILE2))) {
2253
+ if (!existsSync18(join18(planningDir2, STATE_FILE2))) {
2043
2254
  return "No .planning/ found. Run /new-project first.";
2044
2255
  }
2045
2256
  return "Plan not confirmed. Run /plan and confirm to enable execution.";
2046
2257
  }
2047
2258
  function getBlockMessage(planningDir2) {
2048
- if (!existsSync15(join15(planningDir2, STATE_FILE2))) {
2259
+ if (!existsSync18(join18(planningDir2, STATE_FILE2))) {
2049
2260
  return "No .planning/ found. Run /new-project first.";
2050
2261
  }
2051
2262
  return "Plan not confirmed. Run /plan and confirm to enable execution.";
2052
2263
  }
2053
2264
 
2054
2265
  // src/hooks/tool-guard.ts
2055
- import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
2056
- import { join as join16 } from "path";
2266
+ import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
2267
+ import { join as join19 } from "path";
2057
2268
  var IS_ENABLED = () => process.env.FLOWDECK_TOOL_GUARD_ENABLED === "on";
2058
2269
  var BLOCKED_PATTERNS = {
2059
2270
  read: [".env", ".pem", ".key", ".secret"],
@@ -2100,11 +2311,11 @@ function isBlocked(tool18, args) {
2100
2311
  return null;
2101
2312
  }
2102
2313
  function checkArchConstraint(directory, filePath) {
2103
- const constraintsPath = join16(codebaseDir(directory), "CONSTRAINTS.md");
2104
- if (!existsSync16(constraintsPath))
2314
+ const constraintsPath = join19(codebaseDir(directory), "CONSTRAINTS.md");
2315
+ if (!existsSync19(constraintsPath))
2105
2316
  return null;
2106
2317
  try {
2107
- const content = readFileSync14(constraintsPath, "utf-8");
2318
+ const content = readFileSync16(constraintsPath, "utf-8");
2108
2319
  const match = content.match(/## Forbidden Paths\n([\s\S]*?)(?:\n##|$)/);
2109
2320
  if (!match)
2110
2321
  return null;
@@ -2151,18 +2362,18 @@ async function toolGuardHook(ctx, input, output) {
2151
2362
  }
2152
2363
 
2153
2364
  // src/hooks/session-start.ts
2154
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
2365
+ import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
2155
2366
  async function sessionStartHook(ctx) {
2156
2367
  const planningDir2 = ctx.directory + "/.planning";
2157
2368
  const codebaseDirectory = codebaseDir(ctx.directory);
2158
2369
  const workspaceRoot = findWorkspaceRoot(ctx.directory);
2159
2370
  const config = workspaceRoot ? getWorkspaceConfig(ctx.directory) : null;
2160
- if (!existsSync17(planningDir2)) {
2371
+ if (!existsSync20(planningDir2)) {
2161
2372
  return {
2162
2373
  flowdeck_phase: null,
2163
2374
  flowdeck_status: "no_plan",
2164
2375
  flowdeck_warning: "Run /new-project or /map-codebase to initialize.",
2165
- flowdeck_has_codebase: existsSync17(codebaseDirectory),
2376
+ flowdeck_has_codebase: existsSync20(codebaseDirectory),
2166
2377
  ...workspaceRoot && config?.sub_repos ? {
2167
2378
  flowdeck_workspace_root: workspaceRoot,
2168
2379
  flowdeck_sub_repos: config.sub_repos,
@@ -2173,7 +2384,7 @@ async function sessionStartHook(ctx) {
2173
2384
  }
2174
2385
  try {
2175
2386
  const stateFilePath = statePath(ctx.directory);
2176
- const content = readFileSync15(stateFilePath, "utf-8");
2387
+ const content = readFileSync17(stateFilePath, "utf-8");
2177
2388
  const state = parseState(content);
2178
2389
  const currentPhase = state["current_phase"] || {};
2179
2390
  const result = {
@@ -2181,7 +2392,7 @@ async function sessionStartHook(ctx) {
2181
2392
  flowdeck_status: currentPhase["status"] ?? null,
2182
2393
  flowdeck_steps_pending: currentPhase["steps_pending"] ?? null,
2183
2394
  flowdeck_last_action: currentPhase["last_action"] ?? null,
2184
- flowdeck_has_codebase: existsSync17(codebaseDirectory)
2395
+ flowdeck_has_codebase: existsSync20(codebaseDirectory)
2185
2396
  };
2186
2397
  if (workspaceRoot && config?.sub_repos && config.sub_repos.length > 0) {
2187
2398
  result.flowdeck_workspace_root = workspaceRoot;
@@ -2196,7 +2407,7 @@ async function sessionStartHook(ctx) {
2196
2407
  flowdeck_phase: null,
2197
2408
  flowdeck_status: "error",
2198
2409
  flowdeck_warning: "State file unreadable. Continuing without flowdeck context.",
2199
- flowdeck_has_codebase: existsSync17(codebaseDirectory)
2410
+ flowdeck_has_codebase: existsSync20(codebaseDirectory)
2200
2411
  };
2201
2412
  if (workspaceRoot && config?.sub_repos && config.sub_repos.length > 0) {
2202
2413
  result.flowdeck_workspace_root = workspaceRoot;
@@ -2262,8 +2473,8 @@ function notifyPermissionNeeded(tool18) {
2262
2473
  }
2263
2474
 
2264
2475
  // src/hooks/patch-trust.ts
2265
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
2266
- import { join as join17 } from "path";
2476
+ import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
2477
+ import { join as join20 } from "path";
2267
2478
  var HIGH_RISK_KEYWORDS = [
2268
2479
  "password",
2269
2480
  "secret",
@@ -2285,11 +2496,11 @@ var HIGH_RISK_KEYWORDS = [
2285
2496
  "privilege"
2286
2497
  ];
2287
2498
  function loadVolatility(directory) {
2288
- const p = join17(codebaseDir(directory), "VOLATILITY.json");
2289
- if (!existsSync18(p))
2499
+ const p = join20(codebaseDir(directory), "VOLATILITY.json");
2500
+ if (!existsSync21(p))
2290
2501
  return {};
2291
2502
  try {
2292
- const data = JSON.parse(readFileSync16(p, "utf-8"));
2503
+ const data = JSON.parse(readFileSync18(p, "utf-8"));
2293
2504
  const map = {};
2294
2505
  for (const entry of data.entries ?? [])
2295
2506
  map[entry.path] = entry.stability;
@@ -2299,11 +2510,11 @@ function loadVolatility(directory) {
2299
2510
  }
2300
2511
  }
2301
2512
  function loadFailedPaths(directory) {
2302
- const p = join17(codebaseDir(directory), "FAILURES.json");
2303
- if (!existsSync18(p))
2513
+ const p = join20(codebaseDir(directory), "FAILURES.json");
2514
+ if (!existsSync21(p))
2304
2515
  return [];
2305
2516
  try {
2306
- const data = JSON.parse(readFileSync16(p, "utf-8"));
2517
+ const data = JSON.parse(readFileSync18(p, "utf-8"));
2307
2518
  return (data.entries ?? []).flatMap((e) => e.affected_paths ?? []);
2308
2519
  } catch {
2309
2520
  return [];
@@ -2368,8 +2579,8 @@ async function patchTrustHook(ctx, input, output) {
2368
2579
  }
2369
2580
 
2370
2581
  // src/hooks/decision-trace-hook.ts
2371
- import { existsSync as existsSync19, mkdirSync as mkdirSync9, appendFileSync as appendFileSync2 } from "fs";
2372
- import { join as join18 } from "path";
2582
+ import { existsSync as existsSync22, mkdirSync as mkdirSync11, appendFileSync as appendFileSync3 } from "fs";
2583
+ import { join as join21 } from "path";
2373
2584
  async function decisionTraceHook(ctx, input, output) {
2374
2585
  if (input.tool !== "write" && input.tool !== "edit")
2375
2586
  return;
@@ -2378,8 +2589,8 @@ async function decisionTraceHook(ctx, input, output) {
2378
2589
  return;
2379
2590
  const base = codebaseDir(ctx.directory);
2380
2591
  try {
2381
- if (!existsSync19(base))
2382
- mkdirSync9(base, { recursive: true });
2592
+ if (!existsSync22(base))
2593
+ mkdirSync11(base, { recursive: true });
2383
2594
  const entry = {
2384
2595
  timestamp: new Date().toISOString(),
2385
2596
  file_path: filePath,
@@ -2391,62 +2602,87 @@ async function decisionTraceHook(ctx, input, output) {
2391
2602
  risk_level: "unknown",
2392
2603
  auto_recorded: true
2393
2604
  };
2394
- appendFileSync2(join18(base, "DECISIONS.jsonl"), JSON.stringify(entry) + `
2605
+ appendFileSync3(join21(base, "DECISIONS.jsonl"), JSON.stringify(entry) + `
2395
2606
  `, "utf-8");
2396
2607
  } catch {}
2397
2608
  }
2398
2609
 
2399
2610
  // src/services/telemetry.ts
2400
- import { existsSync as existsSync20, readFileSync as readFileSync17, appendFileSync as appendFileSync3, mkdirSync as mkdirSync10 } from "fs";
2401
- import { join as join19 } from "path";
2611
+ import { existsSync as existsSync23, readFileSync as readFileSync19, appendFileSync as appendFileSync4, mkdirSync as mkdirSync12 } from "fs";
2612
+ import { join as join22 } from "path";
2402
2613
  import { randomUUID } from "crypto";
2403
2614
  function telemetryPath(dir) {
2404
- return join19(codebaseDir(dir), "TELEMETRY.jsonl");
2615
+ return join22(codebaseDir(dir), "TELEMETRY.jsonl");
2405
2616
  }
2406
2617
  function appendEvent(dir, partial) {
2407
2618
  if (process.env.TELEMETRY_ENABLED !== "true")
2408
2619
  return null;
2409
2620
  const cd = codebaseDir(dir);
2410
- if (!existsSync20(cd))
2411
- mkdirSync10(cd, { recursive: true });
2621
+ if (!existsSync23(cd))
2622
+ mkdirSync12(cd, { recursive: true });
2412
2623
  const event = {
2413
2624
  id: randomUUID(),
2414
2625
  ts: new Date().toISOString(),
2415
2626
  ...partial
2416
2627
  };
2417
- appendFileSync3(telemetryPath(dir), JSON.stringify(event) + `
2628
+ appendFileSync4(telemetryPath(dir), JSON.stringify(event) + `
2418
2629
  `, "utf-8");
2419
2630
  return event;
2420
2631
  }
2421
2632
 
2422
2633
  // src/hooks/telemetry-hook.ts
2634
+ function resolveIds(toolInput) {
2635
+ const session_id = toolInput.sessionID ?? toolInput.sessionId ?? process.env.OPENCODE_SESSION_ID ?? "session-0";
2636
+ const run_id = toolInput.messageID ?? toolInput.messageId ?? toolInput.runID ?? toolInput.runId ?? process.env.OPENCODE_RUN_ID ?? "run-0";
2637
+ return { session_id, run_id };
2638
+ }
2639
+ function inferStatus(output) {
2640
+ if (output.error)
2641
+ return "error";
2642
+ if (typeof output.output !== "string")
2643
+ return "ok";
2644
+ const text = output.output.trim();
2645
+ if (!text)
2646
+ return "ok";
2647
+ try {
2648
+ const parsed = JSON.parse(text);
2649
+ if (parsed.success === false || parsed.error || parsed.status === "error")
2650
+ return "error";
2651
+ return "ok";
2652
+ } catch {
2653
+ return "ok";
2654
+ }
2655
+ }
2423
2656
  async function telemetryHook(context, toolInput, output) {
2424
2657
  const dir = context.directory ?? process.cwd();
2425
2658
  const tool18 = toolInput.name ?? toolInput.tool ?? "unknown";
2659
+ const ids = resolveIds(toolInput);
2426
2660
  appendEvent(dir, {
2427
- session_id: process.env.OPENCODE_SESSION_ID ?? "session-0",
2428
- run_id: process.env.OPENCODE_RUN_ID ?? "run-0",
2661
+ session_id: ids.session_id,
2662
+ run_id: ids.run_id,
2429
2663
  event: "tool.call",
2430
2664
  tool: tool18,
2431
2665
  status: "ok",
2432
2666
  meta: { parameters: output.args ?? {} }
2433
2667
  });
2434
2668
  }
2435
- async function telemetryAfterHook(context, toolInput, _output) {
2669
+ async function telemetryAfterHook(context, toolInput, output) {
2436
2670
  const dir = context.directory ?? process.cwd();
2437
2671
  const tool18 = toolInput.name ?? toolInput.tool ?? "unknown";
2672
+ const ids = resolveIds(toolInput);
2673
+ const status = inferStatus(output);
2438
2674
  appendEvent(dir, {
2439
- session_id: process.env.OPENCODE_SESSION_ID ?? "session-0",
2440
- run_id: process.env.OPENCODE_RUN_ID ?? "run-0",
2675
+ session_id: ids.session_id,
2676
+ run_id: ids.run_id,
2441
2677
  event: "tool.complete",
2442
2678
  tool: tool18,
2443
- status: "ok"
2679
+ status
2444
2680
  });
2445
2681
  }
2446
2682
 
2447
2683
  // src/services/approval-manager.ts
2448
- import { existsSync as existsSync21, readFileSync as readFileSync18, writeFileSync as writeFileSync13, mkdirSync as mkdirSync11 } from "fs";
2449
- import { join as join20 } from "path";
2684
+ import { existsSync as existsSync24, readFileSync as readFileSync20, writeFileSync as writeFileSync14, mkdirSync as mkdirSync13 } from "fs";
2685
+ import { join as join23 } from "path";
2450
2686
  var APPROVAL_TTL_MS = 30 * 60 * 1000;
2451
2687
  var SENSITIVE_PATTERNS = [
2452
2688
  /auth/i,
@@ -2483,20 +2719,20 @@ function isSensitivePath(filePath) {
2483
2719
  return SENSITIVE_PATTERNS.some((p) => p.test(filePath));
2484
2720
  }
2485
2721
  function approvalsPath(dir) {
2486
- return join20(codebaseDir(dir), "APPROVALS.json");
2722
+ return join23(codebaseDir(dir), "APPROVALS.json");
2487
2723
  }
2488
- function loadStore(dir) {
2724
+ function loadStore2(dir) {
2489
2725
  const p = approvalsPath(dir);
2490
- if (!existsSync21(p))
2726
+ if (!existsSync24(p))
2491
2727
  return { requests: [] };
2492
2728
  try {
2493
- return JSON.parse(readFileSync18(p, "utf-8"));
2729
+ return JSON.parse(readFileSync20(p, "utf-8"));
2494
2730
  } catch {
2495
2731
  return { requests: [] };
2496
2732
  }
2497
2733
  }
2498
2734
  function checkApproval(dir, file_path, command) {
2499
- const store = loadStore(dir);
2735
+ const store = loadStore2(dir);
2500
2736
  const now = Date.now();
2501
2737
  return store.requests.filter((r) => r.status === "approved" && r.resolved_at && (r.file_path === file_path || r.trigger === command) && now - new Date(r.resolved_at).getTime() < APPROVAL_TTL_MS).sort((a, b) => b.resolved_at.localeCompare(a.resolved_at)).at(0) ?? null;
2502
2738
  }
@@ -2585,8 +2821,8 @@ function createContextWindowMonitorHook() {
2585
2821
  }
2586
2822
 
2587
2823
  // src/hooks/shell-env-hook.ts
2588
- import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
2589
- import { join as join21 } from "path";
2824
+ import { existsSync as existsSync25, readFileSync as readFileSync21 } from "fs";
2825
+ import { join as join24 } from "path";
2590
2826
  import { createRequire } from "module";
2591
2827
  var _version;
2592
2828
  function getVersion() {
@@ -2622,7 +2858,7 @@ var MARKER_TO_LANG = {
2622
2858
  };
2623
2859
  function detectPackageManager(root) {
2624
2860
  for (const [lockfile, pm] of Object.entries(LOCKFILE_TO_PM)) {
2625
- if (existsSync22(join21(root, lockfile)))
2861
+ if (existsSync25(join24(root, lockfile)))
2626
2862
  return pm;
2627
2863
  }
2628
2864
  return;
@@ -2631,7 +2867,7 @@ function detectLanguages(root) {
2631
2867
  const langs = [];
2632
2868
  const seen = new Set;
2633
2869
  for (const [marker, lang] of Object.entries(MARKER_TO_LANG)) {
2634
- if (!seen.has(lang) && existsSync22(join21(root, marker))) {
2870
+ if (!seen.has(lang) && existsSync25(join24(root, marker))) {
2635
2871
  langs.push(lang);
2636
2872
  seen.add(lang);
2637
2873
  }
@@ -2639,11 +2875,11 @@ function detectLanguages(root) {
2639
2875
  return langs;
2640
2876
  }
2641
2877
  function readCurrentPhase(root) {
2642
- const statePath2 = join21(root, ".planning", "STATE.md");
2643
- if (!existsSync22(statePath2))
2878
+ const statePath2 = join24(root, ".planning", "STATE.md");
2879
+ if (!existsSync25(statePath2))
2644
2880
  return;
2645
2881
  try {
2646
- const content = readFileSync19(statePath2, "utf-8");
2882
+ const content = readFileSync21(statePath2, "utf-8");
2647
2883
  const match = content.match(/phase:\s*(\S+)/i);
2648
2884
  return match?.[1];
2649
2885
  } catch {
@@ -2737,14 +2973,13 @@ function createSessionIdleHook(client, tracker) {
2737
2973
  if (edited.length > 10) {
2738
2974
  await client.app.log({ body: { service: "flowdeck", level: "info", message: ` … and ${edited.length - 10} more` } }).catch(() => {});
2739
2975
  }
2740
- tracker.clear();
2741
2976
  } catch {}
2742
2977
  };
2743
2978
  }
2744
2979
 
2745
2980
  // src/hooks/compaction-hook.ts
2746
- import { existsSync as existsSync23, readFileSync as readFileSync20 } from "fs";
2747
- import { join as join22 } from "path";
2981
+ import { existsSync as existsSync26, readFileSync as readFileSync22 } from "fs";
2982
+ import { join as join25 } from "path";
2748
2983
  var STRUCTURED_SUMMARY_PROMPT = `
2749
2984
  When summarizing this session, you MUST include the following sections:
2750
2985
 
@@ -2783,11 +3018,11 @@ For each: agent name, status, description, session_id.
2783
3018
  **RESUME, DON'T RESTART.** Use session_id to continue existing sessions.
2784
3019
  `;
2785
3020
  function readPlanningState2(directory) {
2786
- const statePath2 = join22(directory, ".planning", "STATE.md");
2787
- if (!existsSync23(statePath2))
3021
+ const statePath2 = join25(directory, ".planning", "STATE.md");
3022
+ if (!existsSync26(statePath2))
2788
3023
  return null;
2789
3024
  try {
2790
- const content = readFileSync20(statePath2, "utf-8");
3025
+ const content = readFileSync22(statePath2, "utf-8");
2791
3026
  return content.slice(0, 1500);
2792
3027
  } catch {
2793
3028
  return null;
@@ -2882,13 +3117,24 @@ function blockMessage(toolName) {
2882
3117
  class OrchestratorGuard {
2883
3118
  primarySessionId = null;
2884
3119
  onEvent(event) {
2885
- if ((event.type === "session.created" || event.type === "session.started") && this.primarySessionId === null) {
2886
- const props = event.properties;
2887
- const id = props?.info?.id;
2888
- if (id) {
2889
- this.primarySessionId = id;
3120
+ const eventType = event.type ?? "";
3121
+ if (eventType === "session.deleted") {
3122
+ const deletedId = extractSessionId(event);
3123
+ if (deletedId && deletedId === this.primarySessionId) {
3124
+ this.primarySessionId = null;
2890
3125
  }
3126
+ return;
2891
3127
  }
3128
+ if (eventType !== "session.created" && eventType !== "session.started")
3129
+ return;
3130
+ if (this.primarySessionId !== null)
3131
+ return;
3132
+ const id = extractSessionId(event);
3133
+ if (!id)
3134
+ return;
3135
+ if (extractParentSessionId(event))
3136
+ return;
3137
+ this.primarySessionId = id;
2892
3138
  }
2893
3139
  check(sessionId, toolName) {
2894
3140
  if (DISABLED)
@@ -2904,6 +3150,20 @@ class OrchestratorGuard {
2904
3150
  }
2905
3151
  }
2906
3152
  }
3153
+ function extractSessionId(event) {
3154
+ const props = event.properties;
3155
+ const inner = event.event;
3156
+ const info = props?.info;
3157
+ const id = event.sessionID ?? event.sessionId ?? inner?.sessionID ?? inner?.sessionId ?? info?.id;
3158
+ return id ?? null;
3159
+ }
3160
+ function extractParentSessionId(event) {
3161
+ const props = event.properties;
3162
+ const inner = event.event;
3163
+ const info = props?.info;
3164
+ const parentId = inner?.parentID ?? inner?.parentId ?? info?.parentID ?? info?.parentId;
3165
+ return parentId ?? null;
3166
+ }
2907
3167
 
2908
3168
  // src/hooks/auto-learn-hook.ts
2909
3169
  var MIN_EDITS = 1;
@@ -5880,23 +6140,23 @@ function getAgentConfigs(agentModels) {
5880
6140
  }
5881
6141
 
5882
6142
  // src/config/loader.ts
5883
- import { existsSync as existsSync24, readFileSync as readFileSync21 } from "fs";
5884
- import { join as join23 } from "path";
6143
+ import { existsSync as existsSync27, readFileSync as readFileSync23 } from "fs";
6144
+ import { join as join26 } from "path";
5885
6145
  import { homedir as homedir3 } from "os";
5886
6146
  var CONFIG_FILENAME = "flowdeck.json";
5887
6147
  function getGlobalConfigDir() {
5888
- return process.env.OPENCODE_CONFIG_DIR || (process.env.XDG_CONFIG_HOME ? join23(process.env.XDG_CONFIG_HOME, "opencode") : join23(homedir3(), ".config", "opencode"));
6148
+ return process.env.OPENCODE_CONFIG_DIR || (process.env.XDG_CONFIG_HOME ? join26(process.env.XDG_CONFIG_HOME, "opencode") : join26(homedir3(), ".config", "opencode"));
5889
6149
  }
5890
6150
  function loadFlowDeckConfig(directory) {
5891
6151
  const candidates = [];
5892
6152
  if (directory) {
5893
- candidates.push(join23(directory, ".opencode", CONFIG_FILENAME));
6153
+ candidates.push(join26(directory, ".opencode", CONFIG_FILENAME));
5894
6154
  }
5895
- candidates.push(join23(getGlobalConfigDir(), CONFIG_FILENAME));
6155
+ candidates.push(join26(getGlobalConfigDir(), CONFIG_FILENAME));
5896
6156
  for (const configPath of candidates) {
5897
- if (existsSync24(configPath)) {
6157
+ if (existsSync27(configPath)) {
5898
6158
  try {
5899
- const content = readFileSync21(configPath, "utf-8");
6159
+ const content = readFileSync23(configPath, "utf-8");
5900
6160
  return JSON.parse(content);
5901
6161
  } catch {
5902
6162
  console.warn(`[flowdeck] Failed to load config from ${configPath}`);
@@ -5908,13 +6168,13 @@ function loadFlowDeckConfig(directory) {
5908
6168
  // src/index.ts
5909
6169
  function loadRulePaths() {
5910
6170
  const __dir = dirname4(fileURLToPath2(import.meta.url));
5911
- const rulesDir = join24(__dir, "..", "src", "rules");
5912
- if (!existsSync25(rulesDir))
6171
+ const rulesDir = join27(__dir, "..", "src", "rules");
6172
+ if (!existsSync28(rulesDir))
5913
6173
  return [];
5914
6174
  const paths = [];
5915
6175
  function walk(dir) {
5916
6176
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
5917
- const full = join24(dir, entry.name);
6177
+ const full = join27(dir, entry.name);
5918
6178
  if (entry.isDirectory()) {
5919
6179
  walk(full);
5920
6180
  } else if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
@@ -5927,8 +6187,8 @@ function loadRulePaths() {
5927
6187
  }
5928
6188
  function loadCommands() {
5929
6189
  const __dir = dirname4(fileURLToPath2(import.meta.url));
5930
- const commandsDir = join24(__dir, "..", "src", "commands");
5931
- if (!existsSync25(commandsDir))
6190
+ const commandsDir = join27(__dir, "..", "src", "commands");
6191
+ if (!existsSync28(commandsDir))
5932
6192
  return {};
5933
6193
  const commands = {};
5934
6194
  try {
@@ -5936,7 +6196,7 @@ function loadCommands() {
5936
6196
  if (!file.endsWith(".md"))
5937
6197
  continue;
5938
6198
  const name = basename(file, ".md");
5939
- const raw = readFileSync22(join24(commandsDir, file), "utf-8");
6199
+ const raw = readFileSync24(join27(commandsDir, file), "utf-8");
5940
6200
  let description;
5941
6201
  let template = raw;
5942
6202
  const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
@@ -6013,8 +6273,8 @@ var plugin = async (input, _options) => {
6013
6273
  }
6014
6274
  }
6015
6275
  }
6016
- const skillsDir = join24(dirname4(fileURLToPath2(import.meta.url)), "..", "src", "skills");
6017
- if (existsSync25(skillsDir)) {
6276
+ const skillsDir = join27(dirname4(fileURLToPath2(import.meta.url)), "..", "src", "skills");
6277
+ if (existsSync28(skillsDir)) {
6018
6278
  const cfgAny = cfg;
6019
6279
  if (!cfgAny.skills || typeof cfgAny.skills !== "object") {
6020
6280
  cfgAny.skills = { paths: [] };
@@ -6100,8 +6360,12 @@ var plugin = async (input, _options) => {
6100
6360
  await contextMonitor.event({ event });
6101
6361
  orchestratorGuard.onEvent(event);
6102
6362
  if (type === "session.idle") {
6103
- await sessionIdleHook();
6104
- await autoLearnHook();
6363
+ try {
6364
+ await sessionIdleHook();
6365
+ await autoLearnHook();
6366
+ } finally {
6367
+ fileTracker.clear();
6368
+ }
6105
6369
  }
6106
6370
  },
6107
6371
  "tool.execute.before": async (toolInput, toolOutput) => {