@dv.nghiem/flowdeck 0.3.7 → 0.3.9

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 (48) hide show
  1. package/README.md +0 -101
  2. package/dist/dashboard/server.mjs +789 -868
  3. package/dist/hooks/command-ref-guard.d.ts +21 -0
  4. package/dist/hooks/command-ref-guard.d.ts.map +1 -0
  5. package/dist/hooks/compaction-hook.d.ts +2 -1
  6. package/dist/hooks/compaction-hook.d.ts.map +1 -1
  7. package/dist/hooks/session-start.d.ts.map +1 -1
  8. package/dist/index.js +33 -14
  9. package/dist/lib/impact-radar.d.ts +2 -2
  10. package/dist/mcp/index.d.ts +3 -2
  11. package/dist/mcp/index.d.ts.map +1 -1
  12. package/dist/services/command-validator.d.ts +61 -0
  13. package/dist/services/command-validator.d.ts.map +1 -0
  14. package/dist/services/command-validator.test.d.ts +2 -0
  15. package/dist/services/command-validator.test.d.ts.map +1 -0
  16. package/dist/services/preflight-explorer.d.ts +130 -0
  17. package/dist/services/preflight-explorer.d.ts.map +1 -0
  18. package/dist/services/preflight-explorer.test.d.ts +25 -0
  19. package/dist/services/preflight-explorer.test.d.ts.map +1 -0
  20. package/dist/services/question-guard.d.ts +92 -0
  21. package/dist/services/question-guard.d.ts.map +1 -0
  22. package/dist/services/quick-router.d.ts +157 -0
  23. package/dist/services/quick-router.d.ts.map +1 -0
  24. package/dist/services/quick-router.test.d.ts +13 -0
  25. package/dist/services/quick-router.test.d.ts.map +1 -0
  26. package/docs/commands.md +43 -23
  27. package/docs/quick-start.md +2 -0
  28. package/docs/workflows.md +1 -1
  29. package/package.json +8 -8
  30. package/src/commands/fd-deploy-check.md +2 -2
  31. package/src/commands/fd-discuss.md +61 -4
  32. package/src/commands/fd-map-codebase.md +1 -1
  33. package/src/commands/fd-multi-repo.md +3 -3
  34. package/src/commands/fd-plan.md +1 -1
  35. package/src/commands/fd-quick.md +303 -55
  36. package/src/rules/common/behavioral.md +63 -0
  37. package/src/skills/blast-radius-preview/SKILL.md +1 -1
  38. package/src/skills/change-impact-radar/SKILL.md +2 -2
  39. package/src/skills/codebase-mapping/SKILL.md +1 -1
  40. package/src/skills/confidence-aware-planning/SKILL.md +1 -1
  41. package/src/skills/context-load/SKILL.md +1 -1
  42. package/src/skills/human-review-routing/SKILL.md +3 -3
  43. package/src/skills/intent-translator/SKILL.md +2 -2
  44. package/src/skills/multi-repo/SKILL.md +1 -1
  45. package/src/skills/regression-prediction/SKILL.md +1 -1
  46. package/src/skills/repo-memory-graph/SKILL.md +1 -1
  47. package/src/skills/test-gap-detector/SKILL.md +1 -1
  48. package/src/skills/volatility-map/SKILL.md +1 -1
@@ -0,0 +1,21 @@
1
+ /**
2
+ * HOOK: Command Reference Guard
3
+ * Post-response hook that scans agent output for /fd-* slash command references
4
+ * and warns when an invalid (unregistered) command is found.
5
+ *
6
+ * This is a lightweight advisory guardrail — it emits a warning comment
7
+ * at the top of the response rather than blocking it, preserving usability
8
+ * while surfacing drift for developers to fix.
9
+ */
10
+ /**
11
+ * Scan an agent response string and return a warning prefix if any invalid
12
+ * command references are detected. Returns null if response is clean.
13
+ */
14
+ export declare function buildCommandRefWarning(response: string): string | null;
15
+ /**
16
+ * Apply the command reference guardrail to an agent response.
17
+ * If invalid command references are found, prepend a warning block.
18
+ * If clean, return the response unchanged.
19
+ */
20
+ export declare function applyCommandRefGuard(response: string): string;
21
+ //# sourceMappingURL=command-ref-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-ref-guard.d.ts","sourceRoot":"","sources":["../../src/hooks/command-ref-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBtE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI7D"}
@@ -6,7 +6,8 @@
6
6
  * Context includes:
7
7
  * 1. FlowDeck planning state (phase, status, pending steps)
8
8
  * 2. Recently edited files (from SessionFileTracker)
9
- * 3. Structured 8-section summary prompt
9
+ * 3. Historical session summaries from memory store
10
+ * 4. Structured 8-section summary prompt
10
11
  *
11
12
  * Inspired by oh-my-openagent's compaction-context-injector and
12
13
  * ECC's experimental.session.compacting handler.
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-hook.d.ts","sourceRoot":"","sources":["../../src/hooks/compaction-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAoDxD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAC1B,OAAO,EAAE,kBAAkB,IAGzB,QAAQ;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,mBA8BjD"}
1
+ {"version":3,"file":"compaction-hook.d.ts","sourceRoot":"","sources":["../../src/hooks/compaction-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAqDxD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAC1B,OAAO,EAAE,kBAAkB,IAGzB,QAAQ;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,mBAsCjD"}
@@ -1 +1 @@
1
- {"version":3,"file":"session-start.d.ts","sourceRoot":"","sources":["../../src/hooks/session-start.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAkElC"}
1
+ {"version":3,"file":"session-start.d.ts","sourceRoot":"","sources":["../../src/hooks/session-start.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAwElC"}
package/dist/index.js CHANGED
@@ -2452,7 +2452,7 @@ async function guardRailsHook(ctx, input, _output) {
2452
2452
  if (!existsSync18(planningDirPath))
2453
2453
  return;
2454
2454
  if (!existsSync18(codebaseDirectory)) {
2455
- throw new Error(`[flowdeck] WARNING: .codebase/ not found. Run /map-codebase to map the codebase.`);
2455
+ throw new Error(`[flowdeck] WARNING: .codebase/ not found. Run /fd-map-codebase to map the codebase.`);
2456
2456
  }
2457
2457
  const execMode = resolveExecutionMode(configPath, null);
2458
2458
  if (execMode === "review-only") {
@@ -2480,7 +2480,7 @@ async function guardRailsHook(ctx, input, _output) {
2480
2480
  for (const pattern of BUILD_DEPLOY_PATTERNS) {
2481
2481
  if (cmd.includes(pattern)) {
2482
2482
  if (!getPlanConfirmed(statePath2)) {
2483
- throw new Error(`[flowdeck] WARNING: Build/deploy command detected but plan is not confirmed. Run /plan first.`);
2483
+ throw new Error(`[flowdeck] WARNING: Build/deploy command detected but plan is not confirmed. Run /fd-plan first.`);
2484
2484
  }
2485
2485
  break;
2486
2486
  }
@@ -2543,15 +2543,15 @@ function getPlanConfirmed(statePath2) {
2543
2543
  }
2544
2544
  function getWarningMessage(planningDir2) {
2545
2545
  if (!existsSync18(join18(planningDir2, STATE_FILE2))) {
2546
- return "No .planning/ found. Run /new-project first.";
2546
+ return "No .planning/ found. Run /fd-new-project first.";
2547
2547
  }
2548
- return "Plan not confirmed. Run /plan and confirm to enable execution.";
2548
+ return "Plan not confirmed. Run /fd-plan and confirm to enable execution.";
2549
2549
  }
2550
2550
  function getBlockMessage(planningDir2) {
2551
2551
  if (!existsSync18(join18(planningDir2, STATE_FILE2))) {
2552
- return "No .planning/ found. Run /new-project first.";
2552
+ return "No .planning/ found. Run /fd-new-project first.";
2553
2553
  }
2554
- return "Plan not confirmed. Run /plan and confirm to enable execution.";
2554
+ return "Plan not confirmed. Run /fd-plan and confirm to enable execution.";
2555
2555
  }
2556
2556
 
2557
2557
  // src/hooks/tool-guard.ts
@@ -2689,8 +2689,9 @@ async function sessionStartHook(ctx) {
2689
2689
  return {
2690
2690
  flowdeck_phase: null,
2691
2691
  flowdeck_status: "no_plan",
2692
- flowdeck_warning: "Run /new-project or /map-codebase to initialize.",
2692
+ flowdeck_warning: "Run /fd-new-project or /fd-map-codebase to initialize.",
2693
2693
  flowdeck_has_codebase: existsSync20(codebaseDirectory),
2694
+ flowdeck_session_context: getContextForDirectory(ctx.directory),
2694
2695
  ...workspaceRoot && config?.sub_repos ? {
2695
2696
  flowdeck_workspace_root: workspaceRoot,
2696
2697
  flowdeck_sub_repos: config.sub_repos,
@@ -2704,12 +2705,14 @@ async function sessionStartHook(ctx) {
2704
2705
  const content = readFileSync17(stateFilePath, "utf-8");
2705
2706
  const state = parseState(content);
2706
2707
  const currentPhase = state["current_phase"] || {};
2708
+ const sessionContext = getContextForDirectory(ctx.directory);
2707
2709
  const result = {
2708
2710
  flowdeck_phase: currentPhase["phase"] ?? null,
2709
2711
  flowdeck_status: currentPhase["status"] ?? null,
2710
2712
  flowdeck_steps_pending: currentPhase["steps_pending"] ?? null,
2711
2713
  flowdeck_last_action: currentPhase["last_action"] ?? null,
2712
- flowdeck_has_codebase: existsSync20(codebaseDirectory)
2714
+ flowdeck_has_codebase: existsSync20(codebaseDirectory),
2715
+ flowdeck_session_context: sessionContext
2713
2716
  };
2714
2717
  if (workspaceRoot && config?.sub_repos && config.sub_repos.length > 0) {
2715
2718
  result.flowdeck_workspace_root = workspaceRoot;
@@ -2724,7 +2727,8 @@ async function sessionStartHook(ctx) {
2724
2727
  flowdeck_phase: null,
2725
2728
  flowdeck_status: "error",
2726
2729
  flowdeck_warning: "State file unreadable. Continuing without flowdeck context.",
2727
- flowdeck_has_codebase: existsSync20(codebaseDirectory)
2730
+ flowdeck_has_codebase: existsSync20(codebaseDirectory),
2731
+ flowdeck_session_context: getContextForDirectory(ctx.directory)
2728
2732
  };
2729
2733
  if (workspaceRoot && config?.sub_repos && config.sub_repos.length > 0) {
2730
2734
  result.flowdeck_workspace_root = workspaceRoot;
@@ -3366,6 +3370,12 @@ function createCompactionHook(ctx, tracker) {
3366
3370
  sections.push(`- … and ${edited.length - 20} more`);
3367
3371
  sections.push("");
3368
3372
  }
3373
+ const sessionContext = getContextForDirectory(ctx.directory);
3374
+ if (sessionContext) {
3375
+ sections.push("## Previous Sessions Context");
3376
+ sections.push(sessionContext);
3377
+ sections.push("");
3378
+ }
3369
3379
  output.context.push(sections.join(`
3370
3380
  `));
3371
3381
  output.prompt = STRUCTURED_SUMMARY_PROMPT.trim();
@@ -3568,6 +3578,15 @@ function createFlowDeckMcps() {
3568
3578
  oauth: false
3569
3579
  };
3570
3580
  }
3581
+ if (!disabled.has("github")) {
3582
+ mcps.github = {
3583
+ type: "remote",
3584
+ url: "https://api.githubcopilot.com/mcp/",
3585
+ enabled: true,
3586
+ ...process.env.GITHUB_TOKEN ? { headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } } : {},
3587
+ oauth: false
3588
+ };
3589
+ }
3571
3590
  return mcps;
3572
3591
  }
3573
3592
 
@@ -3605,16 +3624,16 @@ MUST execute at session start:
3605
3624
  3. Check which steps are marked complete
3606
3625
  4. Begin execution from the first incomplete step
3607
3626
 
3608
- If STATE.md does not exist, tell the user: "No STATE.md found. Run \`/new-project\` to initialize."
3627
+ If STATE.md does not exist, tell the user: "No STATE.md found. Run \`/fd-new-project\` to initialize."
3609
3628
 
3610
3629
  ## Phase Gating
3611
3630
 
3612
3631
  Only orchestrate in the **execute** phase.
3613
3632
 
3614
3633
  If the project is in another phase:
3615
- - **discuss** phase: "Run \`/discuss\` to complete requirements gathering first."
3616
- - **plan** phase: "Run \`/plan\` to create the implementation plan first."
3617
- - **review** phase: "Run \`/review-code\` to complete the review phase."
3634
+ - **discuss** phase: "Run \`/fd-discuss\` to complete requirements gathering first."
3635
+ - **plan** phase: "Run \`/fd-plan\` to create the implementation plan first."
3636
+ - **review** phase: "Run \`/fd-verify\` to complete the review phase."
3618
3637
 
3619
3638
  ## Step Execution
3620
3639
 
@@ -5539,7 +5558,7 @@ Discussion is complete when:
5539
5558
  - All decisions recorded in DISCUSS.md
5540
5559
  - No open questions remain
5541
5560
 
5542
- Report: "Requirements gathering complete. N decisions recorded. Ready for /plan."`;
5561
+ Report: "Requirements gathering complete. N decisions recorded. Ready for /fd-plan."`;
5543
5562
  var createTaskSplitterAgent = (model, customPrompt, customAppendPrompt) => {
5544
5563
  const prompt = resolvePrompt(TASK_SPLITTER_PROMPT, customPrompt, customAppendPrompt);
5545
5564
  return {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Shared Impact Radar utility.
3
3
  * Scans .codebase/ data stores for risk signals relevant to a change description.
4
- * Used by /discuss, /plan, /new-feature, and /fix-bug.
4
+ * Used by /fd-discuss, /fd-plan, /fd-new-feature, and /fd-fix-bug.
5
5
  */
6
6
  import type { FailureEntry } from "../tools/failure-replay";
7
7
  export interface ImpactRadarResult {
@@ -29,7 +29,7 @@ export declare function impactRadarSummaryLines(radar: ImpactRadarResult): strin
29
29
  /**
30
30
  * Look up prior failures from FAILURES.json that match by path prefix or keyword.
31
31
  * Returns full FailureEntry objects (including root_cause and fix_applied) sorted by recurrence desc.
32
- * Used by /fix-bug to surface lessons learned before the fix begins.
32
+ * Used by /fd-fix-bug to surface lessons learned before the fix begins.
33
33
  */
34
34
  export declare function lookupPriorFailures(dir: string, scope: string, bugText: string, limit?: number): FailureEntry[];
35
35
  //# sourceMappingURL=impact-radar.d.ts.map
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * FlowDeck built-in MCP server configurations.
3
3
  *
4
- * Three free, read-only remote MCPs are enabled by default:
4
+ * Four free, read-only remote MCPs are enabled by default:
5
5
  * - context7 https://mcp.context7.com/mcp (library docs lookup)
6
6
  * - websearch https://mcp.exa.ai/mcp (web search via Exa)
7
7
  * - grep_app https://mcp.grep.app (code search)
8
+ * - github https://api.githubcopilot.com/mcp/ (GitHub code search)
8
9
  *
9
- * Disable individual MCPs with: FLOWDECK_DISABLE_MCP=context7,websearch,grep_app
10
+ * Disable individual MCPs with: FLOWDECK_DISABLE_MCP=context7,websearch,grep_app,github
10
11
  */
11
12
  type RemoteMcp = {
12
13
  type: "remote";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,KAAK,SAAS,GAAG;IACf,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAOD,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAuC9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,KAAK,SAAS,GAAG;IACf,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,KAAK,CAAC,EAAE,KAAK,CAAA;CACd,CAAA;AAOD,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAmD9D"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Command reference validator.
3
+ * Source of truth: REGISTERED_COMMANDS from supervisor-binding.
4
+ * Used at startup, in tests, and by the response guardrail to prevent
5
+ * agents from suggesting slash commands that don't exist.
6
+ */
7
+ export interface CommandValidationResult {
8
+ valid: boolean;
9
+ command: string;
10
+ reason?: string;
11
+ }
12
+ export interface AuditResult {
13
+ text: string;
14
+ references: string[];
15
+ invalid: CommandValidationResult[];
16
+ valid: CommandValidationResult[];
17
+ hasInvalid: boolean;
18
+ }
19
+ /**
20
+ * Returns true if the given slash command (with or without leading slash) is registered.
21
+ */
22
+ export declare function isValidCommand(ref: string): boolean;
23
+ /**
24
+ * Validate a single command reference string (e.g. "/fd-plan" or "fd-plan").
25
+ */
26
+ export declare function validateCommandReference(ref: string): CommandValidationResult;
27
+ /**
28
+ * Extract all /fd-* slash command references from a text string.
29
+ */
30
+ export declare function extractCommandReferences(text: string): string[];
31
+ /**
32
+ * Extract bare /word references that are missing the fd- prefix.
33
+ * Returns only those that would be valid if prefixed with fd-.
34
+ */
35
+ export declare function extractBarePrefixErrors(text: string): string[];
36
+ /**
37
+ * Audit a text string for invalid command references.
38
+ * Checks all /fd-* patterns against the registered command set.
39
+ */
40
+ export declare function auditTextForInvalidCommands(text: string): AuditResult;
41
+ /**
42
+ * Rewrite a text string by replacing invalid /fd-* command references with
43
+ * a note that the command is unavailable. Leaves valid commands untouched.
44
+ */
45
+ export declare function rewriteInvalidCommandRefs(text: string): string;
46
+ /**
47
+ * Combined full audit: checks both invalid /fd-* command references AND
48
+ * bare /word references missing the fd- prefix. Use this for file integrity tests.
49
+ */
50
+ export interface FullAuditResult extends AuditResult {
51
+ /** Bare /word references that are missing the fd- prefix (e.g. /plan → /fd-plan) */
52
+ barePrefixErrors: string[];
53
+ /** True if any issue was found — either invalid /fd-* refs or bare prefix errors */
54
+ hasAnyIssue: boolean;
55
+ }
56
+ export declare function auditTextFull(text: string): FullAuditResult;
57
+ /**
58
+ * Return the full canonical command inventory for inspection/testing.
59
+ */
60
+ export declare function getCommandInventory(): readonly string[];
61
+ //# sourceMappingURL=command-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-validator.d.ts","sourceRoot":"","sources":["../../src/services/command-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,uBAAuB,EAAE,CAAA;IAClC,KAAK,EAAE,uBAAuB,EAAE,CAAA;IAChC,UAAU,EAAE,OAAO,CAAA;CACpB;AAYD;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,uBAAuB,CAmB7E;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAY9D;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAqBrE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK9D;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,oFAAoF;IACpF,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,oFAAoF;IACpF,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAQ3D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,SAAS,MAAM,EAAE,CAEvD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=command-validator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-validator.test.d.ts","sourceRoot":"","sources":["../../src/services/command-validator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Preflight Explorer Service
3
+ *
4
+ * Performs autonomous codebase exploration before any clarifying question is
5
+ * emitted to the user. Both /fd-quick and /fd-discuss run this first.
6
+ *
7
+ * Contract:
8
+ * 1. exploreRepo(dir) → ExplorationResult (what exists in the project)
9
+ * 2. canAnswerFromEvidence(question, result) → boolean (suppress logic)
10
+ * 3. shouldSuppressQuestion(q, result, history) → SuppressResult
11
+ * 4. deriveTaskContext(task, result) → DerivedTaskContext (task-relative findings)
12
+ *
13
+ * This module reads the filesystem synchronously so it can be used in both
14
+ * synchronous test harnesses and async agent runtimes.
15
+ */
16
+ export interface ExplorationResult {
17
+ /** Whether .planning/STATE.md was found */
18
+ hasStateMD: boolean;
19
+ /** Whether .planning/PROJECT.md was found */
20
+ hasProjectMD: boolean;
21
+ /** Whether AGENTS.md was found at repo root */
22
+ hasAgentsMD: boolean;
23
+ /** Whether .planning/phases/ has any prior phase directories */
24
+ hasPriorPhases: boolean;
25
+ /** Whether .planning/phases/ has any DISCUSS.md from prior sessions */
26
+ hasPriorDiscussions: boolean;
27
+ /** fd-* command names found on disk (from src/commands/*.md) */
28
+ availableCommands: string[];
29
+ /** Agent names registered in this FlowDeck installation */
30
+ availableAgents: string[];
31
+ /** Skill directory names found on disk (from src/skills/ subdirectories) */
32
+ availableSkills: string[];
33
+ /** Tech stack indicators inferred from package.json / go.mod / Cargo.toml etc. */
34
+ techStack: string[];
35
+ /** Config keys present in flowdeck.json if it exists */
36
+ configKeys: string[];
37
+ /** Rules / governance keys declared in flowdeck.json */
38
+ governanceEnabled: boolean;
39
+ /** Implementation pattern hints inferred from src/ directory layout */
40
+ implementationPatterns: string[];
41
+ /** Relative paths of files that seem relevant to the task keywords */
42
+ relevantFiles: string[];
43
+ /** Evidence items that can answer common scoping questions */
44
+ evidenceItems: EvidenceItem[];
45
+ /** ISO timestamp when exploration ran */
46
+ exploredAt: string;
47
+ }
48
+ export interface EvidenceItem {
49
+ /** The kind of question this evidence answers */
50
+ answersQuestion: EvidenceQuestionKind;
51
+ /** Human-readable evidence summary */
52
+ summary: string;
53
+ /** Source path (relative) */
54
+ source: string;
55
+ }
56
+ export type EvidenceQuestionKind = "what-tech-stack" | "is-project-initialized" | "what-is-current-phase" | "what-patterns-exist" | "is-ui-heavy" | "has-existing-tests" | "has-existing-docs" | "has-ci-cd" | "what-agents-available" | "what-commands-available" | "what-skills-available" | "has-prior-decisions" | "has-governance";
57
+ export interface DerivedTaskContext {
58
+ /** Whether the task appears to touch frontend/UI code based on repo evidence */
59
+ likelyUITask: boolean;
60
+ /** Whether the task appears to touch API/backend based on repo evidence */
61
+ likelyBackendTask: boolean;
62
+ /** Whether CI/CD config exists (relevant for deploy tasks) */
63
+ hasCICD: boolean;
64
+ /** Whether existing tests exist in the project */
65
+ hasTests: boolean;
66
+ /** Whether existing documentation exists */
67
+ hasDocs: boolean;
68
+ /** Whether governance layer is active */
69
+ hasGovernance: boolean;
70
+ /** Relevant files for this specific task */
71
+ relevantFiles: string[];
72
+ /** Tech stack summary */
73
+ techStack: string[];
74
+ }
75
+ export interface SuppressResult {
76
+ /** Whether the question should be suppressed */
77
+ suppress: boolean;
78
+ /** Reason for suppression (if suppressed) */
79
+ reason?: string;
80
+ /** True when the question was answered by repo evidence (vs. session dedup) */
81
+ answeredByEvidence?: boolean;
82
+ /** The evidence that answers the question (if suppressed) */
83
+ evidence?: EvidenceItem[];
84
+ }
85
+ /**
86
+ * Explore the repository at `dir` and return structured findings.
87
+ * All filesystem reads are synchronous so this can be called in test harnesses.
88
+ *
89
+ * @param dir - Absolute path to repo root (or closest available directory)
90
+ */
91
+ export declare function exploreRepo(dir: string): ExplorationResult;
92
+ /**
93
+ * Narrow the exploration result to what is specifically relevant for a given task.
94
+ * Populates `relevantFiles` based on task keywords.
95
+ */
96
+ export declare function deriveTaskContext(taskDescription: string, result: ExplorationResult, dir: string): DerivedTaskContext;
97
+ /**
98
+ * Determine whether a candidate question should be suppressed because it can
99
+ * already be answered from repo evidence.
100
+ *
101
+ * A question is suppressed when:
102
+ * 1. The repo contains direct evidence that answers it, OR
103
+ * 2. It was already asked in the current session history, OR
104
+ * 3. It is a trivially answerable question given known project state
105
+ */
106
+ export declare function shouldSuppressQuestion(question: string, result: ExplorationResult, sessionHistory: string[]): SuppressResult;
107
+ /**
108
+ * Check if a specific question can be answered from the exploration result alone.
109
+ */
110
+ export declare function canAnswerFromEvidence(question: string, result: ExplorationResult): boolean;
111
+ /**
112
+ * Attempt to resolve a `clarificationNeeded` classification using exploration
113
+ * context. Returns an updated `clarificationNeeded` flag and an optional
114
+ * resolved task type hint.
115
+ *
116
+ * Called by quick-router after classifyTask when clarificationNeeded=true.
117
+ */
118
+ export interface ExplorationRefinement {
119
+ /** Whether clarification is still required after applying exploration data */
120
+ clarificationStillNeeded: boolean;
121
+ /** Reason clarification is no longer needed (if resolved) */
122
+ resolvedReason?: string;
123
+ /**
124
+ * Evidence-based context to pass to @supervisor if clarification is still needed.
125
+ * This lets the supervisor ask a tighter question.
126
+ */
127
+ supervisorContext?: string;
128
+ }
129
+ export declare function refineClassification(clarificationPrompt: string, result: ExplorationResult, sessionHistory: string[]): ExplorationRefinement;
130
+ //# sourceMappingURL=preflight-explorer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight-explorer.d.ts","sourceRoot":"","sources":["../../src/services/preflight-explorer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Preflight Explorer Tests
3
+ *
4
+ * Covers:
5
+ * - exploreRepo: discovers commands, agents, skills, tech stack from filesystem
6
+ * - canAnswerFromEvidence: correctly identifies suppressible questions
7
+ * - shouldSuppressQuestion: suppresses answered / duplicate questions
8
+ * - deriveTaskContext: narrows findings to task-relevant context
9
+ * - refineClassification: resolves ambiguous classification via evidence
10
+ * - createQuestionGuard: tracks asked questions and prevents duplicates
11
+ * - filterQuestions: returns only questions that pass the guard
12
+ * - needsSupervisorClarification: returns false when all questions are answered
13
+ * - classifyTaskWithContext: uses exploration to resolve ambiguity
14
+ * - createQuickRunState: persists exploration snapshot
15
+ * - /fd-quick performs codebase exploration before asking questions
16
+ * - /fd-discuss performs codebase exploration before asking questions
17
+ * - repo evidence prevents unnecessary human questions
18
+ * - supervisor-agent receives only genuine ambiguity
19
+ * - worker agents do not ask ad hoc questions
20
+ * - feature/bug/UI/docs tasks route correctly after preflight
21
+ * - exploration results are stored and reused
22
+ * - repeated question suppression works
23
+ */
24
+ export {};
25
+ //# sourceMappingURL=preflight-explorer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight-explorer.test.d.ts","sourceRoot":"","sources":["../../src/services/preflight-explorer.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Question Guard Service
3
+ *
4
+ * Prevents redundant or unnecessary questions from being emitted to the user.
5
+ *
6
+ * Both /fd-quick and /fd-discuss use this guard before forwarding any
7
+ * clarifying question to @supervisor. Worker agents MUST NOT call ask_user
8
+ * directly — they check this guard first, and if the answer exists in repo
9
+ * evidence or session history, the question is dropped.
10
+ *
11
+ * Contract:
12
+ * 1. createQuestionGuard(history?) → QuestionGuard instance
13
+ * 2. guard.check(question, evidence) → CheckResult
14
+ * 3. guard.record(question) → void
15
+ * 4. guard.getAsked() → string[]
16
+ *
17
+ * Only questions that pass the guard should be forwarded to @supervisor.
18
+ * @supervisor asks the human. Worker agents never ask the human directly.
19
+ */
20
+ import type { ExplorationResult } from "./preflight-explorer";
21
+ export interface CheckResult {
22
+ /** Whether the question should be allowed through to @supervisor */
23
+ allow: boolean;
24
+ /** Reason the question was blocked (when allow=false) */
25
+ blockReason?: string;
26
+ /** Whether the block was due to repo evidence answering it */
27
+ answeredByEvidence?: boolean;
28
+ /** Whether the block was due to a duplicate question */
29
+ duplicate?: boolean;
30
+ }
31
+ export interface QuestionGuard {
32
+ /**
33
+ * Check whether a question should be forwarded to @supervisor.
34
+ *
35
+ * Returns allow=true only when:
36
+ * - The question has not been asked before in this session
37
+ * - The question cannot be answered from repo evidence
38
+ * - The question is not trivially implied by known state
39
+ */
40
+ check(question: string, exploration: ExplorationResult | null): CheckResult;
41
+ /**
42
+ * Record that a question was asked. Call this after forwarding to @supervisor
43
+ * so future identical questions are suppressed.
44
+ */
45
+ record(question: string): void;
46
+ /** Return all questions recorded in this guard instance. */
47
+ getAsked(): string[];
48
+ /** Reset the guard (for new session/run). */
49
+ reset(): void;
50
+ }
51
+ /**
52
+ * Create a QuestionGuard.
53
+ *
54
+ * @param initialHistory - Questions already asked in this session (for
55
+ * persistence across restarts). Pass an empty array for new sessions.
56
+ */
57
+ export declare function createQuestionGuard(initialHistory?: string[]): QuestionGuard;
58
+ /**
59
+ * Convenience: check a list of candidate questions and return only those
60
+ * that should be forwarded to @supervisor. Records allowed questions.
61
+ */
62
+ export declare function filterQuestions(candidates: string[], guard: QuestionGuard, exploration: ExplorationResult | null): string[];
63
+ /**
64
+ * Determine whether supervisor clarification is warranted at all.
65
+ * Returns false when:
66
+ * - No questions remain after evidence filtering
67
+ * - All questions were answered by the exploration result
68
+ *
69
+ * Call this before invoking @supervisor to avoid empty escalations.
70
+ */
71
+ export declare function needsSupervisorClarification(questions: string[], guard: QuestionGuard, exploration: ExplorationResult | null): boolean;
72
+ /**
73
+ * Strict check for worker agents (coder, planner, tester, etc.).
74
+ *
75
+ * Worker agents MUST NOT ask the human directly. They must either:
76
+ * a) use repo evidence (this returns { canProceed: true, evidence })
77
+ * b) report missing data to orchestrator/supervisor
78
+ *
79
+ * This function decides which path to take.
80
+ */
81
+ export interface WorkerAgentDecision {
82
+ /** True when the worker can proceed using evidence alone */
83
+ canProceed: boolean;
84
+ /** True when the worker must stop and report missing data upward */
85
+ mustEscalate: boolean;
86
+ /** Evidence that allows the worker to proceed (when canProceed=true) */
87
+ evidence?: string;
88
+ /** What data is missing (when mustEscalate=true) */
89
+ missingData?: string;
90
+ }
91
+ export declare function workerAgentDecision(requiredInfo: string, exploration: ExplorationResult): WorkerAgentDecision;
92
+ //# sourceMappingURL=question-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-guard.d.ts","sourceRoot":"","sources":["../../src/services/question-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAG7D,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,KAAK,EAAE,OAAO,CAAA;IACd,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,GAAG,IAAI,GAAG,WAAW,CAAA;IAC3E;;;OAGG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,4DAA4D;IAC5D,QAAQ,IAAI,MAAM,EAAE,CAAA;IACpB,6CAA6C;IAC7C,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,GAAE,MAAM,EAAO,GAAG,aAAa,CA0ChF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAAE,EACpB,KAAK,EAAE,aAAa,EACpB,WAAW,EAAE,iBAAiB,GAAG,IAAI,GACpC,MAAM,EAAE,CASV;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,EAAE,aAAa,EACpB,WAAW,EAAE,iBAAiB,GAAG,IAAI,GACpC,OAAO,CAGT;AAMD;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,UAAU,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,YAAY,EAAE,OAAO,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,iBAAiB,GAC7B,mBAAmB,CAmBrB"}