@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.
- package/README.md +140 -392
- package/build/cli/setup.d.ts +4 -2
- package/build/cli/setup.js +44 -18
- package/build/cli/setup.js.map +1 -1
- package/build/core/action-registry.d.ts +36 -0
- package/build/core/action-registry.js +53 -0
- package/build/core/action-registry.js.map +1 -0
- package/build/core/artifacts.d.ts +66 -0
- package/build/core/artifacts.js +178 -0
- package/build/core/artifacts.js.map +1 -0
- package/build/core/dispatch.d.ts +18 -11
- package/build/core/dispatch.js +43 -33
- package/build/core/dispatch.js.map +1 -1
- package/build/core/issues.d.ts +37 -0
- package/build/core/issues.js +100 -0
- package/build/core/issues.js.map +1 -0
- package/build/core/overrides.d.ts +19 -26
- package/build/core/overrides.js +32 -98
- package/build/core/overrides.js.map +1 -1
- package/build/core/plugin.d.ts +86 -0
- package/build/core/plugin.js +332 -0
- package/build/core/plugin.js.map +1 -0
- package/build/core/registry.d.ts +36 -3
- package/build/core/registry.js +63 -5
- package/build/core/registry.js.map +1 -1
- package/build/core/reviews.d.ts +13 -13
- package/build/core/reviews.js +31 -32
- package/build/core/reviews.js.map +1 -1
- package/build/core/schema.d.ts +43 -15
- package/build/core/schema.js +124 -20
- package/build/core/schema.js.map +1 -1
- package/build/core/state.d.ts +29 -22
- package/build/core/state.js +49 -81
- package/build/core/state.js.map +1 -1
- package/build/core/steps.d.ts +15 -15
- package/build/core/steps.js +32 -33
- package/build/core/steps.js.map +1 -1
- package/build/core/tree-utils.d.ts +52 -0
- package/build/core/tree-utils.js +226 -0
- package/build/core/tree-utils.js.map +1 -0
- package/build/core/types.d.ts +417 -117
- package/build/core/types.js +15 -1
- package/build/core/types.js.map +1 -1
- package/build/core/workflow-engine.d.ts +68 -0
- package/build/core/workflow-engine.js +821 -0
- package/build/core/workflow-engine.js.map +1 -0
- package/build/core/workflow-validator.d.ts +22 -0
- package/build/core/workflow-validator.js +489 -0
- package/build/core/workflow-validator.js.map +1 -0
- package/build/index.js +28 -25
- package/build/index.js.map +1 -1
- package/build/setup/inject.d.ts +4 -4
- package/build/setup/inject.js +6 -6
- package/build/setup/inject.js.map +1 -1
- package/build/setup/templates.d.ts +9 -7
- package/build/setup/templates.js +68 -103
- package/build/setup/templates.js.map +1 -1
- package/build/tools/artifact-approve.d.ts +8 -0
- package/build/tools/artifact-approve.js +94 -0
- package/build/tools/artifact-approve.js.map +1 -0
- package/build/tools/artifact-schema.d.ts +12 -0
- package/build/tools/artifact-schema.js +148 -0
- package/build/tools/artifact-schema.js.map +1 -0
- package/build/tools/artifact-tools.d.ts +18 -0
- package/build/tools/artifact-tools.js +465 -0
- package/build/tools/artifact-tools.js.map +1 -0
- package/build/tools/{report-dispatch.d.ts → dispatch-report.d.ts} +7 -3
- package/build/tools/dispatch-report.js +261 -0
- package/build/tools/dispatch-report.js.map +1 -0
- package/build/tools/engine-helpers.d.ts +41 -0
- package/build/tools/engine-helpers.js +182 -0
- package/build/tools/engine-helpers.js.map +1 -0
- package/build/tools/get-project-status.d.ts +6 -4
- package/build/tools/get-project-status.js +308 -246
- package/build/tools/get-project-status.js.map +1 -1
- package/build/tools/get-role-prompt.d.ts +1 -1
- package/build/tools/get-role-prompt.js +7 -41
- package/build/tools/get-role-prompt.js.map +1 -1
- package/build/tools/issue-tools.d.ts +10 -0
- package/build/tools/issue-tools.js +169 -0
- package/build/tools/issue-tools.js.map +1 -0
- package/build/tools/iteration-start.d.ts +7 -4
- package/build/tools/iteration-start.js +51 -20
- package/build/tools/iteration-start.js.map +1 -1
- package/build/tools/loop-done.d.ts +11 -0
- package/build/tools/loop-done.js +109 -0
- package/build/tools/loop-done.js.map +1 -0
- package/build/tools/patch-start.d.ts +16 -0
- package/build/tools/patch-start.js +122 -0
- package/build/tools/patch-start.js.map +1 -0
- package/build/tools/project-init.d.ts +5 -5
- package/build/tools/project-init.js +47 -18
- package/build/tools/project-init.js.map +1 -1
- package/build/tools/role-dispatch.d.ts +55 -0
- package/build/tools/role-dispatch.js +508 -0
- package/build/tools/role-dispatch.js.map +1 -0
- package/build/tools/utils.d.ts +40 -0
- package/build/tools/utils.js +97 -0
- package/build/tools/utils.js.map +1 -0
- package/package.json +1 -1
- package/{build/hooks/claude-code.js → workflows/dev/hooks/claude.js} +34 -23
- package/{build → workflows/dev}/hooks/content.js +27 -18
- package/workflows/dev/hooks/index.js +52 -0
- package/{build → workflows/dev}/hooks/openclaw.js +31 -20
- package/{build → workflows/dev}/hooks/opencode.js +31 -20
- package/workflows/dev/roles/architect.md +68 -28
- package/workflows/dev/roles/coordinator.md +103 -0
- package/workflows/dev/roles/developer.md +5 -5
- package/workflows/dev/roles/tester.md +19 -19
- package/workflows/dev/schemas/api-contract.json +42 -0
- package/workflows/dev/schemas/api-design.json +30 -13
- package/workflows/dev/schemas/data-model.json +20 -7
- package/workflows/dev/schemas/prd.completeness-check.json +6 -5
- package/workflows/dev/schemas/prd.draft.json +13 -5
- package/workflows/dev/schemas/prd.final.json +34 -11
- package/workflows/dev/schemas/prd.json +29 -11
- package/workflows/dev/schemas/prd.requirements.json +6 -5
- package/workflows/dev/schemas/prototype.json +6 -2
- package/workflows/dev/schemas/task-breakdown.coarse.json +4 -3
- package/workflows/dev/schemas/task-breakdown.dependencies.json +5 -4
- package/workflows/dev/schemas/task-breakdown.detailed.json +8 -3
- package/workflows/dev/schemas/task-breakdown.final.json +8 -3
- package/workflows/dev/schemas/task-breakdown.json +8 -3
- package/workflows/dev/schemas/tech-design.analysis.json +6 -5
- package/workflows/dev/schemas/tech-design.draft.json +14 -5
- package/workflows/dev/schemas/tech-design.final.json +39 -13
- package/workflows/dev/schemas/tech-design.json +34 -13
- package/workflows/dev/schemas/tech-design.research.json +21 -0
- package/workflows/dev/schemas/test-plan.json +17 -7
- package/workflows/dev/schemas/test-report.json +26 -9
- package/workflows/dev/schemas/user-stories.json +7 -3
- package/workflows/dev/tools/index.js +23 -0
- package/workflows/dev/workflow.json +234 -101
- package/build/core/docs.d.ts +0 -32
- package/build/core/docs.js +0 -91
- package/build/core/docs.js.map +0 -1
- package/build/core/workflow.d.ts +0 -33
- package/build/core/workflow.js +0 -140
- package/build/core/workflow.js.map +0 -1
- package/build/hooks/claude-code.d.ts +0 -20
- package/build/hooks/claude-code.js.map +0 -1
- package/build/hooks/content.d.ts +0 -43
- package/build/hooks/content.js.map +0 -1
- package/build/hooks/install.d.ts +0 -40
- package/build/hooks/install.js +0 -63
- package/build/hooks/install.js.map +0 -1
- package/build/hooks/openclaw.d.ts +0 -24
- package/build/hooks/openclaw.js.map +0 -1
- package/build/hooks/opencode.d.ts +0 -29
- package/build/hooks/opencode.js.map +0 -1
- package/build/tools/approve-doc.d.ts +0 -6
- package/build/tools/approve-doc.js +0 -108
- package/build/tools/approve-doc.js.map +0 -1
- package/build/tools/dispatch-role.d.ts +0 -16
- package/build/tools/dispatch-role.js +0 -277
- package/build/tools/dispatch-role.js.map +0 -1
- package/build/tools/doc-tools.d.ts +0 -16
- package/build/tools/doc-tools.js +0 -389
- package/build/tools/doc-tools.js.map +0 -1
- package/build/tools/override-tools.d.ts +0 -6
- package/build/tools/override-tools.js +0 -129
- package/build/tools/override-tools.js.map +0 -1
- package/build/tools/report-dispatch.js +0 -194
- package/build/tools/report-dispatch.js.map +0 -1
- package/build/tools/set-scale.d.ts +0 -6
- package/build/tools/set-scale.js +0 -107
- package/build/tools/set-scale.js.map +0 -1
- package/build/tools/setup-project.d.ts +0 -8
- package/build/tools/setup-project.js +0 -116
- package/build/tools/setup-project.js.map +0 -1
- package/build/tools/update-phase.d.ts +0 -12
- package/build/tools/update-phase.js +0 -159
- package/build/tools/update-phase.js.map +0 -1
- package/workflows/dev/roles/pm.md +0 -99
- package/workflows/dev/schemas/deploy.json +0 -20
- package/workflows/dev/schemas/fsd.json +0 -25
- package/workflows/dev/schemas/project-plan.json +0 -20
- package/workflows/dev/schemas/retrospective.json +0 -20
- package/workflows/dev/schemas/risk-assessment.json +0 -15
- 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
|
@@ -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
|
|
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
|
-
|
|
17
|
-
import {
|
|
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
|
|
35
|
-
# Prevents
|
|
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 "
|
|
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 "
|
|
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
|
|
100
|
-
* - Pending
|
|
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
|
|
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
|
-
|
|
160
|
+
PENDING_ARTIFACTS=""
|
|
152
161
|
|
|
153
|
-
while IFS='|' read -r
|
|
154
|
-
[ -z "$
|
|
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
|
-
|
|
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
|
|
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:
|
|
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" ]
|
|
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 ${
|
|
181
|
-
add_reminder "- [$PROJECT_NAME]
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
-
'
|
|
93
|
+
'iteration_start',
|
|
94
|
+
'patch_start',
|
|
90
95
|
'role_dispatch',
|
|
91
96
|
'dispatch_report',
|
|
92
|
-
'
|
|
93
|
-
'
|
|
94
|
-
'
|
|
95
|
-
'
|
|
96
|
-
'
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
19
|
-
import {
|
|
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
|
|
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
|
|
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: \`
|
|
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: \`
|
|
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
|
|
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
|
|
150
|
-
for (const [
|
|
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
|
-
|
|
162
|
+
pendingArtifacts.push(artifactId);
|
|
155
163
|
}
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
|
-
if (
|
|
166
|
+
if (pendingArtifacts.length > 0) {
|
|
159
167
|
reminders.push(
|
|
160
|
-
\`- [\${proj.name}] \${
|
|
168
|
+
\`- [\${proj.name}] \${pendingArtifacts.length} 份产出待审核超过 \${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟: \${pendingArtifacts.join(', ')} — 请尽快处理(artifact_approve)\`,
|
|
161
169
|
);
|
|
162
170
|
}
|
|
163
171
|
}
|
|
164
172
|
|
|
165
|
-
// Check 3:
|
|
173
|
+
// Check 3: Workflow idle check
|
|
166
174
|
const state = readJsonSafe(resolve(proj.path, 'state.json'));
|
|
167
|
-
if (state && state.updatedAt
|
|
175
|
+
if (state && state.updatedAt) {
|
|
168
176
|
const idle = minutesSince(state.updatedAt);
|
|
169
|
-
if (idle >=
|
|
177
|
+
if (idle >= WORKFLOW_IDLE_TIMEOUT_MINUTES) {
|
|
170
178
|
reminders.push(
|
|
171
|
-
\`- [\${proj.name}]
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
23
|
-
import {
|
|
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
|
|
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 拦截]
|
|
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 拦截]
|
|
132
|
-
cmd: \`echo "[HARMONIA 拦截]
|
|
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
|
|
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
|
|
167
|
-
for (const [
|
|
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
|
-
|
|
179
|
+
pendingArtifacts.push(artifactId);
|
|
172
180
|
}
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
|
-
if (
|
|
183
|
+
if (pendingArtifacts.length > 0) {
|
|
176
184
|
reminders.push(
|
|
177
|
-
\`- [\${proj.name}] \${
|
|
185
|
+
\`- [\${proj.name}] \${pendingArtifacts.length} 份产出待审核超过 \${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟: \${pendingArtifacts.join(', ')} — 请尽快处理(artifact_approve)\`,
|
|
178
186
|
);
|
|
179
187
|
}
|
|
180
188
|
}
|
|
181
189
|
|
|
182
|
-
// Check 3:
|
|
190
|
+
// Check 3: Workflow idle check
|
|
183
191
|
const state = readJsonSafe(resolve(proj.path, 'state.json'));
|
|
184
|
-
if (state && state.updatedAt
|
|
192
|
+
if (state && state.updatedAt) {
|
|
185
193
|
const idle = minutesSince(state.updatedAt);
|
|
186
|
-
if (idle >=
|
|
194
|
+
if (idle >= WORKFLOW_IDLE_TIMEOUT_MINUTES) {
|
|
187
195
|
reminders.push(
|
|
188
|
-
\`- [\${proj.name}]
|
|
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
|
|
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
|