@bicorne/task-flow 0.1.0

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 (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/SKILL.md +250 -0
  4. package/assets/.harnessrc +10 -0
  5. package/assets/PHASE-task.json.example +50 -0
  6. package/assets/design.md +69 -0
  7. package/assets/hooks.json +15 -0
  8. package/assets/product-requirement.md +82 -0
  9. package/assets/schema.json +127 -0
  10. package/assets/tasks.md +26 -0
  11. package/dist/commands/analyze.d.ts +32 -0
  12. package/dist/commands/analyze.js +338 -0
  13. package/dist/commands/archive.d.ts +11 -0
  14. package/dist/commands/archive.js +53 -0
  15. package/dist/commands/design.d.ts +38 -0
  16. package/dist/commands/design.js +492 -0
  17. package/dist/commands/extract.d.ts +31 -0
  18. package/dist/commands/extract.js +477 -0
  19. package/dist/commands/init.d.ts +24 -0
  20. package/dist/commands/init.js +165 -0
  21. package/dist/commands/merge/index.d.ts +17 -0
  22. package/dist/commands/merge/index.js +322 -0
  23. package/dist/commands/merge/merger.d.ts +18 -0
  24. package/dist/commands/merge/merger.js +151 -0
  25. package/dist/commands/merge/types.d.ts +67 -0
  26. package/dist/commands/merge/types.js +6 -0
  27. package/dist/commands/merge/validators.d.ts +14 -0
  28. package/dist/commands/merge/validators.js +147 -0
  29. package/dist/commands/merge.d.ts +7 -0
  30. package/dist/commands/merge.js +15 -0
  31. package/dist/commands/start.d.ts +32 -0
  32. package/dist/commands/start.js +265 -0
  33. package/dist/commands/status.d.ts +15 -0
  34. package/dist/commands/status.js +143 -0
  35. package/dist/commands/sync.d.ts +11 -0
  36. package/dist/commands/sync.js +58 -0
  37. package/dist/commands/tasks-gen/doc-parser.d.ts +7 -0
  38. package/dist/commands/tasks-gen/doc-parser.js +259 -0
  39. package/dist/commands/tasks-gen/generators.d.ts +33 -0
  40. package/dist/commands/tasks-gen/generators.js +141 -0
  41. package/dist/commands/tasks-gen/index.d.ts +30 -0
  42. package/dist/commands/tasks-gen/index.js +345 -0
  43. package/dist/commands/tasks-gen/parsers.d.ts +29 -0
  44. package/dist/commands/tasks-gen/parsers.js +272 -0
  45. package/dist/commands/tasks-gen/templates.d.ts +8 -0
  46. package/dist/commands/tasks-gen/templates.js +37 -0
  47. package/dist/commands/tasks-gen/types.d.ts +71 -0
  48. package/dist/commands/tasks-gen/types.js +17 -0
  49. package/dist/commands/tasks-gen/validators.d.ts +14 -0
  50. package/dist/commands/tasks-gen/validators.js +54 -0
  51. package/dist/commands/tasks.d.ts +9 -0
  52. package/dist/commands/tasks.js +22 -0
  53. package/dist/commands/worktree.d.ts +28 -0
  54. package/dist/commands/worktree.js +275 -0
  55. package/dist/hooks/check-prd-exists.d.ts +20 -0
  56. package/dist/hooks/check-prd-exists.js +61 -0
  57. package/dist/hooks/check-worktree-conflict.d.ts +34 -0
  58. package/dist/hooks/check-worktree-conflict.js +107 -0
  59. package/dist/hooks/hook-runner/executor.d.ts +18 -0
  60. package/dist/hooks/hook-runner/executor.js +143 -0
  61. package/dist/hooks/hook-runner/index.d.ts +64 -0
  62. package/dist/hooks/hook-runner/index.js +220 -0
  63. package/dist/hooks/hook-runner/loader.d.ts +23 -0
  64. package/dist/hooks/hook-runner/loader.js +126 -0
  65. package/dist/hooks/hook-runner/types.d.ts +59 -0
  66. package/dist/hooks/hook-runner/types.js +6 -0
  67. package/dist/hooks/hook-runner.d.ts +9 -0
  68. package/dist/hooks/hook-runner.js +30 -0
  69. package/dist/hooks/phase-complete-detector.d.ts +35 -0
  70. package/dist/hooks/phase-complete-detector.js +203 -0
  71. package/dist/hooks/phase-gate-validator.d.ts +76 -0
  72. package/dist/hooks/phase-gate-validator.js +407 -0
  73. package/dist/hooks/save-checkpoint.d.ts +43 -0
  74. package/dist/hooks/save-checkpoint.js +144 -0
  75. package/dist/hooks/start-mcp-servers.d.ts +50 -0
  76. package/dist/hooks/start-mcp-servers.js +75 -0
  77. package/dist/hooks/stop-mcp-servers.d.ts +40 -0
  78. package/dist/hooks/stop-mcp-servers.js +58 -0
  79. package/dist/index.d.ts +29 -0
  80. package/dist/index.js +238 -0
  81. package/dist/lib/archive.d.ts +31 -0
  82. package/dist/lib/archive.js +226 -0
  83. package/dist/lib/config.d.ts +93 -0
  84. package/dist/lib/config.js +251 -0
  85. package/dist/lib/constants.d.ts +222 -0
  86. package/dist/lib/constants.js +247 -0
  87. package/dist/lib/interactive.d.ts +31 -0
  88. package/dist/lib/interactive.js +166 -0
  89. package/dist/lib/mcp-client.d.ts +156 -0
  90. package/dist/lib/mcp-client.js +370 -0
  91. package/dist/lib/state.d.ts +119 -0
  92. package/dist/lib/state.js +293 -0
  93. package/dist/slash/executor.d.ts +22 -0
  94. package/dist/slash/executor.js +259 -0
  95. package/dist/slash/index.d.ts +11 -0
  96. package/dist/slash/index.js +45 -0
  97. package/dist/slash/parser.d.ts +24 -0
  98. package/dist/slash/parser.js +101 -0
  99. package/dist/slash/registry.d.ts +22 -0
  100. package/dist/slash/registry.js +155 -0
  101. package/dist/spec/openspec-to-task/builders.d.ts +107 -0
  102. package/dist/spec/openspec-to-task/builders.js +138 -0
  103. package/dist/spec/openspec-to-task/index.d.ts +20 -0
  104. package/dist/spec/openspec-to-task/index.js +182 -0
  105. package/dist/spec/openspec-to-task/parsers.d.ts +65 -0
  106. package/dist/spec/openspec-to-task/parsers.js +232 -0
  107. package/dist/spec/openspec-to-task/types.d.ts +49 -0
  108. package/dist/spec/openspec-to-task/types.js +6 -0
  109. package/dist/spec/sync-openspec-to-task.d.ts +7 -0
  110. package/dist/spec/sync-openspec-to-task.js +21 -0
  111. package/dist/spec/sync-task-to-openspec.d.ts +27 -0
  112. package/dist/spec/sync-task-to-openspec.js +288 -0
  113. package/dist/types/ai-context.d.ts +108 -0
  114. package/dist/types/ai-context.js +9 -0
  115. package/package.json +66 -0
  116. package/references/AI-CONVERSATION-TUTORIAL.md +270 -0
  117. package/references/CLI-TUTORIAL.md +447 -0
  118. package/references/GIT-WORKTREE-SOP.md +109 -0
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * hooks/hook-runner/loader.ts
4
+ * Hook loading utilities
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.getDefaultHooksPath = getDefaultHooksPath;
41
+ exports.loadHooksConfig = loadHooksConfig;
42
+ exports.loadHookScriptAsync = loadHookScriptAsync;
43
+ exports.loadHookScript = loadHookScript;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const config_1 = require("../../lib/config");
47
+ /**
48
+ * Default hooks config
49
+ */
50
+ const DEFAULT_HOOKS_CONFIG = {
51
+ hooks: {},
52
+ };
53
+ /**
54
+ * Get default hooks path
55
+ */
56
+ function getDefaultHooksPath(config) {
57
+ return path.resolve(config.harnessRootAbs, 'hooks.json');
58
+ }
59
+ /**
60
+ * Load hooks config from file
61
+ */
62
+ function loadHooksConfig(config) {
63
+ const cfg = config || (0, config_1.loadConfig)();
64
+ const hooksPath = getDefaultHooksPath(cfg);
65
+ if (!fs.existsSync(hooksPath)) {
66
+ return { ...DEFAULT_HOOKS_CONFIG, configPath: hooksPath };
67
+ }
68
+ try {
69
+ const content = fs.readFileSync(hooksPath, 'utf8');
70
+ const parsed = JSON.parse(content);
71
+ return {
72
+ ...DEFAULT_HOOKS_CONFIG,
73
+ ...parsed,
74
+ configPath: hooksPath,
75
+ };
76
+ }
77
+ catch (error) {
78
+ console.warn('[HOOK-RUNNER] Failed to load hooks config:', error instanceof Error ? error.message : 'Unknown error');
79
+ return { ...DEFAULT_HOOKS_CONFIG, configPath: hooksPath };
80
+ }
81
+ }
82
+ /**
83
+ * Load hook script with dynamic import to avoid require.cache issues
84
+ */
85
+ async function loadHookScriptAsync(scriptPath, config) {
86
+ const cfg = config || (0, config_1.loadConfig)();
87
+ const absolutePath = path.isAbsolute(scriptPath)
88
+ ? scriptPath
89
+ : path.resolve(cfg.projectRoot, scriptPath);
90
+ if (!fs.existsSync(absolutePath)) {
91
+ console.warn('[HOOK-RUNNER] Hook script not found:', absolutePath);
92
+ return null;
93
+ }
94
+ try {
95
+ const fileUrl = `file://${absolutePath}?t=${Date.now()}`;
96
+ const hookModule = await Promise.resolve(`${fileUrl}`).then(s => __importStar(require(s)));
97
+ return hookModule.default || hookModule;
98
+ }
99
+ catch (error) {
100
+ console.error('[HOOK-RUNNER] Failed to load hook script:', absolutePath, error instanceof Error ? error.message : 'Unknown error');
101
+ return null;
102
+ }
103
+ }
104
+ /**
105
+ * Load hook script (legacy sync version)
106
+ * @deprecated Use loadHookScriptAsync instead
107
+ */
108
+ function loadHookScript(scriptPath, config) {
109
+ const cfg = config || (0, config_1.loadConfig)();
110
+ const absolutePath = path.isAbsolute(scriptPath)
111
+ ? scriptPath
112
+ : path.resolve(cfg.projectRoot, scriptPath);
113
+ if (!fs.existsSync(absolutePath)) {
114
+ console.warn('[HOOK-RUNNER] Hook script not found:', absolutePath);
115
+ return null;
116
+ }
117
+ try {
118
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
119
+ const hookModule = require(absolutePath);
120
+ return hookModule;
121
+ }
122
+ catch (error) {
123
+ console.error('[HOOK-RUNNER] Failed to load hook script:', absolutePath, error instanceof Error ? error.message : 'Unknown error');
124
+ return null;
125
+ }
126
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * hooks/hook-runner/types.ts
3
+ * Type definitions for hook runner
4
+ */
5
+ import { Config } from '../../lib/config';
6
+ /**
7
+ * Hook lifecycle information
8
+ */
9
+ export interface HookLifecycle {
10
+ phase: string;
11
+ timing: string;
12
+ }
13
+ /**
14
+ * Hook configuration
15
+ */
16
+ export interface HookConfig {
17
+ name: string;
18
+ script: string;
19
+ enabled?: boolean;
20
+ timeout?: number;
21
+ hookName?: string;
22
+ lifecycle?: HookLifecycle;
23
+ }
24
+ /**
25
+ * Hook execution context
26
+ */
27
+ export interface HookContext {
28
+ config?: Config;
29
+ [key: string]: unknown;
30
+ }
31
+ /**
32
+ * Hook execution result
33
+ */
34
+ export interface HookResult {
35
+ name?: string;
36
+ success: boolean;
37
+ result?: unknown;
38
+ error?: string;
39
+ script?: string;
40
+ stack?: string;
41
+ }
42
+ /**
43
+ * Hook execution engine result
44
+ */
45
+ export interface HookExecutionResult {
46
+ success: boolean;
47
+ executed: number;
48
+ results: HookResult[];
49
+ message?: string;
50
+ hookName?: string;
51
+ hookNames?: string[];
52
+ }
53
+ /**
54
+ * Loaded hooks config
55
+ */
56
+ export interface LoadedHooksConfig {
57
+ hooks: Record<string, HookConfig[]>;
58
+ configPath: string;
59
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * hooks/hook-runner/types.ts
4
+ * Type definitions for hook runner
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * hooks/hook-runner.ts
3
+ * Backward-compatible re-export from hook-runner/ module
4
+ * @deprecated Import from './hook-runner/index' directly
5
+ */
6
+ export { runHook, runHookSync, runHookChain, getHookLifecycle, getHooksForPhase, validateHookConfig, createHookConfig, HOOK_LIFECYCLE, default, } from './hook-runner/index';
7
+ export { HookLifecycle, HookConfig, HookContext, HookResult, HookExecutionResult, LoadedHooksConfig, } from './hook-runner/types';
8
+ export { getDefaultHooksPath, loadHooksConfig, loadHookScript, loadHookScriptAsync, } from './hook-runner/loader';
9
+ export { executeHookFunction, executeSingleHookAsync, executeSingleHookSync, } from './hook-runner/executor';
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ /**
3
+ * hooks/hook-runner.ts
4
+ * Backward-compatible re-export from hook-runner/ module
5
+ * @deprecated Import from './hook-runner/index' directly
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.executeSingleHookSync = exports.executeSingleHookAsync = exports.executeHookFunction = exports.loadHookScriptAsync = exports.loadHookScript = exports.loadHooksConfig = exports.getDefaultHooksPath = exports.default = exports.HOOK_LIFECYCLE = exports.createHookConfig = exports.validateHookConfig = exports.getHooksForPhase = exports.getHookLifecycle = exports.runHookChain = exports.runHookSync = exports.runHook = void 0;
12
+ var index_1 = require("./hook-runner/index");
13
+ Object.defineProperty(exports, "runHook", { enumerable: true, get: function () { return index_1.runHook; } });
14
+ Object.defineProperty(exports, "runHookSync", { enumerable: true, get: function () { return index_1.runHookSync; } });
15
+ Object.defineProperty(exports, "runHookChain", { enumerable: true, get: function () { return index_1.runHookChain; } });
16
+ Object.defineProperty(exports, "getHookLifecycle", { enumerable: true, get: function () { return index_1.getHookLifecycle; } });
17
+ Object.defineProperty(exports, "getHooksForPhase", { enumerable: true, get: function () { return index_1.getHooksForPhase; } });
18
+ Object.defineProperty(exports, "validateHookConfig", { enumerable: true, get: function () { return index_1.validateHookConfig; } });
19
+ Object.defineProperty(exports, "createHookConfig", { enumerable: true, get: function () { return index_1.createHookConfig; } });
20
+ Object.defineProperty(exports, "HOOK_LIFECYCLE", { enumerable: true, get: function () { return index_1.HOOK_LIFECYCLE; } });
21
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(index_1).default; } });
22
+ var loader_1 = require("./hook-runner/loader");
23
+ Object.defineProperty(exports, "getDefaultHooksPath", { enumerable: true, get: function () { return loader_1.getDefaultHooksPath; } });
24
+ Object.defineProperty(exports, "loadHooksConfig", { enumerable: true, get: function () { return loader_1.loadHooksConfig; } });
25
+ Object.defineProperty(exports, "loadHookScript", { enumerable: true, get: function () { return loader_1.loadHookScript; } });
26
+ Object.defineProperty(exports, "loadHookScriptAsync", { enumerable: true, get: function () { return loader_1.loadHookScriptAsync; } });
27
+ var executor_1 = require("./hook-runner/executor");
28
+ Object.defineProperty(exports, "executeHookFunction", { enumerable: true, get: function () { return executor_1.executeHookFunction; } });
29
+ Object.defineProperty(exports, "executeSingleHookAsync", { enumerable: true, get: function () { return executor_1.executeSingleHookAsync; } });
30
+ Object.defineProperty(exports, "executeSingleHookSync", { enumerable: true, get: function () { return executor_1.executeSingleHookSync; } });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * hooks/phase-complete-detector.ts
3
+ * Detect phase completion and update state
4
+ */
5
+ import { Config } from '../lib/config';
6
+ import { State } from '../lib/state';
7
+ import { GateResult } from './phase-gate-validator';
8
+ export interface DetectOptions {
9
+ filePath: string;
10
+ config: Config;
11
+ state: State;
12
+ }
13
+ export interface DetectResult {
14
+ detected: boolean;
15
+ reason?: string;
16
+ blocked?: boolean;
17
+ gateResult?: GateResult;
18
+ phaseCompleted?: string;
19
+ newPhase?: string;
20
+ }
21
+ /**
22
+ * Update task JSON phase status with error handling
23
+ */
24
+ export declare function updateTaskJsonPhaseStatus(taskPath: string, phaseName: string, status: string): boolean;
25
+ /**
26
+ * Detect phase completion
27
+ * @param options - Options
28
+ * @returns Detection result
29
+ */
30
+ export declare function detectPhaseCompletion(options: DetectOptions): DetectResult;
31
+ declare const _default: {
32
+ detectPhaseCompletion: typeof detectPhaseCompletion;
33
+ updateTaskJsonPhaseStatus: typeof updateTaskJsonPhaseStatus;
34
+ };
35
+ export default _default;
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ /**
3
+ * hooks/phase-complete-detector.ts
4
+ * Detect phase completion and update state
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.updateTaskJsonPhaseStatus = updateTaskJsonPhaseStatus;
41
+ exports.detectPhaseCompletion = detectPhaseCompletion;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const config_1 = require("../lib/config");
45
+ const state_1 = require("../lib/state");
46
+ const archive_1 = require("../lib/archive");
47
+ const phase_gate_validator_1 = require("./phase-gate-validator");
48
+ const sync_task_to_openspec_1 = require("../spec/sync-task-to-openspec");
49
+ /**
50
+ * Update task JSON phase status with error handling
51
+ */
52
+ function updateTaskJsonPhaseStatus(taskPath, phaseName, status) {
53
+ if (!fs.existsSync(taskPath)) {
54
+ console.warn(`[PHASE-DETECTOR] Task file not found: ${taskPath}`);
55
+ return false;
56
+ }
57
+ try {
58
+ const content = fs.readFileSync(taskPath, 'utf8');
59
+ const taskJson = JSON.parse(content);
60
+ taskJson.status = status;
61
+ fs.writeFileSync(taskPath, JSON.stringify(taskJson, null, 2), 'utf8');
62
+ console.log(`[PHASE-DETECTOR] Updated task JSON: ${phaseName} phase -> ${status}`);
63
+ return true;
64
+ }
65
+ catch (e) {
66
+ console.error(`[PHASE-DETECTOR] Error updating task JSON: ${e.message}`);
67
+ return false;
68
+ }
69
+ }
70
+ /**
71
+ * Log gate blocked warnings with type-safe access
72
+ */
73
+ function logGateBlockedWarnings(gateResult) {
74
+ if (!gateResult) {
75
+ return;
76
+ }
77
+ console.warn(`[PHASE-DETECTOR] Phase blocked: ${gateResult.reason}`);
78
+ const missingValidations = Array.isArray(gateResult.missingValidations)
79
+ ? gateResult.missingValidations
80
+ : [];
81
+ if (missingValidations.length > 0) {
82
+ console.warn(`[PHASE-DETECTOR] Missing: ${missingValidations.join(', ')}`);
83
+ }
84
+ }
85
+ /**
86
+ * Detect phase completion
87
+ * @param options - Options
88
+ * @returns Detection result
89
+ */
90
+ function detectPhaseCompletion(options) {
91
+ const { filePath, config, state } = options;
92
+ if (!filePath.includes('.harness/')) {
93
+ return { detected: false, reason: 'not-harness-file' };
94
+ }
95
+ const taskId = state.currentTask;
96
+ if (!taskId) {
97
+ return { detected: false, reason: 'no-current-task' };
98
+ }
99
+ const gateResult = (0, phase_gate_validator_1.assessTransitionGate)({ taskId, filePath, state, config });
100
+ if (gateResult.skipped) {
101
+ return { detected: false, reason: 'gate-skipped' };
102
+ }
103
+ if (!gateResult.allowed) {
104
+ // Update state with blocked gate info
105
+ (0, state_1.updateState)({
106
+ lastGateBlockedAt: new Date().toISOString(),
107
+ lastGateBlockedReason: gateResult.reason,
108
+ lastGateBlockedPhase: gateResult.phaseCompleted || null,
109
+ lastMissingValidations: gateResult.missingValidations || [],
110
+ }, config);
111
+ // Archive task state
112
+ (0, archive_1.archiveTaskState)({
113
+ cwd: config.projectRoot,
114
+ taskId,
115
+ eventType: 'gate_blocked',
116
+ status: state.status || 'running',
117
+ phase: state.currentPhase || undefined,
118
+ phaseCompleted: gateResult.phaseCompleted || state.phaseCompleted || undefined,
119
+ reviewConclusion: state.reviewConclusion || undefined,
120
+ reason: gateResult.reason,
121
+ });
122
+ // Sync to Spec
123
+ (0, sync_task_to_openspec_1.syncTaskToSpec)({ cwd: config.projectRoot, taskId });
124
+ return {
125
+ detected: true,
126
+ blocked: true,
127
+ gateResult,
128
+ };
129
+ }
130
+ // Phase transition allowed
131
+ const newPhase = gateResult.nextPhase;
132
+ const phaseCompleted = gateResult.phaseCompleted;
133
+ // Update state
134
+ (0, state_1.updateState)({
135
+ currentPhase: newPhase,
136
+ phaseCompleted,
137
+ reviewConclusion: gateResult.reviewConclusion || state.reviewConclusion || null,
138
+ lastGateBlockedAt: null,
139
+ lastGateBlockedReason: null,
140
+ lastGateBlockedPhase: null,
141
+ lastMissingValidations: [],
142
+ }, config);
143
+ // Update task JSON phase statuses
144
+ const taskPath = path.resolve(config.tasksDirAbs, `${taskId}.json`);
145
+ if (phaseCompleted) {
146
+ updateTaskJsonPhaseStatus(taskPath, phaseCompleted, 'completed');
147
+ }
148
+ if (newPhase && newPhase !== 'merge') {
149
+ updateTaskJsonPhaseStatus(taskPath, newPhase, 'in_progress');
150
+ }
151
+ // Archive task state
152
+ (0, archive_1.archiveTaskState)({
153
+ cwd: config.projectRoot,
154
+ taskId,
155
+ eventType: 'phase_transition',
156
+ status: state.status || 'running',
157
+ phase: newPhase || undefined,
158
+ phaseCompleted: phaseCompleted || undefined,
159
+ reviewConclusion: gateResult.reviewConclusion || undefined,
160
+ });
161
+ // Sync to Spec
162
+ (0, sync_task_to_openspec_1.syncTaskToSpec)({ cwd: config.projectRoot, taskId });
163
+ return {
164
+ detected: true,
165
+ blocked: false,
166
+ phaseCompleted,
167
+ newPhase,
168
+ gateResult,
169
+ };
170
+ }
171
+ /**
172
+ * Main entry point for hook usage
173
+ */
174
+ function main() {
175
+ const input = JSON.parse(process.argv[2] || '{}');
176
+ const filePath = input.tool_input?.file_path || input.tool_input?.path || '';
177
+ const config = (0, config_1.loadConfig)();
178
+ const state = (0, state_1.loadState)(config);
179
+ if (!state?.currentTask) {
180
+ process.exit(0);
181
+ }
182
+ const result = detectPhaseCompletion({ filePath, config, state });
183
+ if (!result.detected || result.reason) {
184
+ process.exit(0);
185
+ }
186
+ if (result.blocked) {
187
+ logGateBlockedWarnings(result.gateResult);
188
+ }
189
+ else {
190
+ console.log(`[PHASE-DETECTOR] Phase transition: ${result.phaseCompleted} -> ${result.newPhase}`);
191
+ if (result.gateResult?.reviewConclusion) {
192
+ console.log(`[PHASE-DETECTOR] Review conclusion: ${result.gateResult.reviewConclusion}`);
193
+ }
194
+ }
195
+ process.exit(0);
196
+ }
197
+ if (require.main === module) {
198
+ main();
199
+ }
200
+ exports.default = {
201
+ detectPhaseCompletion,
202
+ updateTaskJsonPhaseStatus,
203
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * hooks/phase-gate-validator.ts
3
+ * Validate phase transition gates
4
+ */
5
+ import { Config } from '../lib/config';
6
+ import { State } from '../lib/state';
7
+ export interface TaskJson {
8
+ status?: string;
9
+ validation?: {
10
+ commands?: string[];
11
+ manual_checks?: string[];
12
+ };
13
+ implementation?: {
14
+ notes?: string;
15
+ steps?: Array<string | {
16
+ step?: string;
17
+ }>;
18
+ };
19
+ metadata?: {
20
+ outputs?: {
21
+ task?: string;
22
+ };
23
+ };
24
+ }
25
+ export interface GateResult {
26
+ allowed: boolean;
27
+ skipped: boolean;
28
+ phaseCompleted?: string;
29
+ nextPhase?: string;
30
+ reason?: string;
31
+ missingValidations?: string[];
32
+ details?: string[];
33
+ validations?: string[];
34
+ reviewConclusion?: string | null;
35
+ }
36
+ export interface GateOptions {
37
+ taskId: string;
38
+ filePath: string;
39
+ state: State;
40
+ config: Config;
41
+ }
42
+ export declare function normalizePath(inputPath: string | undefined, projectRoot: string): string;
43
+ export declare function normalizeValidationName(name: string): string;
44
+ export declare function getValidationAliases(validationName: string): string[];
45
+ export declare function isPassedStatus(status: string | undefined): boolean;
46
+ export declare function parseReviewConclusion(content: string): string | null;
47
+ export declare function parseReviewValidationMap(content: string): Record<string, string>;
48
+ export declare function parseReviewReport(content: string): {
49
+ conclusion: string | null;
50
+ validationMap: Record<string, string>;
51
+ };
52
+ export declare function deriveTransitionFromFilePath(filePath: string, config: Config): {
53
+ phaseCompleted: string;
54
+ nextPhase: string;
55
+ relativePath: string;
56
+ } | null;
57
+ export declare function getTaskFilePath(config: Config, taskId: string): string;
58
+ export declare function readTaskJson(config: Config, taskId: string): TaskJson | null;
59
+ export declare function getValidationItems(taskJson: TaskJson | null, phaseName: string): string[];
60
+ export declare function getPlanningSummary(taskJson: TaskJson | null): string;
61
+ export declare function getPlanningList(taskJson: TaskJson | null, key: string): string[];
62
+ export declare function getSnapshotValidationMap(snapshotPath: string): Record<string, boolean>;
63
+ export declare function hasValidationPassed(validationName: string, snapshotValidationMap: Record<string, boolean | string>): boolean;
64
+ /**
65
+ * Assess transition gate
66
+ * @param options - Options
67
+ * @returns Gate assessment result
68
+ */
69
+ export declare function assessTransitionGate(options: GateOptions): GateResult;
70
+ declare const _default: {
71
+ assessTransitionGate: typeof assessTransitionGate;
72
+ deriveTransitionFromFilePath: typeof deriveTransitionFromFilePath;
73
+ parseReviewConclusion: typeof parseReviewConclusion;
74
+ parseReviewReport: typeof parseReviewReport;
75
+ };
76
+ export default _default;