@s_s/harmonia 1.2.0 → 1.4.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 (180) hide show
  1. package/README.md +140 -392
  2. package/build/cli/setup.d.ts +4 -2
  3. package/build/cli/setup.js +44 -18
  4. package/build/cli/setup.js.map +1 -1
  5. package/build/core/action-registry.d.ts +36 -0
  6. package/build/core/action-registry.js +53 -0
  7. package/build/core/action-registry.js.map +1 -0
  8. package/build/core/artifacts.d.ts +66 -0
  9. package/build/core/artifacts.js +178 -0
  10. package/build/core/artifacts.js.map +1 -0
  11. package/build/core/dispatch.d.ts +18 -11
  12. package/build/core/dispatch.js +43 -33
  13. package/build/core/dispatch.js.map +1 -1
  14. package/build/core/issues.d.ts +37 -0
  15. package/build/core/issues.js +100 -0
  16. package/build/core/issues.js.map +1 -0
  17. package/build/core/overrides.d.ts +19 -26
  18. package/build/core/overrides.js +32 -98
  19. package/build/core/overrides.js.map +1 -1
  20. package/build/core/plugin.d.ts +86 -0
  21. package/build/core/plugin.js +332 -0
  22. package/build/core/plugin.js.map +1 -0
  23. package/build/core/registry.d.ts +36 -3
  24. package/build/core/registry.js +63 -5
  25. package/build/core/registry.js.map +1 -1
  26. package/build/core/reviews.d.ts +13 -13
  27. package/build/core/reviews.js +31 -32
  28. package/build/core/reviews.js.map +1 -1
  29. package/build/core/schema.d.ts +43 -15
  30. package/build/core/schema.js +124 -20
  31. package/build/core/schema.js.map +1 -1
  32. package/build/core/state.d.ts +29 -22
  33. package/build/core/state.js +49 -81
  34. package/build/core/state.js.map +1 -1
  35. package/build/core/steps.d.ts +15 -15
  36. package/build/core/steps.js +32 -33
  37. package/build/core/steps.js.map +1 -1
  38. package/build/core/tree-utils.d.ts +52 -0
  39. package/build/core/tree-utils.js +226 -0
  40. package/build/core/tree-utils.js.map +1 -0
  41. package/build/core/types.d.ts +417 -117
  42. package/build/core/types.js +15 -1
  43. package/build/core/types.js.map +1 -1
  44. package/build/core/workflow-engine.d.ts +68 -0
  45. package/build/core/workflow-engine.js +821 -0
  46. package/build/core/workflow-engine.js.map +1 -0
  47. package/build/core/workflow-validator.d.ts +22 -0
  48. package/build/core/workflow-validator.js +489 -0
  49. package/build/core/workflow-validator.js.map +1 -0
  50. package/build/index.js +28 -25
  51. package/build/index.js.map +1 -1
  52. package/build/setup/inject.d.ts +4 -4
  53. package/build/setup/inject.js +6 -6
  54. package/build/setup/inject.js.map +1 -1
  55. package/build/setup/templates.d.ts +9 -7
  56. package/build/setup/templates.js +68 -103
  57. package/build/setup/templates.js.map +1 -1
  58. package/build/tools/artifact-approve.d.ts +8 -0
  59. package/build/tools/artifact-approve.js +94 -0
  60. package/build/tools/artifact-approve.js.map +1 -0
  61. package/build/tools/artifact-schema.d.ts +12 -0
  62. package/build/tools/artifact-schema.js +148 -0
  63. package/build/tools/artifact-schema.js.map +1 -0
  64. package/build/tools/artifact-tools.d.ts +18 -0
  65. package/build/tools/artifact-tools.js +465 -0
  66. package/build/tools/artifact-tools.js.map +1 -0
  67. package/build/tools/{report-dispatch.d.ts → dispatch-report.d.ts} +7 -3
  68. package/build/tools/dispatch-report.js +261 -0
  69. package/build/tools/dispatch-report.js.map +1 -0
  70. package/build/tools/engine-helpers.d.ts +41 -0
  71. package/build/tools/engine-helpers.js +182 -0
  72. package/build/tools/engine-helpers.js.map +1 -0
  73. package/build/tools/get-project-status.d.ts +6 -4
  74. package/build/tools/get-project-status.js +308 -246
  75. package/build/tools/get-project-status.js.map +1 -1
  76. package/build/tools/get-role-prompt.d.ts +1 -1
  77. package/build/tools/get-role-prompt.js +7 -41
  78. package/build/tools/get-role-prompt.js.map +1 -1
  79. package/build/tools/issue-tools.d.ts +10 -0
  80. package/build/tools/issue-tools.js +169 -0
  81. package/build/tools/issue-tools.js.map +1 -0
  82. package/build/tools/iteration-start.d.ts +7 -4
  83. package/build/tools/iteration-start.js +51 -20
  84. package/build/tools/iteration-start.js.map +1 -1
  85. package/build/tools/loop-done.d.ts +11 -0
  86. package/build/tools/loop-done.js +109 -0
  87. package/build/tools/loop-done.js.map +1 -0
  88. package/build/tools/patch-start.d.ts +16 -0
  89. package/build/tools/patch-start.js +122 -0
  90. package/build/tools/patch-start.js.map +1 -0
  91. package/build/tools/project-init.d.ts +5 -5
  92. package/build/tools/project-init.js +47 -18
  93. package/build/tools/project-init.js.map +1 -1
  94. package/build/tools/role-dispatch.d.ts +55 -0
  95. package/build/tools/role-dispatch.js +508 -0
  96. package/build/tools/role-dispatch.js.map +1 -0
  97. package/build/tools/utils.d.ts +40 -0
  98. package/build/tools/utils.js +97 -0
  99. package/build/tools/utils.js.map +1 -0
  100. package/package.json +1 -1
  101. package/{build/hooks/claude-code.js → workflows/dev/hooks/claude.js} +34 -23
  102. package/{build → workflows/dev}/hooks/content.js +27 -18
  103. package/workflows/dev/hooks/index.js +52 -0
  104. package/{build → workflows/dev}/hooks/openclaw.js +31 -20
  105. package/{build → workflows/dev}/hooks/opencode.js +31 -20
  106. package/workflows/dev/roles/architect.md +68 -28
  107. package/workflows/dev/roles/coordinator.md +103 -0
  108. package/workflows/dev/roles/developer.md +5 -5
  109. package/workflows/dev/roles/tester.md +19 -19
  110. package/workflows/dev/schemas/api-contract.json +42 -0
  111. package/workflows/dev/schemas/api-design.json +30 -13
  112. package/workflows/dev/schemas/data-model.json +20 -7
  113. package/workflows/dev/schemas/prd.completeness-check.json +6 -5
  114. package/workflows/dev/schemas/prd.draft.json +13 -5
  115. package/workflows/dev/schemas/prd.final.json +34 -11
  116. package/workflows/dev/schemas/prd.json +29 -11
  117. package/workflows/dev/schemas/prd.requirements.json +6 -5
  118. package/workflows/dev/schemas/prototype.json +6 -2
  119. package/workflows/dev/schemas/task-breakdown.coarse.json +4 -3
  120. package/workflows/dev/schemas/task-breakdown.dependencies.json +5 -4
  121. package/workflows/dev/schemas/task-breakdown.detailed.json +8 -3
  122. package/workflows/dev/schemas/task-breakdown.final.json +8 -3
  123. package/workflows/dev/schemas/task-breakdown.json +8 -3
  124. package/workflows/dev/schemas/tech-design.analysis.json +6 -5
  125. package/workflows/dev/schemas/tech-design.draft.json +14 -5
  126. package/workflows/dev/schemas/tech-design.final.json +39 -13
  127. package/workflows/dev/schemas/tech-design.json +34 -13
  128. package/workflows/dev/schemas/tech-design.research.json +21 -0
  129. package/workflows/dev/schemas/test-plan.json +17 -7
  130. package/workflows/dev/schemas/test-report.json +26 -9
  131. package/workflows/dev/schemas/user-stories.json +7 -3
  132. package/workflows/dev/tools/index.js +23 -0
  133. package/workflows/dev/workflow.json +234 -101
  134. package/build/core/docs.d.ts +0 -32
  135. package/build/core/docs.js +0 -91
  136. package/build/core/docs.js.map +0 -1
  137. package/build/core/workflow.d.ts +0 -33
  138. package/build/core/workflow.js +0 -140
  139. package/build/core/workflow.js.map +0 -1
  140. package/build/hooks/claude-code.d.ts +0 -20
  141. package/build/hooks/claude-code.js.map +0 -1
  142. package/build/hooks/content.d.ts +0 -43
  143. package/build/hooks/content.js.map +0 -1
  144. package/build/hooks/install.d.ts +0 -40
  145. package/build/hooks/install.js +0 -63
  146. package/build/hooks/install.js.map +0 -1
  147. package/build/hooks/openclaw.d.ts +0 -24
  148. package/build/hooks/openclaw.js.map +0 -1
  149. package/build/hooks/opencode.d.ts +0 -29
  150. package/build/hooks/opencode.js.map +0 -1
  151. package/build/tools/approve-doc.d.ts +0 -6
  152. package/build/tools/approve-doc.js +0 -108
  153. package/build/tools/approve-doc.js.map +0 -1
  154. package/build/tools/dispatch-role.d.ts +0 -16
  155. package/build/tools/dispatch-role.js +0 -277
  156. package/build/tools/dispatch-role.js.map +0 -1
  157. package/build/tools/doc-tools.d.ts +0 -16
  158. package/build/tools/doc-tools.js +0 -389
  159. package/build/tools/doc-tools.js.map +0 -1
  160. package/build/tools/override-tools.d.ts +0 -6
  161. package/build/tools/override-tools.js +0 -129
  162. package/build/tools/override-tools.js.map +0 -1
  163. package/build/tools/report-dispatch.js +0 -194
  164. package/build/tools/report-dispatch.js.map +0 -1
  165. package/build/tools/set-scale.d.ts +0 -6
  166. package/build/tools/set-scale.js +0 -107
  167. package/build/tools/set-scale.js.map +0 -1
  168. package/build/tools/setup-project.d.ts +0 -8
  169. package/build/tools/setup-project.js +0 -116
  170. package/build/tools/setup-project.js.map +0 -1
  171. package/build/tools/update-phase.d.ts +0 -12
  172. package/build/tools/update-phase.js +0 -159
  173. package/build/tools/update-phase.js.map +0 -1
  174. package/workflows/dev/roles/pm.md +0 -99
  175. package/workflows/dev/schemas/deploy.json +0 -20
  176. package/workflows/dev/schemas/fsd.json +0 -25
  177. package/workflows/dev/schemas/project-plan.json +0 -20
  178. package/workflows/dev/schemas/retrospective.json +0 -20
  179. package/workflows/dev/schemas/risk-assessment.json +0 -15
  180. package/workflows/dev/schemas/tech-design.api-contract.json +0 -20
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/tools/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@s_s/harmonia",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Multi-agent orchestration MCP server with pluggable workflows",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",
@@ -7,14 +7,21 @@
7
7
  *
8
8
  * Hooks:
9
9
  * 1. PreToolUse — boundary guard: block code edits and dev commands
10
- * 2. UserPromptSubmit — proactive reminders: dispatch timeout, idle phase, pending reviews
10
+ * 2. UserPromptSubmit — proactive reminders: dispatch timeout, idle timeout, pending reviews
11
11
  *
12
12
  * Project-agnostic: no project name/dir baked in.
13
13
  * - Boundary guard uses tool names + code file extensions only
14
14
  * - Reminders scan all projects under DATA_DIR
15
15
  */
16
- import { defineHooks } from '@s_s/agent-kit';
17
- import { BLOCKED_COMMANDS, CODE_EXTENSIONS, DISPATCH_TIMEOUT_MINUTES, PHASE_IDLE_TIMEOUT_MINUTES, REVIEW_PENDING_TIMEOUT_MINUTES, } from './content.js';
16
+
17
+ import {
18
+ BLOCKED_COMMANDS,
19
+ CODE_EXTENSIONS,
20
+ DISPATCH_TIMEOUT_MINUTES,
21
+ WORKFLOW_IDLE_TIMEOUT_MINUTES,
22
+ REVIEW_PENDING_TIMEOUT_MINUTES,
23
+ } from './content.js';
24
+
18
25
  /**
19
26
  * Generate Claude Code PreToolUse hook script.
20
27
  *
@@ -30,9 +37,10 @@ import { BLOCKED_COMMANDS, CODE_EXTENSIONS, DISPATCH_TIMEOUT_MINUTES, PHASE_IDLE
30
37
  function generatePreToolUseScript(_params) {
31
38
  const codeExtsPattern = CODE_EXTENSIONS.map((e) => `*${e}`).join('|');
32
39
  const blockedCmdsChecks = BLOCKED_COMMANDS.map((cmd) => ` *"${cmd}"*) BLOCKED_CMD="${cmd}" ;;`).join('\n');
40
+
33
41
  return `#!/bin/bash
34
- # Harmonia PM boundary guard — PreToolUse hook
35
- # Prevents PM from directly modifying code or running dev commands.
42
+ # Harmonia coordinator boundary guard — PreToolUse hook
43
+ # Prevents coordinator from directly modifying code or running dev commands.
36
44
  # Generated by Harmonia setup. Do not edit manually.
37
45
 
38
46
  set -euo pipefail
@@ -61,7 +69,7 @@ case "$TOOL_NAME" in
61
69
  # Block writes to code files (by extension)
62
70
  case "$FILE_PATH" in
63
71
  ${codeExtsPattern})
64
- block "PM 不应直接修改代码文件。请通过 role_dispatch 将编码任务分配给 developer。文件: $FILE_PATH"
72
+ block "Coordinator 不应直接修改代码文件。请通过 role_dispatch 将编码任务分配给 developer。文件: $FILE_PATH"
65
73
  ;;
66
74
  esac
67
75
  ;;
@@ -82,7 +90,7 @@ ${blockedCmdsChecks}
82
90
  esac
83
91
 
84
92
  if [ -n "$BLOCKED_CMD" ]; then
85
- block "PM 不应直接执行开发命令 ($BLOCKED_CMD...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。"
93
+ block "Coordinator 不应直接执行开发命令 ($BLOCKED_CMD...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。"
86
94
  fi
87
95
  ;;
88
96
  esac
@@ -91,13 +99,14 @@ esac
91
99
  exit 0
92
100
  `;
93
101
  }
102
+
94
103
  /**
95
104
  * Generate Claude Code UserPromptSubmit hook script.
96
105
  *
97
106
  * Scans all projects under DATA_DIR for:
98
107
  * - Running dispatches that exceed timeout
99
- * - Idle phase warning
100
- * - Pending document reviews
108
+ * - Idle timeout warning
109
+ * - Pending artifact reviews
101
110
  */
102
111
  function generateUserPromptSubmitScript(params) {
103
112
  return `#!/bin/bash
@@ -143,42 +152,41 @@ if [ -d "$DATA_DIR" ]; then
143
152
  done <<< "$RUNNING"
144
153
  fi
145
154
 
146
- # ── Check 2: Pending document reviews ──
155
+ # ── Check 2: Pending artifact reviews ──
147
156
  REVIEWS_FILE="$PROJECT_DATA/reviews.json"
148
157
  if [ -f "$REVIEWS_FILE" ]; then
149
158
  PENDING=$(jq -r 'to_entries[] | select(.value.status == "pending") | "\\(.key)|\\(.value.submittedAt)"' "$REVIEWS_FILE" 2>/dev/null || true)
150
159
  PENDING_COUNT=0
151
- PENDING_DOCS=""
160
+ PENDING_ARTIFACTS=""
152
161
 
153
- while IFS='|' read -r DOC_ID SUBMITTED_AT; do
154
- [ -z "$DOC_ID" ] && continue
162
+ while IFS='|' read -r ARTIFACT_ID SUBMITTED_AT; do
163
+ [ -z "$ARTIFACT_ID" ] && continue
155
164
  SUBMITTED_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$SUBMITTED_AT" | cut -c1-19)" +%s 2>/dev/null || echo "0")
156
165
  if [ "$SUBMITTED_EPOCH" != "0" ]; then
157
166
  ELAPSED_MIN=$(( (NOW_EPOCH - SUBMITTED_EPOCH) / 60 ))
158
167
  if [ "$ELAPSED_MIN" -ge ${REVIEW_PENDING_TIMEOUT_MINUTES} ]; then
159
168
  PENDING_COUNT=$((PENDING_COUNT + 1))
160
- PENDING_DOCS="$PENDING_DOCS $DOC_ID"
169
+ PENDING_ARTIFACTS="$PENDING_ARTIFACTS $ARTIFACT_ID"
161
170
  fi
162
171
  fi
163
172
  done <<< "$PENDING"
164
173
 
165
174
  if [ "$PENDING_COUNT" -gt 0 ]; then
166
- add_reminder "- [$PROJECT_NAME] $PENDING_COUNT 份文档待审核超过 ${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟:$PENDING_DOCS — 请尽快处理(doc_approve / reject_doc)"
175
+ add_reminder "- [$PROJECT_NAME] $PENDING_COUNT 份产出待审核超过 ${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟:$PENDING_ARTIFACTS — 请尽快处理(artifact_approve)"
167
176
  fi
168
177
  fi
169
178
 
170
- # ── Check 3: Phase idle check ──
179
+ # ── Check 3: Workflow idle check ──
171
180
  STATE_FILE="$PROJECT_DATA/state.json"
172
181
  if [ -f "$STATE_FILE" ]; then
173
182
  UPDATED_AT=$(jq -r '.updatedAt // empty' "$STATE_FILE" 2>/dev/null || true)
174
- CURRENT_PHASE=$(jq -r '.currentPhase // empty' "$STATE_FILE" 2>/dev/null || true)
175
183
 
176
- if [ -n "$UPDATED_AT" ] && [ -n "$CURRENT_PHASE" ]; then
184
+ if [ -n "$UPDATED_AT" ]; then
177
185
  STATE_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$UPDATED_AT" | cut -c1-19)" +%s 2>/dev/null || echo "0")
178
186
  if [ "$STATE_EPOCH" != "0" ]; then
179
187
  IDLE_MIN=$(( (NOW_EPOCH - STATE_EPOCH) / 60 ))
180
- if [ "$IDLE_MIN" -ge ${PHASE_IDLE_TIMEOUT_MINUTES} ]; then
181
- add_reminder "- [$PROJECT_NAME] 当前阶段 ($CURRENT_PHASE) 已空闲 $IDLE_MIN 分钟,建议调用 project_status 检查项目状态"
188
+ if [ "$IDLE_MIN" -ge ${WORKFLOW_IDLE_TIMEOUT_MINUTES} ]; then
189
+ add_reminder "- [$PROJECT_NAME] 工作流已空闲 $IDLE_MIN 分钟,建议调用 project_status 检查项目状态"
182
190
  fi
183
191
  fi
184
192
  fi
@@ -200,10 +208,14 @@ fi
200
208
  exit 0
201
209
  `;
202
210
  }
211
+
203
212
  /**
204
- * Create Claude Code hook definitions using agent-kit's defineHooks.
213
+ * Create Claude Code hook definitions using defineHooks from context.
214
+ *
215
+ * @param {Function} defineHooks - defineHooks function from agent-kit (passed via context)
216
+ * @param {{ dataDir: string }} params - Parameters to bake into hook content
205
217
  */
206
- export function createClaudeCodeHooks(params) {
218
+ export function createClaudeCodeHooks(defineHooks, params) {
207
219
  return defineHooks('claude-code', [
208
220
  {
209
221
  events: ['PreToolUse'],
@@ -215,4 +227,3 @@ export function createClaudeCodeHooks(params) {
215
227
  },
216
228
  ]);
217
229
  }
218
- //# sourceMappingURL=claude-code.js.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Hook content generation — shared configuration and rule definitions.
2
+ * Hook content — shared configuration and rule definitions.
3
3
  *
4
4
  * Hook scripts run on the agent side (shell scripts for Claude Code,
5
5
  * TS plugins for OpenCode, handlers for OpenClaw). They need to know:
@@ -9,9 +9,11 @@
9
9
  * Boundary guards work on tool names + code file extensions only.
10
10
  * Proactive reminders scan all projects under the data directory.
11
11
  */
12
+
12
13
  // ─── Boundary Rules ───
14
+
13
15
  /**
14
- * Tool names that PM should not call directly (code modification tools).
16
+ * Tool names that coordinator should not call directly (code modification tools).
15
17
  * These are the standard agent tool names across different platforms.
16
18
  */
17
19
  export const BLOCKED_TOOLS = [
@@ -27,8 +29,9 @@ export const BLOCKED_TOOLS = [
27
29
  'Terminal',
28
30
  'terminal',
29
31
  ];
32
+
30
33
  /**
31
- * Shell commands that indicate development work (PM should not run these).
34
+ * Shell commands that indicate development work (coordinator should not run these).
32
35
  */
33
36
  export const BLOCKED_COMMANDS = [
34
37
  'npm run',
@@ -52,8 +55,9 @@ export const BLOCKED_COMMANDS = [
52
55
  'mvn ',
53
56
  'gradle ',
54
57
  ];
58
+
55
59
  /**
56
- * File extensions that indicate source code (PM should not modify these).
60
+ * File extensions that indicate source code (coordinator should not modify these).
57
61
  */
58
62
  export const CODE_EXTENSIONS = [
59
63
  '.ts',
@@ -78,32 +82,37 @@ export const CODE_EXTENSIONS = [
78
82
  '.vue',
79
83
  '.svelte',
80
84
  ];
85
+
81
86
  /**
82
- * Harmonia MCP tool names — these are always allowed since PM uses them
87
+ * Harmonia MCP tool names — these are always allowed since coordinator uses them
83
88
  * through Harmonia's own tool system.
84
89
  */
85
90
  export const HARMONIA_TOOLS = [
86
91
  'project_init',
87
- 'project_set_scale',
88
92
  'project_status',
89
- 'phase_update',
93
+ 'iteration_start',
94
+ 'patch_start',
90
95
  'role_dispatch',
91
96
  'dispatch_report',
92
- 'doc_write',
93
- 'doc_read',
94
- 'doc_list',
95
- 'doc_approve',
96
- 'reject_doc',
97
- 'guard_set',
98
- 'guard_get',
99
- 'review_set_rule',
97
+ 'artifact_write',
98
+ 'artifact_read',
99
+ 'artifact_list',
100
+ 'artifact_approve',
101
+ 'artifact_schema',
100
102
  'review_list',
103
+ 'role_prompt',
104
+ 'issue_create',
105
+ 'issue_update',
106
+ 'issue_list',
101
107
  ];
108
+
102
109
  // ─── Timeout thresholds (minutes) ───
110
+
103
111
  /** Dispatch running timeout — warn after this many minutes */
104
112
  export const DISPATCH_TIMEOUT_MINUTES = 30;
105
- /** Phase idle timeout — warn after this many minutes with no tool calls */
106
- export const PHASE_IDLE_TIMEOUT_MINUTES = 15;
113
+
114
+ /** Workflow idle timeout — warn after this many minutes with no tool calls */
115
+ export const WORKFLOW_IDLE_TIMEOUT_MINUTES = 15;
116
+
107
117
  /** Review pending timeout — warn after this many minutes */
108
118
  export const REVIEW_PENDING_TIMEOUT_MINUTES = 10;
109
- //# sourceMappingURL=content.js.map
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Dev workflow plugin — hook creator.
3
+ *
4
+ * Entry point for hook creation. All hook generation logic lives in
5
+ * sibling files within this directory — no cross-directory imports.
6
+ *
7
+ * The defineHooks function is received from Core via context — this module
8
+ * does NOT directly depend on @s_s/agent-kit.
9
+ */
10
+
11
+ import { createClaudeCodeHooks } from './claude.js';
12
+ import { createOpenCodeHooks } from './opencode.js';
13
+ import { createOpenClawHooks } from './openclaw.js';
14
+
15
+ /**
16
+ * Create hook definitions for a specific agent type.
17
+ *
18
+ * Routes to the appropriate agent-specific hook generator,
19
+ * passing through the defineHooks function from context.
20
+ *
21
+ * @param {Function} defineHooks - defineHooks function from agent-kit (passed via context)
22
+ * @param {string} agentType - Agent type (opencode, claude-code, openclaw, codex)
23
+ * @param {{ dataDir: string }} params - Parameters to bake into hook content
24
+ * @returns {import('@s_s/agent-kit').HookSet} Hook set for the agent
25
+ */
26
+ function createHooksForAgent(defineHooks, agentType, params) {
27
+ switch (agentType) {
28
+ case 'claude-code':
29
+ case 'codex':
30
+ return createClaudeCodeHooks(defineHooks, params);
31
+ case 'opencode':
32
+ return createOpenCodeHooks(defineHooks, params);
33
+ case 'openclaw':
34
+ return createOpenClawHooks(defineHooks, params);
35
+ default:
36
+ throw new Error(`Unsupported agent type: ${agentType}`);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Create hooks for the dev workflow.
42
+ *
43
+ * Matches the HookCreator signature: (agentType, context) => HookSet.
44
+ * Called by the plugin system during project_init.
45
+ *
46
+ * @param {string} agentType - Agent type (opencode, claude-code, openclaw, codex)
47
+ * @param {{ defineHooks: Function, dataDir: string, projectName: string }} context
48
+ * @returns {import('@s_s/agent-kit').HookSet} Hook set for the agent
49
+ */
50
+ export function createHooks(agentType, context) {
51
+ return createHooksForAgent(context.defineHooks, agentType, { dataDir: context.dataDir });
52
+ }
@@ -15,18 +15,26 @@
15
15
  * - Boundary guard uses tool names + code file extensions only
16
16
  * - Reminders scan all projects under DATA_DIR
17
17
  */
18
- import { defineHooks } from '@s_s/agent-kit';
19
- import { BLOCKED_COMMANDS, CODE_EXTENSIONS, DISPATCH_TIMEOUT_MINUTES, PHASE_IDLE_TIMEOUT_MINUTES, REVIEW_PENDING_TIMEOUT_MINUTES, } from './content.js';
18
+
19
+ import {
20
+ BLOCKED_COMMANDS,
21
+ CODE_EXTENSIONS,
22
+ DISPATCH_TIMEOUT_MINUTES,
23
+ WORKFLOW_IDLE_TIMEOUT_MINUTES,
24
+ REVIEW_PENDING_TIMEOUT_MINUTES,
25
+ } from './content.js';
26
+
20
27
  /**
21
28
  * Generate the OpenClaw handler.ts source code.
22
29
  *
23
30
  * Handles two events:
24
31
  * 1. before_tool_call — boundary guard: block code edits and dev commands
25
- * 2. message_received — proactive reminders: dispatch timeout, idle phase, pending reviews
32
+ * 2. message_received — proactive reminders: dispatch timeout, idle timeout, pending reviews
26
33
  */
27
34
  function generateOpenClawHandler(params) {
28
35
  const codeExtsJson = JSON.stringify(CODE_EXTENSIONS);
29
36
  const blockedCmdsJson = JSON.stringify(BLOCKED_COMMANDS);
37
+
30
38
  return `import { readFileSync, readdirSync, statSync } from 'fs';
31
39
  import { resolve, join } from 'path';
32
40
 
@@ -36,7 +44,7 @@ const DATA_DIR = ${JSON.stringify(params.dataDir)};
36
44
  const CODE_EXTENSIONS: readonly string[] = ${codeExtsJson};
37
45
  const BLOCKED_COMMANDS: readonly string[] = ${blockedCmdsJson};
38
46
  const DISPATCH_TIMEOUT_MINUTES = ${DISPATCH_TIMEOUT_MINUTES};
39
- const PHASE_IDLE_TIMEOUT_MINUTES = ${PHASE_IDLE_TIMEOUT_MINUTES};
47
+ const WORKFLOW_IDLE_TIMEOUT_MINUTES = ${WORKFLOW_IDLE_TIMEOUT_MINUTES};
40
48
  const REVIEW_PENDING_TIMEOUT_MINUTES = ${REVIEW_PENDING_TIMEOUT_MINUTES};
41
49
 
42
50
  // ── Helpers ──
@@ -99,7 +107,7 @@ function handleBeforeToolCall(event: any): any {
99
107
  if (filePath && isCodeFile(filePath)) {
100
108
  return {
101
109
  block: true,
102
- reason: \`PM 不应直接修改代码文件。请通过 role_dispatch 将编码任务分配给 developer。文件: \${filePath}\`,
110
+ reason: \`Coordinator 不应直接修改代码文件。请通过 role_dispatch 将编码任务分配给 developer。文件: \${filePath}\`,
103
111
  };
104
112
  }
105
113
  }
@@ -112,7 +120,7 @@ function handleBeforeToolCall(event: any): any {
112
120
  if (blocked) {
113
121
  return {
114
122
  block: true,
115
- reason: \`PM 不应直接执行开发命令 (\${blocked}...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。\`,
123
+ reason: \`Coordinator 不应直接执行开发命令 (\${blocked}...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。\`,
116
124
  };
117
125
  }
118
126
  }
@@ -143,32 +151,32 @@ function handleMessageReceived(): any {
143
151
  }
144
152
  }
145
153
 
146
- // Check 2: Pending document reviews
154
+ // Check 2: Pending artifact reviews
147
155
  const reviews = readJsonSafe(resolve(proj.path, 'reviews.json'));
148
156
  if (reviews && typeof reviews === 'object') {
149
- const pendingDocs: string[] = [];
150
- for (const [docId, review] of Object.entries<any>(reviews)) {
157
+ const pendingArtifacts: string[] = [];
158
+ for (const [artifactId, review] of Object.entries<any>(reviews)) {
151
159
  if (review.status === 'pending' && review.submittedAt) {
152
160
  const elapsed = minutesSince(review.submittedAt);
153
161
  if (elapsed >= REVIEW_PENDING_TIMEOUT_MINUTES) {
154
- pendingDocs.push(docId);
162
+ pendingArtifacts.push(artifactId);
155
163
  }
156
164
  }
157
165
  }
158
- if (pendingDocs.length > 0) {
166
+ if (pendingArtifacts.length > 0) {
159
167
  reminders.push(
160
- \`- [\${proj.name}] \${pendingDocs.length} 份文档待审核超过 \${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟: \${pendingDocs.join(', ')} — 请尽快处理(doc_approve / reject_doc)\`,
168
+ \`- [\${proj.name}] \${pendingArtifacts.length} 份产出待审核超过 \${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟: \${pendingArtifacts.join(', ')} — 请尽快处理(artifact_approve)\`,
161
169
  );
162
170
  }
163
171
  }
164
172
 
165
- // Check 3: Phase idle check
173
+ // Check 3: Workflow idle check
166
174
  const state = readJsonSafe(resolve(proj.path, 'state.json'));
167
- if (state && state.updatedAt && state.currentPhase) {
175
+ if (state && state.updatedAt) {
168
176
  const idle = minutesSince(state.updatedAt);
169
- if (idle >= PHASE_IDLE_TIMEOUT_MINUTES) {
177
+ if (idle >= WORKFLOW_IDLE_TIMEOUT_MINUTES) {
170
178
  reminders.push(
171
- \`- [\${proj.name}] 当前阶段 (\${state.currentPhase}) 已空闲 \${idle} 分钟,建议调用 project_status 检查项目状态\`,
179
+ \`- [\${proj.name}] 工作流已空闲 \${idle} 分钟,建议调用 project_status 检查项目状态\`,
172
180
  );
173
181
  }
174
182
  }
@@ -204,16 +212,19 @@ export default async function handler(event: any) {
204
212
  }
205
213
  `;
206
214
  }
215
+
207
216
  /**
208
- * Create OpenClaw hook definitions using agent-kit's defineHooks.
217
+ * Create OpenClaw hook definitions using defineHooks from context.
209
218
  *
210
219
  * Single definition with two events (OpenClaw only supports one HookDefinition).
220
+ *
221
+ * @param {Function} defineHooks - defineHooks function from agent-kit (passed via context)
222
+ * @param {{ dataDir: string }} params - Parameters to bake into hook content
211
223
  */
212
- export function createOpenClawHooks(params) {
224
+ export function createOpenClawHooks(defineHooks, params) {
213
225
  return defineHooks('openclaw', {
214
226
  events: ['message_received', 'before_tool_call'],
215
227
  content: generateOpenClawHandler(params),
216
- description: 'Harmonia PM 边界守卫与状态提醒',
228
+ description: 'Harmonia coordinator 边界守卫与状态提醒',
217
229
  });
218
230
  }
219
- //# sourceMappingURL=openclaw.js.map
@@ -13,14 +13,21 @@
13
13
  * the operation (e.g., prepend echo to bash commands, empty write content)
14
14
  * and inject a warning message.
15
15
  * 2. experimental.chat.messages.transform — read Harmonia data files, inject
16
- * reminders about dispatch timeouts, idle phases, pending reviews.
16
+ * reminders about dispatch timeouts, idle timeouts, pending reviews.
17
17
  *
18
18
  * Project-agnostic: no project name/dir baked in.
19
19
  * - Boundary guard uses tool names + code file extensions only
20
20
  * - Reminders scan all projects under DATA_DIR
21
21
  */
22
- import { defineHooks } from '@s_s/agent-kit';
23
- import { BLOCKED_COMMANDS, CODE_EXTENSIONS, DISPATCH_TIMEOUT_MINUTES, PHASE_IDLE_TIMEOUT_MINUTES, REVIEW_PENDING_TIMEOUT_MINUTES, } from './content.js';
22
+
23
+ import {
24
+ BLOCKED_COMMANDS,
25
+ CODE_EXTENSIONS,
26
+ DISPATCH_TIMEOUT_MINUTES,
27
+ WORKFLOW_IDLE_TIMEOUT_MINUTES,
28
+ REVIEW_PENDING_TIMEOUT_MINUTES,
29
+ } from './content.js';
30
+
24
31
  /**
25
32
  * Generate the OpenCode plugin TypeScript source code.
26
33
  *
@@ -31,6 +38,7 @@ import { BLOCKED_COMMANDS, CODE_EXTENSIONS, DISPATCH_TIMEOUT_MINUTES, PHASE_IDLE
31
38
  function generateOpenCodePlugin(params) {
32
39
  const codeExtsJson = JSON.stringify(CODE_EXTENSIONS);
33
40
  const blockedCmdsJson = JSON.stringify(BLOCKED_COMMANDS);
41
+
34
42
  // We generate raw TS source as a string. All values from params/constants
35
43
  // are baked in as literals at generation time.
36
44
  return `import type { Plugin } from 'opencode';
@@ -43,7 +51,7 @@ const DATA_DIR = ${JSON.stringify(params.dataDir)};
43
51
  const CODE_EXTENSIONS: readonly string[] = ${codeExtsJson};
44
52
  const BLOCKED_COMMANDS: readonly string[] = ${blockedCmdsJson};
45
53
  const DISPATCH_TIMEOUT_MINUTES = ${DISPATCH_TIMEOUT_MINUTES};
46
- const PHASE_IDLE_TIMEOUT_MINUTES = ${PHASE_IDLE_TIMEOUT_MINUTES};
54
+ const WORKFLOW_IDLE_TIMEOUT_MINUTES = ${WORKFLOW_IDLE_TIMEOUT_MINUTES};
47
55
  const REVIEW_PENDING_TIMEOUT_MINUTES = ${REVIEW_PENDING_TIMEOUT_MINUTES};
48
56
 
49
57
  // ── Helpers ──
@@ -112,7 +120,7 @@ export default {
112
120
  // Neutralize: replace content with warning
113
121
  output.args = {
114
122
  ...args,
115
- content: \`[HARMONIA 拦截] PM 不应直接修改代码文件。请通过 role_dispatch 将编码任务分配给 developer。\\n原目标文件: \${filePath}\`,
123
+ content: \`[HARMONIA 拦截] Coordinator 不应直接修改代码文件。请通过 role_dispatch 将编码任务分配给 developer。\\n原目标文件: \${filePath}\`,
116
124
  oldString: '__HARMONIA_BLOCKED_EDIT__',
117
125
  newString: '__HARMONIA_BLOCKED_EDIT__',
118
126
  };
@@ -128,8 +136,8 @@ export default {
128
136
  if (blocked) {
129
137
  output.args = {
130
138
  ...args,
131
- command: \`echo "[HARMONIA 拦截] PM 不应直接执行开发命令 (\${blocked}...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。"\`,
132
- cmd: \`echo "[HARMONIA 拦截] PM 不应直接执行开发命令 (\${blocked}...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。"\`,
139
+ command: \`echo "[HARMONIA 拦截] Coordinator 不应直接执行开发命令 (\${blocked}...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。"\`,
140
+ cmd: \`echo "[HARMONIA 拦截] Coordinator 不应直接执行开发命令 (\${blocked}...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。"\`,
133
141
  };
134
142
  return;
135
143
  }
@@ -160,32 +168,32 @@ export default {
160
168
  }
161
169
  }
162
170
 
163
- // Check 2: Pending document reviews
171
+ // Check 2: Pending artifact reviews
164
172
  const reviews = readJsonSafe(resolve(proj.path, 'reviews.json'));
165
173
  if (reviews && typeof reviews === 'object') {
166
- const pendingDocs: string[] = [];
167
- for (const [docId, review] of Object.entries<any>(reviews)) {
174
+ const pendingArtifacts: string[] = [];
175
+ for (const [artifactId, review] of Object.entries<any>(reviews)) {
168
176
  if (review.status === 'pending' && review.submittedAt) {
169
177
  const elapsed = minutesSince(review.submittedAt);
170
178
  if (elapsed >= REVIEW_PENDING_TIMEOUT_MINUTES) {
171
- pendingDocs.push(docId);
179
+ pendingArtifacts.push(artifactId);
172
180
  }
173
181
  }
174
182
  }
175
- if (pendingDocs.length > 0) {
183
+ if (pendingArtifacts.length > 0) {
176
184
  reminders.push(
177
- \`- [\${proj.name}] \${pendingDocs.length} 份文档待审核超过 \${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟: \${pendingDocs.join(', ')} — 请尽快处理(doc_approve / reject_doc)\`,
185
+ \`- [\${proj.name}] \${pendingArtifacts.length} 份产出待审核超过 \${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟: \${pendingArtifacts.join(', ')} — 请尽快处理(artifact_approve)\`,
178
186
  );
179
187
  }
180
188
  }
181
189
 
182
- // Check 3: Phase idle check
190
+ // Check 3: Workflow idle check
183
191
  const state = readJsonSafe(resolve(proj.path, 'state.json'));
184
- if (state && state.updatedAt && state.currentPhase) {
192
+ if (state && state.updatedAt) {
185
193
  const idle = minutesSince(state.updatedAt);
186
- if (idle >= PHASE_IDLE_TIMEOUT_MINUTES) {
194
+ if (idle >= WORKFLOW_IDLE_TIMEOUT_MINUTES) {
187
195
  reminders.push(
188
- \`- [\${proj.name}] 当前阶段 (\${state.currentPhase}) 已空闲 \${idle} 分钟,建议调用 project_status 检查项目状态\`,
196
+ \`- [\${proj.name}] 工作流已空闲 \${idle} 分钟,建议调用 project_status 检查项目状态\`,
189
197
  );
190
198
  }
191
199
  }
@@ -211,16 +219,19 @@ export default {
211
219
  } satisfies Plugin;
212
220
  `;
213
221
  }
222
+
214
223
  /**
215
- * Create OpenCode hook definitions using agent-kit's defineHooks.
224
+ * Create OpenCode hook definitions using defineHooks from context.
216
225
  *
217
226
  * Produces a single plugin file with both tool.execute.before and
218
227
  * experimental.chat.messages.transform hooks.
228
+ *
229
+ * @param {Function} defineHooks - defineHooks function from agent-kit (passed via context)
230
+ * @param {{ dataDir: string }} params - Parameters to bake into hook content
219
231
  */
220
- export function createOpenCodeHooks(params) {
232
+ export function createOpenCodeHooks(defineHooks, params) {
221
233
  return defineHooks('opencode', {
222
234
  events: ['tool.execute.before', 'experimental.chat.messages.transform'],
223
235
  content: generateOpenCodePlugin(params),
224
236
  });
225
237
  }
226
- //# sourceMappingURL=opencode.js.map