@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,107 @@
1
+ "use strict";
2
+ /**
3
+ * hooks/check-worktree-conflict.ts
4
+ * Check for worktree conflicts to prevent multi-agent file conflicts
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.normalizePath = normalizePath;
41
+ exports.checkWorktreeConflict = checkWorktreeConflict;
42
+ const path = __importStar(require("path"));
43
+ const config_1 = require("../lib/config");
44
+ const state_1 = require("../lib/state");
45
+ function normalizePath(inputPath, projectRoot) {
46
+ if (!inputPath || typeof inputPath !== 'string') {
47
+ return '';
48
+ }
49
+ const absolutePath = path.isAbsolute(inputPath) ? inputPath : path.resolve(projectRoot, inputPath);
50
+ return path.relative(projectRoot, absolutePath).replace(/\\/g, '/');
51
+ }
52
+ /**
53
+ * Check for worktree conflicts
54
+ * @param options - Options
55
+ * @returns Result
56
+ */
57
+ function checkWorktreeConflict(options) {
58
+ const { input, config } = options;
59
+ const currentWorktree = process.env.WORKTREE_NAME;
60
+ const activeWorktrees = (0, state_1.getActiveWorktrees)(config);
61
+ if (activeWorktrees.length <= 1) {
62
+ return { conflict: false, reason: 'single-worktree' };
63
+ }
64
+ // Check if current operation involves file path
65
+ const targetFile = normalizePath(input.tool_input?.file_path || input.tool_input?.path, config.projectRoot);
66
+ if (!targetFile) {
67
+ return { conflict: false, reason: 'no-file-path' };
68
+ }
69
+ // Check if other active worktrees are modifying the same file
70
+ for (const wt of activeWorktrees) {
71
+ if (wt.name !== currentWorktree) {
72
+ const wtChanges = (wt.pendingChanges || [])
73
+ .map((p) => normalizePath(p, config.projectRoot))
74
+ .filter(Boolean);
75
+ if (wtChanges.includes(targetFile)) {
76
+ return {
77
+ conflict: true,
78
+ file: targetFile,
79
+ conflictingWorktree: wt.name,
80
+ message: `File ${targetFile} is being modified by ${wt.name}`,
81
+ };
82
+ }
83
+ }
84
+ }
85
+ return { conflict: false, reason: 'no-conflict' };
86
+ }
87
+ /**
88
+ * Main entry point for hook usage
89
+ */
90
+ function main() {
91
+ const input = JSON.parse(process.argv[2] || '{}');
92
+ const config = (0, config_1.loadConfig)();
93
+ (0, state_1.loadState)(config);
94
+ const result = checkWorktreeConflict({ input, config });
95
+ if (result.conflict) {
96
+ console.error(`[CONFLICT] ${result.message}`);
97
+ console.error(`[CONFLICT] Suggestion: Wait for ${result.conflictingWorktree} to complete or use a different file`);
98
+ process.exit(2); // Non-zero exit to block operation
99
+ }
100
+ process.exit(0);
101
+ }
102
+ if (require.main === module) {
103
+ main();
104
+ }
105
+ exports.default = {
106
+ checkWorktreeConflict,
107
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * hooks/hook-runner/executor.ts
3
+ * Hook execution utilities
4
+ */
5
+ import { Config } from '../../lib/config';
6
+ import { HookConfig, HookContext, HookResult } from './types';
7
+ /**
8
+ * Execute a hook module
9
+ */
10
+ export declare function executeHookFunction(hookModule: Record<string, unknown> | null, context: HookContext): Promise<HookResult>;
11
+ /**
12
+ * Execute a single hook with timeout control
13
+ */
14
+ export declare function executeSingleHookAsync(hookConfig: HookConfig, context: HookContext, config: Config): Promise<HookResult>;
15
+ /**
16
+ * Execute a single hook synchronously
17
+ */
18
+ export declare function executeSingleHookSync(hookConfig: HookConfig, context: HookContext, config: Config): HookResult;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * hooks/hook-runner/executor.ts
4
+ * Hook execution 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.executeHookFunction = executeHookFunction;
41
+ exports.executeSingleHookAsync = executeSingleHookAsync;
42
+ exports.executeSingleHookSync = executeSingleHookSync;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const loader_1 = require("./loader");
46
+ /**
47
+ * Execute a hook module
48
+ */
49
+ async function executeHookFunction(hookModule, context) {
50
+ if (!hookModule) {
51
+ return { success: false, error: 'hook-module-not-found' };
52
+ }
53
+ const hookFn = hookModule?.run
54
+ || hookModule?.execute
55
+ || hookModule?.default
56
+ || hookModule;
57
+ if (typeof hookFn !== 'function') {
58
+ return { success: false, error: 'hook-function-not-found' };
59
+ }
60
+ try {
61
+ const result = hookFn(context);
62
+ if (result && typeof result.then === 'function') {
63
+ const asyncResult = await result;
64
+ return { success: true, result: asyncResult };
65
+ }
66
+ return { success: true, result };
67
+ }
68
+ catch (error) {
69
+ console.error('[HOOK-RUNNER] Hook execution failed:', error instanceof Error ? error.message : 'Unknown error');
70
+ return { success: false, error: error instanceof Error ? error.message : 'Unknown error', stack: error instanceof Error ? error.stack : undefined };
71
+ }
72
+ }
73
+ /**
74
+ * Execute a single hook with timeout control
75
+ */
76
+ async function executeSingleHookAsync(hookConfig, context, config) {
77
+ const { name, script, timeout = 30000 } = hookConfig;
78
+ console.log(`[HOOK-RUNNER] Executing hook: ${name} (${hookConfig.hookName || 'unknown'})`);
79
+ // Use dynamic import for async execution
80
+ const cfg = config || (await Promise.resolve().then(() => __importStar(require('../../lib/config')))).loadConfig();
81
+ const absolutePath = path.isAbsolute(script)
82
+ ? script
83
+ : path.resolve(cfg.projectRoot, script);
84
+ if (!fs.existsSync(absolutePath)) {
85
+ return { name, success: false, error: 'script-not-found', script };
86
+ }
87
+ try {
88
+ const fileUrl = `file://${absolutePath}?t=${Date.now()}`;
89
+ const hookModule = await Promise.resolve(`${fileUrl}`).then(s => __importStar(require(s)));
90
+ const abortController = new AbortController();
91
+ const timeoutId = setTimeout(() => {
92
+ abortController.abort(new Error(`hook-timeout:${timeout}ms`));
93
+ }, timeout);
94
+ try {
95
+ const executePromise = executeHookFunction(hookModule.default || hookModule, context);
96
+ const result = await Promise.race([
97
+ executePromise,
98
+ new Promise((_, reject) => {
99
+ abortController.signal.addEventListener('abort', () => {
100
+ reject(abortController.signal.reason);
101
+ });
102
+ }),
103
+ ]);
104
+ return { name, ...result, script };
105
+ }
106
+ finally {
107
+ clearTimeout(timeoutId);
108
+ }
109
+ }
110
+ catch (error) {
111
+ return { name, success: false, error: error instanceof Error ? error.message : 'Unknown error', script };
112
+ }
113
+ }
114
+ /**
115
+ * Execute a single hook synchronously
116
+ */
117
+ function executeSingleHookSync(hookConfig, context, config) {
118
+ const { name, script } = hookConfig;
119
+ console.log(`[HOOK-RUNNER] Executing hook (sync): ${name} (${hookConfig.hookName || 'unknown'})`);
120
+ const hookModule = (0, loader_1.loadHookScript)(script, config);
121
+ if (!hookModule) {
122
+ return { name, success: false, error: 'script-not-found', script };
123
+ }
124
+ const hookFn = hookModule?.run
125
+ || hookModule?.execute
126
+ || hookModule?.default
127
+ || hookModule;
128
+ if (typeof hookFn !== 'function') {
129
+ return { name, success: false, error: 'hook-function-not-found', script };
130
+ }
131
+ try {
132
+ const result = hookFn(context);
133
+ if (result && typeof result.then === 'function') {
134
+ console.warn('[HOOK-RUNNER] Async hook detected in sync execution:', name);
135
+ return { name, success: false, error: 'async-hook-in-sync-mode', script };
136
+ }
137
+ return { name, success: true, result, script };
138
+ }
139
+ catch (error) {
140
+ console.error('[HOOK-RUNNER] Hook execution failed:', error instanceof Error ? error.message : 'Unknown error');
141
+ return { name, success: false, error: error instanceof Error ? error.message : 'Unknown error', script };
142
+ }
143
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * hooks/hook-runner/index.ts
3
+ * Unified hook execution engine for task-flow
4
+ */
5
+ import { Config } from '../../lib/config';
6
+ import { HookLifecycle, HookConfig, HookContext, HookExecutionResult } from './types';
7
+ import { loadHooksConfig, loadHookScript, loadHookScriptAsync } from './loader';
8
+ import { executeHookFunction } from './executor';
9
+ /**
10
+ * Hook lifecycle definitions
11
+ */
12
+ export declare const HOOK_LIFECYCLE: Record<string, HookLifecycle>;
13
+ /**
14
+ * Run a single hook by name
15
+ */
16
+ export declare function runHook(hookName: string, context: HookContext): Promise<HookExecutionResult>;
17
+ /**
18
+ * Run a single hook by name (sync)
19
+ */
20
+ export declare function runHookSync(hookName: string, context: HookContext): HookExecutionResult;
21
+ /**
22
+ * Run a chain of hooks
23
+ */
24
+ export declare function runHookChain(hookNames: string[], context: HookContext): Promise<HookExecutionResult>;
25
+ /**
26
+ * Get hook lifecycle info
27
+ */
28
+ export declare function getHookLifecycle(hookName: string): HookLifecycle | null;
29
+ /**
30
+ * Get hooks for a phase and timing
31
+ */
32
+ export declare function getHooksForPhase(phase: string, timing: string, config?: Config): (HookConfig & {
33
+ hookName: string;
34
+ lifecycle: HookLifecycle;
35
+ })[];
36
+ /**
37
+ * Validate hook config
38
+ */
39
+ export declare function validateHookConfig(hookConfig: HookConfig): {
40
+ valid: boolean;
41
+ errors: string[];
42
+ };
43
+ /**
44
+ * Create hook config
45
+ */
46
+ export declare function createHookConfig(name: string, script: string, options?: {
47
+ enabled?: boolean;
48
+ timeout?: number;
49
+ }): HookConfig;
50
+ declare const _default: {
51
+ runHook: typeof runHook;
52
+ runHookSync: typeof runHookSync;
53
+ runHookChain: typeof runHookChain;
54
+ loadHooksConfig: typeof loadHooksConfig;
55
+ loadHookScript: typeof loadHookScript;
56
+ loadHookScriptAsync: typeof loadHookScriptAsync;
57
+ executeHookFunction: typeof executeHookFunction;
58
+ getHookLifecycle: typeof getHookLifecycle;
59
+ getHooksForPhase: typeof getHooksForPhase;
60
+ validateHookConfig: typeof validateHookConfig;
61
+ createHookConfig: typeof createHookConfig;
62
+ HOOK_LIFECYCLE: Record<string, HookLifecycle>;
63
+ };
64
+ export default _default;
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ /**
3
+ * hooks/hook-runner/index.ts
4
+ * Unified hook execution engine for task-flow
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.HOOK_LIFECYCLE = void 0;
8
+ exports.runHook = runHook;
9
+ exports.runHookSync = runHookSync;
10
+ exports.runHookChain = runHookChain;
11
+ exports.getHookLifecycle = getHookLifecycle;
12
+ exports.getHooksForPhase = getHooksForPhase;
13
+ exports.validateHookConfig = validateHookConfig;
14
+ exports.createHookConfig = createHookConfig;
15
+ const config_1 = require("../../lib/config");
16
+ const loader_1 = require("./loader");
17
+ const executor_1 = require("./executor");
18
+ /**
19
+ * Hook lifecycle definitions
20
+ */
21
+ exports.HOOK_LIFECYCLE = {
22
+ 'pre-design': { phase: 'design', timing: 'pre' },
23
+ 'post-design': { phase: 'design', timing: 'post' },
24
+ 'pre-planning': { phase: 'planning', timing: 'pre' },
25
+ 'post-planning': { phase: 'planning', timing: 'post' },
26
+ 'pre-implementation': { phase: 'implementation', timing: 'pre' },
27
+ 'post-implementation': { phase: 'implementation', timing: 'post' },
28
+ 'pre-review': { phase: 'review', timing: 'pre' },
29
+ 'post-review': { phase: 'review', timing: 'post' },
30
+ 'pre-merge': { phase: 'merge', timing: 'pre' },
31
+ 'post-merge': { phase: 'merge', timing: 'post' },
32
+ 'pre-command': { phase: 'command', timing: 'pre' },
33
+ 'post-command': { phase: 'command', timing: 'post' },
34
+ };
35
+ /**
36
+ * Run enabled hooks async
37
+ */
38
+ async function runEnabledHooks(enabledHooks, context, config, hookName) {
39
+ const results = [];
40
+ let allSuccess = true;
41
+ for (const hookConfig of enabledHooks) {
42
+ const result = await (0, executor_1.executeSingleHookAsync)(hookConfig, context, config);
43
+ results.push(result);
44
+ if (!result.success) {
45
+ allSuccess = false;
46
+ }
47
+ }
48
+ return {
49
+ success: allSuccess,
50
+ executed: results.length,
51
+ results,
52
+ hookName,
53
+ };
54
+ }
55
+ /**
56
+ * Run enabled hooks sync
57
+ */
58
+ function runEnabledHooksSync(enabledHooks, context, config, hookName) {
59
+ const results = [];
60
+ let allSuccess = true;
61
+ for (const hookConfig of enabledHooks) {
62
+ const result = (0, executor_1.executeSingleHookSync)(hookConfig, context, config);
63
+ results.push(result);
64
+ if (!result.success) {
65
+ allSuccess = false;
66
+ }
67
+ }
68
+ return {
69
+ success: allSuccess,
70
+ executed: results.length,
71
+ results,
72
+ hookName,
73
+ };
74
+ }
75
+ /**
76
+ * Run a single hook by name
77
+ */
78
+ async function runHook(hookName, context) {
79
+ const config = context?.config || (0, config_1.loadConfig)();
80
+ const hooksConfig = (0, loader_1.loadHooksConfig)(config);
81
+ if (!hooksConfig.hooks || !hooksConfig.hooks[hookName]) {
82
+ return {
83
+ success: true,
84
+ executed: 0,
85
+ results: [],
86
+ message: `no-hooks-configured:${hookName}`,
87
+ };
88
+ }
89
+ const hooks = hooksConfig.hooks[hookName];
90
+ const enabledHooks = hooks.filter((h) => h.enabled !== false);
91
+ if (enabledHooks.length === 0) {
92
+ return {
93
+ success: true,
94
+ executed: 0,
95
+ results: [],
96
+ message: `no-enabled-hooks:${hookName}`,
97
+ };
98
+ }
99
+ return runEnabledHooks(enabledHooks, context, config, hookName);
100
+ }
101
+ /**
102
+ * Run a single hook by name (sync)
103
+ */
104
+ function runHookSync(hookName, context) {
105
+ const config = context?.config || (0, config_1.loadConfig)();
106
+ const hooksConfig = (0, loader_1.loadHooksConfig)(config);
107
+ if (!hooksConfig.hooks || !hooksConfig.hooks[hookName]) {
108
+ return {
109
+ success: true,
110
+ executed: 0,
111
+ results: [],
112
+ message: `no-hooks-configured:${hookName}`,
113
+ };
114
+ }
115
+ const hooks = hooksConfig.hooks[hookName];
116
+ const enabledHooks = hooks.filter((h) => h.enabled !== false);
117
+ if (enabledHooks.length === 0) {
118
+ return {
119
+ success: true,
120
+ executed: 0,
121
+ results: [],
122
+ message: `no-enabled-hooks:${hookName}`,
123
+ };
124
+ }
125
+ return runEnabledHooksSync(enabledHooks, context, config, hookName);
126
+ }
127
+ /**
128
+ * Run a chain of hooks
129
+ */
130
+ async function runHookChain(hookNames, context) {
131
+ const results = [];
132
+ let allSuccess = true;
133
+ for (const hookName of hookNames) {
134
+ const result = await runHook(hookName, context);
135
+ results.push(result);
136
+ if (!result.success) {
137
+ allSuccess = false;
138
+ break;
139
+ }
140
+ }
141
+ return {
142
+ success: allSuccess,
143
+ executed: results.length,
144
+ results: results.flatMap((r) => r.results),
145
+ hookNames,
146
+ };
147
+ }
148
+ /**
149
+ * Get hook lifecycle info
150
+ */
151
+ function getHookLifecycle(hookName) {
152
+ return exports.HOOK_LIFECYCLE[hookName] || null;
153
+ }
154
+ /**
155
+ * Get hooks for a phase and timing
156
+ */
157
+ function getHooksForPhase(phase, timing, config) {
158
+ const cfg = config || (0, config_1.loadConfig)();
159
+ const hooksConfig = (0, loader_1.loadHooksConfig)(cfg);
160
+ if (!hooksConfig.hooks) {
161
+ return [];
162
+ }
163
+ const matchingHooks = [];
164
+ for (const [hookName, lifecycle] of Object.entries(exports.HOOK_LIFECYCLE)) {
165
+ if (lifecycle.phase === phase && lifecycle.timing === timing) {
166
+ const hooks = hooksConfig.hooks[hookName] || [];
167
+ matchingHooks.push(...hooks.map((h) => ({
168
+ ...h,
169
+ hookName,
170
+ lifecycle,
171
+ })));
172
+ }
173
+ }
174
+ return matchingHooks;
175
+ }
176
+ /**
177
+ * Validate hook config
178
+ */
179
+ function validateHookConfig(hookConfig) {
180
+ const errors = [];
181
+ if (!hookConfig.name || typeof hookConfig.name !== 'string') {
182
+ errors.push('hook-must-have-name');
183
+ }
184
+ if (!hookConfig.script || typeof hookConfig.script !== 'string') {
185
+ errors.push('hook-must-have-script');
186
+ }
187
+ if (hookConfig.timeout !== undefined && typeof hookConfig.timeout !== 'number') {
188
+ errors.push('hook-timeout-must-be-number');
189
+ }
190
+ return {
191
+ valid: errors.length === 0,
192
+ errors,
193
+ };
194
+ }
195
+ /**
196
+ * Create hook config
197
+ */
198
+ function createHookConfig(name, script, options = {}) {
199
+ return {
200
+ name,
201
+ script,
202
+ enabled: options.enabled !== false,
203
+ timeout: options.timeout || 30000,
204
+ ...options,
205
+ };
206
+ }
207
+ exports.default = {
208
+ runHook,
209
+ runHookSync,
210
+ runHookChain,
211
+ loadHooksConfig: loader_1.loadHooksConfig,
212
+ loadHookScript: loader_1.loadHookScript,
213
+ loadHookScriptAsync: loader_1.loadHookScriptAsync,
214
+ executeHookFunction: executor_1.executeHookFunction,
215
+ getHookLifecycle,
216
+ getHooksForPhase,
217
+ validateHookConfig,
218
+ createHookConfig,
219
+ HOOK_LIFECYCLE: exports.HOOK_LIFECYCLE,
220
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * hooks/hook-runner/loader.ts
3
+ * Hook loading utilities
4
+ */
5
+ import { Config } from '../../lib/config';
6
+ import { LoadedHooksConfig } from './types';
7
+ /**
8
+ * Get default hooks path
9
+ */
10
+ export declare function getDefaultHooksPath(config: Config): string;
11
+ /**
12
+ * Load hooks config from file
13
+ */
14
+ export declare function loadHooksConfig(config?: Config): LoadedHooksConfig;
15
+ /**
16
+ * Load hook script with dynamic import to avoid require.cache issues
17
+ */
18
+ export declare function loadHookScriptAsync(scriptPath: string, config?: Config): Promise<Record<string, unknown> | null>;
19
+ /**
20
+ * Load hook script (legacy sync version)
21
+ * @deprecated Use loadHookScriptAsync instead
22
+ */
23
+ export declare function loadHookScript(scriptPath: string, config?: Config): Record<string, unknown> | null;