owflow 0.5.0 → 0.7.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 (195) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +448 -261
  3. package/dist/agents/bottleneck-analyzer.md +332 -329
  4. package/dist/agents/code-quality-pragmatist.md +353 -354
  5. package/dist/agents/code-reviewer.md +238 -239
  6. package/dist/agents/codebase-analysis-reporter.md +245 -246
  7. package/dist/agents/docs-operator.md +22 -23
  8. package/dist/agents/e2e-test-verifier.md +447 -448
  9. package/dist/agents/gap-analyzer.md +501 -502
  10. package/dist/agents/implementation-completeness-checker.md +219 -220
  11. package/dist/agents/implementation-planner.md +308 -309
  12. package/dist/agents/information-gatherer.md +690 -691
  13. package/dist/agents/production-readiness-checker.md +278 -279
  14. package/dist/agents/project-analyzer.md +403 -403
  15. package/dist/agents/reality-assessor.md +365 -366
  16. package/dist/agents/research-planner.md +449 -450
  17. package/dist/agents/research-synthesizer.md +402 -403
  18. package/dist/agents/solution-brainstormer.md +256 -257
  19. package/dist/agents/solution-designer.md +371 -372
  20. package/dist/agents/spec-auditor.md +297 -298
  21. package/dist/agents/specification-creator.md +333 -334
  22. package/dist/agents/task-classifier.md +449 -450
  23. package/dist/agents/task-group-implementer.md +283 -284
  24. package/dist/agents/test-suite-runner.md +194 -195
  25. package/dist/agents/user-docs-generator.md +508 -509
  26. package/dist/commands/reviews-code.md +90 -90
  27. package/dist/commands/reviews-pragmatic.md +99 -99
  28. package/dist/commands/reviews-production-readiness.md +113 -113
  29. package/dist/commands/reviews-reality-check.md +110 -110
  30. package/dist/commands/reviews-spec-audit.md +114 -114
  31. package/dist/commands/work.md +280 -280
  32. package/dist/configuration/agents-config.d.ts +2 -0
  33. package/dist/configuration/agents-config.js +8 -44
  34. package/dist/configuration/commands-config.d.ts +23 -0
  35. package/dist/configuration/commands-config.js +17 -17
  36. package/dist/configuration/skills-config.d.ts +2 -0
  37. package/dist/configuration/skills-config.js +3 -3
  38. package/dist/hooks/hooks-diagnostics.d.ts +3 -0
  39. package/dist/hooks/hooks-diagnostics.js +21 -0
  40. package/dist/hooks/hooks-persistence.d.ts +17 -0
  41. package/dist/hooks/hooks-persistence.js +207 -0
  42. package/dist/hooks/hooks-registry.d.ts +6 -0
  43. package/dist/hooks/hooks-registry.js +104 -0
  44. package/dist/hooks/hooks-wrapper.d.ts +25 -0
  45. package/dist/hooks/hooks-wrapper.js +318 -0
  46. package/dist/hooks/internal/before-tool.d.ts +6 -0
  47. package/dist/hooks/internal/before-tool.js +13 -0
  48. package/dist/hooks/internal/session-compaction.d.ts +9 -0
  49. package/dist/hooks/{session-compaction.js → internal/session-compaction.js} +9 -7
  50. package/dist/hooks/runtime/circuit-breaker.d.ts +23 -0
  51. package/dist/hooks/runtime/circuit-breaker.js +39 -0
  52. package/dist/hooks/runtime/failure-policy.d.ts +15 -0
  53. package/dist/hooks/runtime/failure-policy.js +49 -0
  54. package/dist/hooks/runtime/process-executor.d.ts +33 -0
  55. package/dist/hooks/runtime/process-executor.js +147 -0
  56. package/dist/hooks/runtime/status-recorder.d.ts +27 -0
  57. package/dist/hooks/runtime/status-recorder.js +111 -0
  58. package/dist/index.d.ts +7 -0
  59. package/dist/index.js +111 -40
  60. package/dist/skills/agents-md-generator/SKILL.md +223 -223
  61. package/dist/skills/codebase-analyzer/SKILL.md +162 -162
  62. package/dist/skills/codebase-analyzer/references/code-analysis.md +63 -63
  63. package/dist/skills/codebase-analyzer/references/combined.md +31 -31
  64. package/dist/skills/codebase-analyzer/references/context-discovery.md +63 -63
  65. package/dist/skills/codebase-analyzer/references/file-discovery.md +51 -51
  66. package/dist/skills/codebase-analyzer/references/migration-target.md +23 -23
  67. package/dist/skills/codebase-analyzer/references/pattern-mining.md +22 -22
  68. package/dist/skills/dev-analyze/SKILL.md +115 -115
  69. package/dist/skills/dev-bugfix/SKILL.md +452 -452
  70. package/dist/skills/dev-finalize/SKILL.md +118 -118
  71. package/dist/skills/dev-implement/SKILL.md +167 -167
  72. package/dist/skills/dev-plan/SKILL.md +147 -147
  73. package/dist/skills/dev-spec/SKILL.md +182 -182
  74. package/dist/skills/dev-tdd-red/SKILL.md +94 -94
  75. package/dist/skills/dev-verify/SKILL.md +153 -153
  76. package/dist/skills/development/SKILL.md +260 -260
  77. package/dist/skills/diagrams-mermaid/SKILL.md +183 -159
  78. package/dist/skills/diagrams-mermaid/references/reference.md +220 -148
  79. package/dist/skills/docs-manager/SKILL.md +379 -379
  80. package/dist/skills/docs-manager/docs/INDEX.md +199 -199
  81. package/dist/skills/docs-manager/docs/standards/backend/api.md +25 -25
  82. package/dist/skills/docs-manager/docs/standards/backend/migrations.md +22 -22
  83. package/dist/skills/docs-manager/docs/standards/backend/models.md +25 -25
  84. package/dist/skills/docs-manager/docs/standards/backend/queries.md +22 -22
  85. package/dist/skills/docs-manager/docs/standards/frontend/accessibility.md +25 -25
  86. package/dist/skills/docs-manager/docs/standards/frontend/components.md +28 -28
  87. package/dist/skills/docs-manager/docs/standards/frontend/css.md +16 -16
  88. package/dist/skills/docs-manager/docs/standards/frontend/responsive.md +28 -28
  89. package/dist/skills/docs-manager/docs/standards/global/coding-style.md +25 -25
  90. package/dist/skills/docs-manager/docs/standards/global/commenting.md +10 -10
  91. package/dist/skills/docs-manager/docs/standards/global/conventions.md +31 -31
  92. package/dist/skills/docs-manager/docs/standards/global/error-handling.md +22 -22
  93. package/dist/skills/docs-manager/docs/standards/global/minimal-implementation.md +22 -22
  94. package/dist/skills/docs-manager/docs/standards/global/validation.md +28 -28
  95. package/dist/skills/docs-manager/docs/standards/testing/test-writing.md +25 -25
  96. package/dist/skills/docs-manager/references/agents-md-template.md +29 -29
  97. package/dist/skills/docs-manager/references/index-md-template.md +73 -73
  98. package/dist/skills/flow-init/SKILL.md +234 -234
  99. package/dist/skills/flow-init/references/architecture-template.md +45 -45
  100. package/dist/skills/flow-init/references/roadmap-templates.md +93 -93
  101. package/dist/skills/flow-init/references/tech-stack-template.md +70 -70
  102. package/dist/skills/flow-init/references/vision-templates.md +75 -75
  103. package/dist/skills/goal-development/SKILL.md +96 -96
  104. package/dist/skills/goal-migration/SKILL.md +114 -114
  105. package/dist/skills/goal-performance/SKILL.md +126 -0
  106. package/dist/skills/goal-research/SKILL.md +118 -118
  107. package/dist/skills/html-renderer/SKILL.md +173 -173
  108. package/dist/skills/html-renderer/references/index.html +318 -318
  109. package/dist/skills/html-renderer/references/styles.css +388 -388
  110. package/dist/skills/html-renderer/references/tokens.json +104 -104
  111. package/dist/skills/implementation-plan-executor/SKILL.md +378 -378
  112. package/dist/skills/implementation-verifier/SKILL.md +307 -307
  113. package/dist/skills/migration/SKILL.md +268 -268
  114. package/dist/skills/migration-analyze/SKILL.md +126 -126
  115. package/dist/skills/migration-finalize/SKILL.md +161 -161
  116. package/dist/skills/migration-fix/SKILL.md +155 -155
  117. package/dist/skills/migration-implement/SKILL.md +131 -131
  118. package/dist/skills/migration-plan/SKILL.md +131 -131
  119. package/dist/skills/migration-spec/SKILL.md +135 -135
  120. package/dist/skills/migration-spec/references/migration-strategies.md +430 -430
  121. package/dist/skills/migration-target/SKILL.md +134 -134
  122. package/dist/skills/migration-target/references/migration-types.md +489 -489
  123. package/dist/skills/migration-verify/SKILL.md +184 -184
  124. package/dist/skills/orchestrator-framework/SKILL.md +65 -65
  125. package/dist/skills/orchestrator-framework/references/command-namespacing.md +7 -7
  126. package/dist/skills/orchestrator-framework/references/confirm-or-revise-exception.md +20 -20
  127. package/dist/skills/orchestrator-framework/references/delegation-rules.md +54 -54
  128. package/dist/skills/orchestrator-framework/references/dispatcher-handoff.md +27 -27
  129. package/dist/skills/orchestrator-framework/references/fix-loop-contract.md +65 -65
  130. package/dist/skills/orchestrator-framework/references/gate-contract.md +122 -117
  131. package/dist/skills/orchestrator-framework/references/orchestrator-creation-checklist.md +51 -51
  132. package/dist/skills/orchestrator-framework/references/orchestrator-patterns.md +235 -235
  133. package/dist/skills/performance/SKILL.md +267 -437
  134. package/dist/skills/performance-analyze/SKILL.md +129 -0
  135. package/dist/skills/{performance → performance-analyze}/references/performance-optimization-guide.md +271 -365
  136. package/dist/skills/performance-finalize/SKILL.md +117 -0
  137. package/dist/skills/performance-implement/SKILL.md +114 -0
  138. package/dist/skills/performance-plan/SKILL.md +112 -0
  139. package/dist/skills/performance-spec/SKILL.md +145 -0
  140. package/dist/skills/performance-verify/SKILL.md +164 -0
  141. package/dist/skills/research/SKILL.md +249 -249
  142. package/dist/skills/research-brainstorm/SKILL.md +120 -120
  143. package/dist/skills/research-brainstorm/references/brainstorming-techniques.md +84 -84
  144. package/dist/skills/research-converge/SKILL.md +130 -130
  145. package/dist/skills/research-design/SKILL.md +146 -146
  146. package/dist/skills/research-design/references/design-techniques.md +40 -40
  147. package/dist/skills/research-finalize/SKILL.md +125 -125
  148. package/dist/skills/research-fork/SKILL.md +190 -190
  149. package/dist/skills/research-gather/SKILL.md +133 -133
  150. package/dist/skills/research-plan/SKILL.md +141 -141
  151. package/dist/skills/research-plan/references/research-methodologies.md +710 -710
  152. package/dist/skills/research-quick/SKILL.md +139 -139
  153. package/dist/skills/research-synthesize/SKILL.md +127 -127
  154. package/dist/skills/rule-reviewer/SKILL.md +334 -334
  155. package/dist/skills/standards-discover/SKILL.md +260 -260
  156. package/dist/skills/standards-discover/references/aggregation-strategy.md +76 -76
  157. package/dist/skills/standards-discover/references/code-pattern-prompt.md +68 -68
  158. package/dist/skills/standards-discover/references/config-analyzer-prompt.md +67 -67
  159. package/dist/skills/standards-discover/references/docs-extractor-prompt.md +65 -65
  160. package/dist/skills/standards-discover/references/external-analyzer-prompt.md +75 -75
  161. package/dist/skills/standards-update/SKILL.md +207 -207
  162. package/dist/templates/orchestrator-state-base.yml +17 -17
  163. package/dist/templates/orchestrator-state-development.yml +95 -95
  164. package/dist/templates/orchestrator-state-migration.yml +95 -95
  165. package/dist/templates/orchestrator-state-performance.yml +64 -53
  166. package/dist/templates/orchestrator-state-research.yml +67 -67
  167. package/dist/tools/fork_task.d.ts +17 -0
  168. package/dist/tools/fork_task.js +21 -11
  169. package/dist/tools/verify_template.d.ts +11 -0
  170. package/dist/tools/verify_template.js +32 -17
  171. package/dist/tui/hooks-dialogs.d.ts +20 -0
  172. package/dist/tui/hooks-dialogs.js +181 -0
  173. package/dist/tui/hooks-presenter.d.ts +15 -0
  174. package/dist/tui/hooks-presenter.js +70 -0
  175. package/dist/tui/hooks-sidebar.d.ts +10 -0
  176. package/dist/tui/hooks-sidebar.js +104 -0
  177. package/dist/tui/primitives.d.ts +21 -0
  178. package/dist/tui/primitives.js +47 -0
  179. package/dist/tui.d.ts +12 -0
  180. package/dist/tui.js +129 -0
  181. package/dist/types/hook-types.d.ts +125 -0
  182. package/dist/types/hook-types.js +76 -0
  183. package/dist/types/opencode-types.d.ts +13 -0
  184. package/dist/utils/file-util.d.ts +2 -0
  185. package/dist/utils/file-util.js +6 -6
  186. package/dist/utils/fork-task.d.ts +27 -0
  187. package/dist/utils/fork-task.js +18 -23
  188. package/dist/utils/plugin-info.d.ts +3 -0
  189. package/dist/utils/plugin-info.js +8 -10
  190. package/dist/utils/workflow-paths.d.ts +17 -0
  191. package/dist/utils/workflow-paths.js +18 -0
  192. package/dist/utils/yaml-structure.d.ts +7 -0
  193. package/dist/utils/yaml-structure.js +7 -11
  194. package/package.json +68 -48
  195. package/dist/hooks/before-tool.js +0 -19
package/dist/tui.js ADDED
@@ -0,0 +1,129 @@
1
+ import fs from 'node:fs';
2
+ import { readHookStatusSnapshot, readSessionLatestLogs, resolveHooksLogFile, resolveHooksStatusFile } from './hooks/hooks-persistence.js';
3
+ import {} from './types/hook-types.js';
4
+ import { buildHookLogsBody, buildHookRunDetailBody, getActiveDialogHandle, openHookLogPopup } from './tui/hooks-dialogs.js';
5
+ import { buildActiveHooksView, colorForStatus, formatHookLogsMessage, formatHookRunSummary, statusLabelFor, } from './tui/hooks-presenter.js';
6
+ import { materializeSidebar } from './tui/hooks-sidebar.js';
7
+ import { loadSolidRuntime } from './tui/primitives.js';
8
+ export { buildActiveHooksView, buildHookLogsBody, buildHookRunDetailBody, colorForStatus, formatHookLogsMessage, formatHookRunSummary, getActiveDialogHandle, statusLabelFor, };
9
+ const SIDEBAR_SLOT = 'sidebar_content';
10
+ const DEFAULT_STATUS_POLL_MS = 200;
11
+ export const registerActiveHooksSidebar = async (api, directory, loadSolid = loadSolidRuntime) => {
12
+ const solid = await loadSolid();
13
+ if (solid === null) {
14
+ console.warn('[owflow] TUI Solid runtime unavailable; cannot render the active hooks panel.');
15
+ return false;
16
+ }
17
+ const view = buildActiveHooksView(directory);
18
+ const statusFile = resolveHooksStatusFile(directory);
19
+ const handles = [];
20
+ let statuses = new Map();
21
+ let lastMtime;
22
+ const sessionEpochs = new Map();
23
+ const pollingEnabled = process.env.OWFLOW_HOOKS_DISABLED !== '1';
24
+ if (pollingEnabled) {
25
+ const renderer = api.renderer;
26
+ const tick = () => {
27
+ try {
28
+ const stat = fs.statSync(statusFile);
29
+ if (stat.isFile() && lastMtime !== stat.mtimeMs) {
30
+ lastMtime = stat.mtimeMs;
31
+ const result = readHookStatusSnapshot(statusFile);
32
+ if (result.kind === 'ok') {
33
+ statuses = new Map(Object.entries(result.snapshot.hooks));
34
+ }
35
+ }
36
+ else if (!stat.isFile()) {
37
+ lastMtime = undefined;
38
+ }
39
+ }
40
+ catch {
41
+ lastMtime = undefined;
42
+ }
43
+ for (const handle of handles) {
44
+ if (handle.disposed())
45
+ continue;
46
+ if (handle.applyStatuses(statuses)) {
47
+ renderer.requestRender();
48
+ }
49
+ }
50
+ for (let i = handles.length - 1; i >= 0; i -= 1) {
51
+ if (handles[i].disposed())
52
+ handles.splice(i, 1);
53
+ }
54
+ };
55
+ const timer = setInterval(tick, DEFAULT_STATUS_POLL_MS);
56
+ timer.unref?.();
57
+ api.lifecycle.onDispose(() => {
58
+ clearInterval(timer);
59
+ getActiveDialogHandle()?.dispose();
60
+ for (const handle of handles) {
61
+ handle.dispose();
62
+ }
63
+ handles.length = 0;
64
+ });
65
+ }
66
+ else {
67
+ api.lifecycle.onDispose(() => {
68
+ getActiveDialogHandle()?.dispose();
69
+ for (const handle of handles) {
70
+ handle.dispose();
71
+ }
72
+ handles.length = 0;
73
+ });
74
+ }
75
+ api.slots.register({
76
+ order: 50,
77
+ slots: {
78
+ [SIDEBAR_SLOT]: (ctx, props) => {
79
+ const sessionID = props?.session_id;
80
+ const key = sessionID ?? '';
81
+ const notBefore = sessionEpochs.get(key) ?? (sessionEpochs.set(key, Date.now()), sessionEpochs.get(key));
82
+ const initialStates = new Map();
83
+ if (sessionID !== undefined && sessionID.length > 0) {
84
+ for (const row of view.rows) {
85
+ try {
86
+ const rowLogFile = resolveHooksLogFile(directory, row.name);
87
+ const sessionLogs = readSessionLatestLogs(rowLogFile, sessionID);
88
+ const rec = sessionLogs.get(row.event);
89
+ if (rec)
90
+ initialStates.set(row.event, rec.status);
91
+ }
92
+ catch { }
93
+ }
94
+ }
95
+ const handle = materializeSidebar(view, ctx.theme.current, solid, (row, theme) => {
96
+ if (pollingEnabled) {
97
+ const rowLogFile = resolveHooksLogFile(directory, row.name);
98
+ openHookLogPopup(api, solid, rowLogFile, row, theme, notBefore, sessionID, statusFile, statuses.get(row.event));
99
+ }
100
+ }, () => api.renderer.requestRender(), pollingEnabled, notBefore, sessionID, initialStates);
101
+ handle.applyStatuses(statuses);
102
+ handles.push(handle);
103
+ if (typeof solid.onCleanup === 'function') {
104
+ try {
105
+ solid.onCleanup(() => {
106
+ handle.dispose();
107
+ const idx = handles.indexOf(handle);
108
+ if (idx !== -1) {
109
+ handles.splice(idx, 1);
110
+ }
111
+ });
112
+ }
113
+ catch { }
114
+ }
115
+ return handle.root;
116
+ },
117
+ },
118
+ });
119
+ return true;
120
+ };
121
+ const tui = async (api) => {
122
+ const directory = api.state.path.directory;
123
+ if (typeof directory !== 'string' || directory.length === 0) {
124
+ console.warn('[owflow] workspace directory unavailable; cannot render the active hooks panel.');
125
+ return;
126
+ }
127
+ await registerActiveHooksSidebar(api, directory);
128
+ };
129
+ export default { id: 'owflow', tui };
@@ -0,0 +1,125 @@
1
+ export declare enum HookEvent {
2
+ ToolExecuteBefore = "tool.execute.before",
3
+ ToolExecuteAfter = "tool.execute.after",
4
+ ToolDefinition = "tool.definition",
5
+ ChatMessage = "chat.message",
6
+ ChatParams = "chat.params",
7
+ ChatHeaders = "chat.headers",
8
+ CommandExecuteBefore = "command.execute.before",
9
+ ShellEnv = "shell.env",
10
+ ExperimentalChatMessagesTransform = "experimental.chat.messages.transform",
11
+ ExperimentalChatSystemTransform = "experimental.chat.system.transform",
12
+ ExperimentalSessionCompacting = "experimental.session.compacting",
13
+ ExperimentalCompactionAutocontinue = "experimental.compaction.autocontinue",
14
+ ExperimentalTextComplete = "experimental.text.complete",
15
+ Config = "config",
16
+ Event = "event",
17
+ Dispose = "dispose"
18
+ }
19
+ export type DispatchableEvent = `${HookEvent}`;
20
+ export declare const DISPATCHABLE_EVENTS: readonly DispatchableEvent[];
21
+ export declare const HOST_EVENT_NAMES: readonly ["command.executed", "file.edited", "file.watcher.updated", "installation.update-available", "installation.updated", "lsp.client.diagnostics", "lsp.updated", "message.part.removed", "message.part.updated", "message.removed", "message.updated", "permission.replied", "permission.updated", "pty.created", "pty.deleted", "pty.exited", "pty.updated", "server.connected", "server.instance.disposed", "session.compacted", "session.created", "session.deleted", "session.diff", "session.error", "session.idle", "session.status", "session.updated", "todo.updated", "tui.command.execute", "tui.prompt.append", "tui.toast.show", "vcs.branch.updated"];
22
+ export type HostEventName = (typeof HOST_EVENT_NAMES)[number];
23
+ export type HookEventName = DispatchableEvent | HostEventName;
24
+ export declare const HOOK_EVENT_NAMES: readonly HookEventName[];
25
+ export declare const EXCLUDED_EVENT_NAMES: readonly ["permission.ask", "tool", "auth", "provider", "experimental.provider.small_model"];
26
+ export declare const isHostEventName: (value: string) => value is HostEventName;
27
+ /**
28
+ * Validation digest mode for `scanHooks`.
29
+ * - `FailFast`: Throws on any validation failure at startup/runtime to prevent
30
+ * running with invalid or ambiguous hooks (fail-closed contract).
31
+ * - `Report`: Collects failures without throwing, allowing UI surfaces (TUI sidebar)
32
+ * and test suites to safely inspect hook status without crashing the process.
33
+ */
34
+ export declare enum HookDigestMode {
35
+ FailFast = "fail-fast",
36
+ Report = "report"
37
+ }
38
+ export interface InterpreterResolution {
39
+ readonly resolved?: string;
40
+ readonly expected: string;
41
+ readonly available: boolean;
42
+ }
43
+ export interface HookRegistryEntry {
44
+ readonly path: string;
45
+ readonly event: HookEventName;
46
+ readonly interpreter: InterpreterResolution;
47
+ readonly status: 'registered' | 'will-fail-closed';
48
+ }
49
+ export interface HookValidationFailure {
50
+ readonly path: string;
51
+ readonly error: string;
52
+ }
53
+ export interface HookScan {
54
+ readonly entries: readonly HookRegistryEntry[];
55
+ readonly failures: readonly HookValidationFailure[];
56
+ }
57
+ export declare enum HookRunState {
58
+ IDLE = "idle",
59
+ PENDING = "pending",
60
+ SUCCESS = "success",
61
+ FAILURE = "failure"
62
+ }
63
+ export interface HookRuntimeEntry {
64
+ readonly status: HookRunState;
65
+ readonly startedAt: number;
66
+ readonly finishedAt?: number;
67
+ readonly durationMs?: number;
68
+ readonly reason?: string;
69
+ readonly sessionID?: string;
70
+ }
71
+ export interface HookStatusSnapshot {
72
+ readonly version: 1;
73
+ readonly pid: number;
74
+ readonly workspace: string;
75
+ readonly updatedAt: number;
76
+ readonly hooks: Readonly<Record<string, HookRuntimeEntry>>;
77
+ }
78
+ export type HookStatusReadResult = {
79
+ readonly kind: 'missing' | 'invalid';
80
+ } | {
81
+ readonly kind: 'ok';
82
+ readonly snapshot: HookStatusSnapshot;
83
+ };
84
+ export interface HookRunLogRecord {
85
+ readonly version: 1;
86
+ readonly runId: string;
87
+ readonly pid: number;
88
+ readonly workspace: string;
89
+ readonly event: HookEventName;
90
+ readonly hook: string;
91
+ readonly name: string;
92
+ readonly sessionID?: string;
93
+ readonly startedAt: number;
94
+ readonly finishedAt: number;
95
+ readonly durationMs: number;
96
+ readonly status: HookRunState;
97
+ readonly exitCode?: number | null;
98
+ readonly signal?: string | null;
99
+ readonly reason?: string;
100
+ readonly stdout?: string;
101
+ readonly stderr?: string;
102
+ }
103
+ export type HookLogReadResult = {
104
+ readonly kind: 'missing' | 'invalid';
105
+ } | {
106
+ readonly kind: 'ok';
107
+ readonly records: readonly HookRunLogRecord[];
108
+ readonly truncated: boolean;
109
+ };
110
+ export type Dispatcher = (event: HookEventName, input?: unknown, output?: Record<string, unknown>) => Promise<void>;
111
+ export type HookReportRow = Readonly<Record<'path' | 'event' | 'interpreter' | 'status', string>>;
112
+ export interface HookReportInput {
113
+ readonly entries: readonly HookRegistryEntry[];
114
+ readonly failures?: readonly HookValidationFailure[];
115
+ readonly bypassed?: boolean;
116
+ }
117
+ export interface HooksWrapper {
118
+ readonly dispatch: Dispatcher;
119
+ readonly snapshot: HookReportInput;
120
+ dispose(): void;
121
+ }
122
+ export interface HooksWrapperOptions {
123
+ readonly statusFile?: string;
124
+ readonly logFile?: string;
125
+ }
@@ -0,0 +1,76 @@
1
+ export var HookEvent;
2
+ (function (HookEvent) {
3
+ HookEvent["ToolExecuteBefore"] = "tool.execute.before";
4
+ HookEvent["ToolExecuteAfter"] = "tool.execute.after";
5
+ HookEvent["ToolDefinition"] = "tool.definition";
6
+ HookEvent["ChatMessage"] = "chat.message";
7
+ HookEvent["ChatParams"] = "chat.params";
8
+ HookEvent["ChatHeaders"] = "chat.headers";
9
+ HookEvent["CommandExecuteBefore"] = "command.execute.before";
10
+ HookEvent["ShellEnv"] = "shell.env";
11
+ HookEvent["ExperimentalChatMessagesTransform"] = "experimental.chat.messages.transform";
12
+ HookEvent["ExperimentalChatSystemTransform"] = "experimental.chat.system.transform";
13
+ HookEvent["ExperimentalSessionCompacting"] = "experimental.session.compacting";
14
+ HookEvent["ExperimentalCompactionAutocontinue"] = "experimental.compaction.autocontinue";
15
+ HookEvent["ExperimentalTextComplete"] = "experimental.text.complete";
16
+ HookEvent["Config"] = "config";
17
+ HookEvent["Event"] = "event";
18
+ HookEvent["Dispose"] = "dispose";
19
+ })(HookEvent || (HookEvent = {}));
20
+ export const DISPATCHABLE_EVENTS = Object.values(HookEvent);
21
+ export const HOST_EVENT_NAMES = [
22
+ 'command.executed',
23
+ 'file.edited',
24
+ 'file.watcher.updated',
25
+ 'installation.update-available',
26
+ 'installation.updated',
27
+ 'lsp.client.diagnostics',
28
+ 'lsp.updated',
29
+ 'message.part.removed',
30
+ 'message.part.updated',
31
+ 'message.removed',
32
+ 'message.updated',
33
+ 'permission.replied',
34
+ 'permission.updated',
35
+ 'pty.created',
36
+ 'pty.deleted',
37
+ 'pty.exited',
38
+ 'pty.updated',
39
+ 'server.connected',
40
+ 'server.instance.disposed',
41
+ 'session.compacted',
42
+ 'session.created',
43
+ 'session.deleted',
44
+ 'session.diff',
45
+ 'session.error',
46
+ 'session.idle',
47
+ 'session.status',
48
+ 'session.updated',
49
+ 'todo.updated',
50
+ 'tui.command.execute',
51
+ 'tui.prompt.append',
52
+ 'tui.toast.show',
53
+ 'vcs.branch.updated',
54
+ ];
55
+ export const HOOK_EVENT_NAMES = [...DISPATCHABLE_EVENTS, ...HOST_EVENT_NAMES];
56
+ export const EXCLUDED_EVENT_NAMES = ['permission.ask', 'tool', 'auth', 'provider', 'experimental.provider.small_model'];
57
+ export const isHostEventName = (value) => HOST_EVENT_NAMES.includes(value);
58
+ /**
59
+ * Validation digest mode for `scanHooks`.
60
+ * - `FailFast`: Throws on any validation failure at startup/runtime to prevent
61
+ * running with invalid or ambiguous hooks (fail-closed contract).
62
+ * - `Report`: Collects failures without throwing, allowing UI surfaces (TUI sidebar)
63
+ * and test suites to safely inspect hook status without crashing the process.
64
+ */
65
+ export var HookDigestMode;
66
+ (function (HookDigestMode) {
67
+ HookDigestMode["FailFast"] = "fail-fast";
68
+ HookDigestMode["Report"] = "report";
69
+ })(HookDigestMode || (HookDigestMode = {}));
70
+ export var HookRunState;
71
+ (function (HookRunState) {
72
+ HookRunState["IDLE"] = "idle";
73
+ HookRunState["PENDING"] = "pending";
74
+ HookRunState["SUCCESS"] = "success";
75
+ HookRunState["FAILURE"] = "failure";
76
+ })(HookRunState || (HookRunState = {}));
@@ -0,0 +1,13 @@
1
+ export interface OpenCodeConfig {
2
+ agent: any;
3
+ command: Record<string, OpenCodeCommand>;
4
+ skills: any;
5
+ small_model?: string;
6
+ }
7
+ export interface OpenCodeCommand {
8
+ template: string;
9
+ description?: string;
10
+ agent?: string;
11
+ model?: string;
12
+ subtask?: boolean;
13
+ }
@@ -0,0 +1,2 @@
1
+ import matter from 'gray-matter';
2
+ export declare const loadMarkdownDir: (dirPath: string) => matter.GrayMatterFile<string>[];
@@ -1,12 +1,12 @@
1
- import matter from "gray-matter";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { PLUGIN_ROOT } from "./plugin-info.js";
1
+ import matter from 'gray-matter';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { PLUGIN_ROOT } from './plugin-info.js';
5
5
  export const loadMarkdownDir = (dirPath) => {
6
6
  const fullPath = path.join(PLUGIN_ROOT, dirPath);
7
7
  let files;
8
8
  try {
9
- files = fs.readdirSync(fullPath).filter((f) => f.endsWith(".md"));
9
+ files = fs.readdirSync(fullPath).filter((f) => f.endsWith('.md'));
10
10
  }
11
11
  catch {
12
12
  return [];
@@ -14,7 +14,7 @@ export const loadMarkdownDir = (dirPath) => {
14
14
  const parsed = [];
15
15
  for (const f of files) {
16
16
  try {
17
- const raw = fs.readFileSync(path.join(fullPath, f), "utf8");
17
+ const raw = fs.readFileSync(path.join(fullPath, f), 'utf8');
18
18
  parsed.push(matter(raw));
19
19
  }
20
20
  catch (e) {
@@ -0,0 +1,27 @@
1
+ export interface ForkTaskInput {
2
+ /** Root of all tasks, e.g. the value of `workflowRelativePath(TASKS_DIRECTORY_NAME)`. */
3
+ taskRoot: string;
4
+ /** Source task: absolute/relative path or a bare identifier resolved under <taskRoot>/research/ */
5
+ source: string;
6
+ /** Kebab-case slug derived by the agent, without date prefix */
7
+ slug: string;
8
+ /** Fork point: completed step slug up to which completed_phases are kept */
9
+ from: string;
10
+ /** Short description of what the fork should explore (stamped into the fork state) */
11
+ intent?: string;
12
+ /** Working directory used to resolve relative paths (defaults to process.cwd()) */
13
+ cwd?: string;
14
+ }
15
+ export interface ForkTaskResult {
16
+ forkPath: string;
17
+ forkName: string;
18
+ forkPoint: string;
19
+ }
20
+ /** Lowercase kebab-case, exactly 3-5 words of [a-z0-9] (Task Name Generation convention). */
21
+ export declare const RESEARCH_SLUG_PATTERN: RegExp;
22
+ /**
23
+ * Copies the whole source task directory into a new date-prefixed directory
24
+ * named from the slug, then trims the copied state so the fork continues
25
+ * from the fork point onwards. The source task is never modified.
26
+ */
27
+ export declare const forkTask: (input: ForkTaskInput) => ForkTaskResult;
@@ -1,10 +1,11 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import yaml from "yaml";
4
- import { parseYamlFile } from "./yaml-structure.js";
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import yaml from 'yaml';
4
+ import { RESEARCH_DIRECTORY_NAME, TASKS_DIRECTORY_NAME, workflowRelativePath } from './workflow-paths.js';
5
+ import { parseYamlFile } from './yaml-structure.js';
5
6
  /** Lowercase kebab-case, exactly 3-5 words of [a-z0-9] (Task Name Generation convention). */
6
7
  export const RESEARCH_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+){2,4}$/;
7
- const FORK_TERMINAL_STEP = "research-completed";
8
+ const FORK_TERMINAL_STEP = 'research-completed';
8
9
  /**
9
10
  * Copies the whole source task directory into a new date-prefixed directory
10
11
  * named from the slug, then trims the copied state so the fork continues
@@ -16,30 +17,27 @@ export const forkTask = (input) => {
16
17
  throw new Error("INVALID_NAME: slug must be lowercase kebab-case, 3-5 words of [a-z0-9] (e.g. 'compare-caching-strategies')");
17
18
  }
18
19
  const taskRoot = path.resolve(cwd, input.taskRoot);
19
- const researchRoot = path.join(taskRoot, "research");
20
- const sourceDir = [
21
- path.resolve(cwd, input.source),
22
- path.resolve(researchRoot, input.source),
23
- ].find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory());
20
+ const researchRoot = path.join(taskRoot, 'research');
21
+ const sourceDir = [path.resolve(cwd, input.source), path.resolve(researchRoot, input.source)].find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory());
24
22
  if (sourceDir === undefined) {
25
- throw new Error(`INVALID_SOURCE: no task directory at ${input.source}. Hint: pass the task path or a directory name under .owflow/tasks/research/.`);
23
+ throw new Error(`INVALID_SOURCE: no task directory at ${input.source}. Hint: pass the task path or a directory name under ${workflowRelativePath(TASKS_DIRECTORY_NAME, RESEARCH_DIRECTORY_NAME)}/.`);
26
24
  }
27
25
  if (!sourceDir.startsWith(researchRoot + path.sep)) {
28
- throw new Error(`INVALID_SOURCE: ${input.source} is not a research task directory under .owflow/tasks/research/.`);
26
+ throw new Error(`INVALID_SOURCE: ${input.source} is not a research task directory under ${workflowRelativePath(TASKS_DIRECTORY_NAME, RESEARCH_DIRECTORY_NAME)}/.`);
29
27
  }
30
- const parsedSourceState = parseYamlFile(path.join(sourceDir, "orchestrator-state.yml"));
28
+ const parsedSourceState = parseYamlFile(path.join(sourceDir, 'orchestrator-state.yml'));
31
29
  if (!parsedSourceState.ok || !parsedSourceState.data) {
32
30
  throw new Error(`INVALID_SOURCE: ${parsedSourceState.error ?? `no orchestrator-state.yml in ${input.source}`}`);
33
31
  }
34
32
  const sourceState = parsedSourceState.data;
35
33
  const completed = Array.isArray(sourceState?.orchestrator?.completed_phases)
36
- ? sourceState.orchestrator.completed_phases.filter((s) => typeof s === "string")
34
+ ? sourceState.orchestrator.completed_phases.filter((s) => typeof s === 'string')
37
35
  : [];
38
36
  if (input.from === FORK_TERMINAL_STEP) {
39
37
  throw new Error(`UNKNOWN_STEP: ${FORK_TERMINAL_STEP} is not a valid fork point — nothing diverges after completion.`);
40
38
  }
41
39
  if (!completed.includes(input.from)) {
42
- throw new Error(`UNKNOWN_STEP: ${input.from} is not completed in the source task. Completed steps: ${completed.join(", ") || "none"}`);
40
+ throw new Error(`UNKNOWN_STEP: ${input.from} is not completed in the source task. Completed steps: ${completed.join(', ') || 'none'}`);
43
41
  }
44
42
  const forkName = `${new Date().toISOString().slice(0, 10)}-${input.slug}`;
45
43
  const forkPath = path.join(researchRoot, forkName);
@@ -50,11 +48,8 @@ export const forkTask = (input) => {
50
48
  const forkState = structuredClone(sourceState);
51
49
  forkState.orchestrator.started_phase = null;
52
50
  forkState.orchestrator.completed_phases = completed.slice(0, completed.indexOf(input.from) + 1);
53
- forkState.orchestrator.entry_point = "research-fork";
54
- forkState.orchestrator.task_path = path
55
- .relative(cwd, forkPath)
56
- .split(path.sep)
57
- .join("/");
51
+ forkState.orchestrator.entry_point = 'research-fork';
52
+ forkState.orchestrator.task_path = path.relative(cwd, forkPath).split(path.sep).join('/');
58
53
  forkState.orchestrator.options = {
59
54
  ...(forkState.orchestrator.options ?? {}),
60
55
  fork_information: {
@@ -66,9 +61,9 @@ export const forkTask = (input) => {
66
61
  };
67
62
  forkState.task = {
68
63
  ...forkState.task,
69
- status: "in_progress",
70
- title: `${forkState.task?.title ?? "Research task"} (fork of ${path.basename(sourceDir)})`,
64
+ status: 'in_progress',
65
+ title: `${forkState.task?.title ?? 'Research task'} (fork of ${path.basename(sourceDir)})`,
71
66
  };
72
- fs.writeFileSync(path.join(forkPath, "orchestrator-state.yml"), yaml.stringify(forkState), "utf8");
67
+ fs.writeFileSync(path.join(forkPath, 'orchestrator-state.yml'), yaml.stringify(forkState), 'utf8');
73
68
  return { forkPath, forkName, forkPoint: input.from };
74
69
  };
@@ -0,0 +1,3 @@
1
+ export declare const PLUGIN_ROOT: string;
2
+ export declare const readPluginVersion: (pluginRoot?: string) => string;
3
+ export declare const PLUGIN_VERSION: string;
@@ -1,17 +1,15 @@
1
- import path from "node:path";
2
- import fs from "node:fs";
3
- import { fileURLToPath } from "node:url";
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
4
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
- export const PLUGIN_ROOT = path.join(__dirname, ".."); // Points to dist/ where skills/commands/agents are copied
6
- const UNKNOWN_VERSION = "unknown";
5
+ export const PLUGIN_ROOT = path.join(__dirname, '..'); // Points to dist/ where skills/commands/agents are copied
6
+ const UNKNOWN_VERSION = 'unknown';
7
7
  export const readPluginVersion = (pluginRoot = PLUGIN_ROOT) => {
8
8
  try {
9
- const packageJsonPath = path.join(pluginRoot, "..", "package.json");
10
- const raw = fs.readFileSync(packageJsonPath, "utf8");
9
+ const packageJsonPath = path.join(pluginRoot, '..', 'package.json');
10
+ const raw = fs.readFileSync(packageJsonPath, 'utf8');
11
11
  const version = JSON.parse(raw).version;
12
- return typeof version === "string" && version.length > 0
13
- ? version
14
- : UNKNOWN_VERSION;
12
+ return typeof version === 'string' && version.length > 0 ? version : UNKNOWN_VERSION;
15
13
  }
16
14
  catch {
17
15
  return UNKNOWN_VERSION;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Canonical owflow directory names. Host projects and every internal surface
3
+ * (server plugin, CLI, TUI, hooks, tools) refer to these constants instead of
4
+ * repeating path fragments.
5
+ */
6
+ export declare const WORKFLOW_DIRECTORY_NAME = ".owflow";
7
+ export declare const HOOKS_DIRECTORY_NAME = "hooks";
8
+ export declare const TASKS_DIRECTORY_NAME = "tasks";
9
+ export declare const DOCS_DIRECTORY_NAME = "docs";
10
+ export declare const RESEARCH_DIRECTORY_NAME = "research";
11
+ export declare const workflowPath: (root: string, ...segments: readonly string[]) => string;
12
+ export declare const workflowRelativePath: (...segments: readonly string[]) => string;
13
+ export declare const hooksDirectory: (root: string) => string;
14
+ export declare const tasksDirectory: (root: string) => string;
15
+ export declare const docsDirectory: (root: string) => string;
16
+ export declare const hooksLocationLabel: string;
17
+ export declare const tasksLocationLabel: string;
@@ -0,0 +1,18 @@
1
+ import path from 'node:path';
2
+ /**
3
+ * Canonical owflow directory names. Host projects and every internal surface
4
+ * (server plugin, CLI, TUI, hooks, tools) refer to these constants instead of
5
+ * repeating path fragments.
6
+ */
7
+ export const WORKFLOW_DIRECTORY_NAME = '.owflow';
8
+ export const HOOKS_DIRECTORY_NAME = 'hooks';
9
+ export const TASKS_DIRECTORY_NAME = 'tasks';
10
+ export const DOCS_DIRECTORY_NAME = 'docs';
11
+ export const RESEARCH_DIRECTORY_NAME = 'research';
12
+ export const workflowPath = (root, ...segments) => path.join(root, WORKFLOW_DIRECTORY_NAME, ...segments);
13
+ export const workflowRelativePath = (...segments) => [WORKFLOW_DIRECTORY_NAME, ...segments].join('/');
14
+ export const hooksDirectory = (root) => workflowPath(root, HOOKS_DIRECTORY_NAME);
15
+ export const tasksDirectory = (root) => workflowPath(root, TASKS_DIRECTORY_NAME);
16
+ export const docsDirectory = (root) => workflowPath(root, DOCS_DIRECTORY_NAME);
17
+ export const hooksLocationLabel = workflowRelativePath(HOOKS_DIRECTORY_NAME);
18
+ export const tasksLocationLabel = workflowRelativePath(TASKS_DIRECTORY_NAME);
@@ -0,0 +1,7 @@
1
+ export interface ParsedYaml {
2
+ ok: boolean;
3
+ data?: any;
4
+ error?: string;
5
+ }
6
+ export declare const parseYamlFile: (absolutePath: string) => ParsedYaml;
7
+ export declare const findStructuralErrors: (templateObj: any, targetObj: any, currentPath?: string) => string[];
@@ -1,30 +1,26 @@
1
- import fs from "node:fs";
2
- import yaml from "yaml";
1
+ import fs from 'node:fs';
2
+ import yaml from 'yaml';
3
3
  export const parseYamlFile = (absolutePath) => {
4
4
  try {
5
- const fileContent = fs.readFileSync(absolutePath, "utf8");
5
+ const fileContent = fs.readFileSync(absolutePath, 'utf8');
6
6
  return { ok: true, data: yaml.parse(fileContent) };
7
7
  }
8
8
  catch (error) {
9
9
  return { ok: false, error: error.message };
10
10
  }
11
11
  };
12
- export const findStructuralErrors = (templateObj, targetObj, currentPath = "") => {
12
+ export const findStructuralErrors = (templateObj, targetObj, currentPath = '') => {
13
13
  const errors = [];
14
- if (typeof templateObj !== "object" || templateObj === null) {
14
+ if (typeof templateObj !== 'object' || templateObj === null) {
15
15
  return errors;
16
16
  }
17
17
  for (const key of Object.keys(templateObj)) {
18
18
  const newPath = currentPath ? `${currentPath}.${key}` : key;
19
- if (typeof targetObj !== "object" ||
20
- targetObj === null ||
21
- !(key in targetObj)) {
19
+ if (typeof targetObj !== 'object' || targetObj === null || !(key in targetObj)) {
22
20
  errors.push(`Missing key: ${newPath}`);
23
21
  continue;
24
22
  }
25
- if (typeof templateObj[key] === "object" &&
26
- templateObj[key] !== null &&
27
- !Array.isArray(templateObj[key])) {
23
+ if (typeof templateObj[key] === 'object' && templateObj[key] !== null && !Array.isArray(templateObj[key])) {
28
24
  errors.push(...findStructuralErrors(templateObj[key], targetObj[key], newPath));
29
25
  }
30
26
  }