@s_s/harmonia 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +396 -2
  2. package/build/cli/setup.d.ts +21 -0
  3. package/build/cli/setup.js +72 -0
  4. package/build/cli/setup.js.map +1 -0
  5. package/build/core/dispatch.d.ts +10 -0
  6. package/build/core/dispatch.js +21 -0
  7. package/build/core/dispatch.js.map +1 -1
  8. package/build/core/docs.d.ts +13 -0
  9. package/build/core/docs.js +32 -0
  10. package/build/core/docs.js.map +1 -1
  11. package/build/core/registry.d.ts +1 -1
  12. package/build/core/registry.js +5 -16
  13. package/build/core/registry.js.map +1 -1
  14. package/build/core/schema.d.ts +38 -0
  15. package/build/core/schema.js +187 -0
  16. package/build/core/schema.js.map +1 -0
  17. package/build/core/state.d.ts +11 -1
  18. package/build/core/state.js +23 -2
  19. package/build/core/state.js.map +1 -1
  20. package/build/core/steps.d.ts +34 -0
  21. package/build/core/steps.js +113 -0
  22. package/build/core/steps.js.map +1 -0
  23. package/build/core/types.d.ts +81 -4
  24. package/build/core/workflow.d.ts +26 -6
  25. package/build/core/workflow.js +88 -11
  26. package/build/core/workflow.js.map +1 -1
  27. package/build/hooks/claude-code.d.ts +20 -0
  28. package/build/hooks/claude-code.js +218 -0
  29. package/build/hooks/claude-code.js.map +1 -0
  30. package/build/hooks/content.d.ts +43 -0
  31. package/build/hooks/content.js +109 -0
  32. package/build/hooks/content.js.map +1 -0
  33. package/build/hooks/install.d.ts +40 -0
  34. package/build/hooks/install.js +63 -0
  35. package/build/hooks/install.js.map +1 -0
  36. package/build/hooks/openclaw.d.ts +24 -0
  37. package/build/hooks/openclaw.js +219 -0
  38. package/build/hooks/openclaw.js.map +1 -0
  39. package/build/hooks/opencode.d.ts +29 -0
  40. package/build/hooks/opencode.js +226 -0
  41. package/build/hooks/opencode.js.map +1 -0
  42. package/build/index.d.ts +4 -7
  43. package/build/index.js +80 -42
  44. package/build/index.js.map +1 -1
  45. package/build/setup/inject.d.ts +13 -17
  46. package/build/setup/inject.js +34 -92
  47. package/build/setup/inject.js.map +1 -1
  48. package/build/setup/templates.d.ts +12 -16
  49. package/build/setup/templates.js +52 -69
  50. package/build/setup/templates.js.map +1 -1
  51. package/build/tools/approve-doc.d.ts +1 -1
  52. package/build/tools/approve-doc.js +4 -4
  53. package/build/tools/approve-doc.js.map +1 -1
  54. package/build/tools/dispatch-role.d.ts +2 -2
  55. package/build/tools/dispatch-role.js +41 -11
  56. package/build/tools/dispatch-role.js.map +1 -1
  57. package/build/tools/doc-tools.d.ts +11 -3
  58. package/build/tools/doc-tools.js +257 -13
  59. package/build/tools/doc-tools.js.map +1 -1
  60. package/build/tools/get-project-status.d.ts +4 -2
  61. package/build/tools/get-project-status.js +165 -50
  62. package/build/tools/get-project-status.js.map +1 -1
  63. package/build/tools/get-role-prompt.d.ts +2 -2
  64. package/build/tools/get-role-prompt.js +4 -4
  65. package/build/tools/get-role-prompt.js.map +1 -1
  66. package/build/tools/override-tools.d.ts +1 -1
  67. package/build/tools/override-tools.js +4 -4
  68. package/build/tools/override-tools.js.map +1 -1
  69. package/build/tools/project-init.d.ts +5 -1
  70. package/build/tools/project-init.js +92 -32
  71. package/build/tools/project-init.js.map +1 -1
  72. package/build/tools/report-dispatch.d.ts +6 -3
  73. package/build/tools/report-dispatch.js +45 -8
  74. package/build/tools/report-dispatch.js.map +1 -1
  75. package/build/tools/set-scale.d.ts +6 -0
  76. package/build/tools/set-scale.js +92 -0
  77. package/build/tools/set-scale.js.map +1 -0
  78. package/build/tools/setup-project.d.ts +1 -1
  79. package/build/tools/setup-project.js +33 -5
  80. package/build/tools/setup-project.js.map +1 -1
  81. package/build/tools/update-phase.d.ts +8 -3
  82. package/build/tools/update-phase.js +85 -20
  83. package/build/tools/update-phase.js.map +1 -1
  84. package/package.json +2 -1
  85. package/workflows/dev/roles/architect.md +1 -1
  86. package/workflows/dev/roles/pm.md +5 -5
  87. package/workflows/dev/roles/tester.md +1 -1
  88. package/workflows/dev/schemas/api-design.json +25 -0
  89. package/workflows/dev/schemas/data-model.json +20 -0
  90. package/workflows/dev/schemas/deploy.json +20 -0
  91. package/workflows/dev/schemas/fsd.json +25 -0
  92. package/workflows/dev/schemas/prd.completeness-check.json +24 -0
  93. package/workflows/dev/schemas/prd.draft.json +15 -0
  94. package/workflows/dev/schemas/prd.final.json +30 -0
  95. package/workflows/dev/schemas/prd.json +30 -0
  96. package/workflows/dev/schemas/prd.requirements.json +25 -0
  97. package/workflows/dev/schemas/project-plan.json +20 -0
  98. package/workflows/dev/schemas/prototype.json +4 -0
  99. package/workflows/dev/schemas/retrospective.json +20 -0
  100. package/workflows/dev/schemas/risk-assessment.json +15 -0
  101. package/workflows/dev/schemas/task-breakdown.coarse.json +15 -0
  102. package/workflows/dev/schemas/task-breakdown.dependencies.json +20 -0
  103. package/workflows/dev/schemas/task-breakdown.detailed.json +10 -0
  104. package/workflows/dev/schemas/task-breakdown.final.json +10 -0
  105. package/workflows/dev/schemas/task-breakdown.json +10 -0
  106. package/workflows/dev/schemas/tech-design.analysis.json +25 -0
  107. package/workflows/dev/schemas/tech-design.api-contract.json +20 -0
  108. package/workflows/dev/schemas/tech-design.draft.json +15 -0
  109. package/workflows/dev/schemas/tech-design.final.json +30 -0
  110. package/workflows/dev/schemas/tech-design.json +30 -0
  111. package/workflows/dev/schemas/test-plan.json +20 -0
  112. package/workflows/dev/schemas/test-report.json +25 -0
  113. package/workflows/dev/schemas/user-stories.json +10 -0
  114. package/workflows/dev/workflow.json +85 -5
@@ -11,6 +11,17 @@ export interface PhaseDefinition {
11
11
  description: string;
12
12
  }
13
13
  export type DocScale = 'full' | 'lite' | 'skip' | 'optional';
14
+ /** Step definition within a doc (for sequential mode) */
15
+ export interface DocStepDefinition {
16
+ /** Step ID, e.g. "requirements", "draft", "final" */
17
+ id: string;
18
+ /** Human-readable name */
19
+ name: string;
20
+ /** Output format for this step's artifact */
21
+ format: 'json' | 'md';
22
+ /** Description shown to agent */
23
+ description: string;
24
+ }
14
25
  export interface DocDefinition {
15
26
  name: string;
16
27
  scale: Record<ProjectScale, DocScale>;
@@ -20,6 +31,8 @@ export interface DocDefinition {
20
31
  review?: boolean;
21
32
  /** External output — not managed by write_doc (e.g. code written directly to project dir) */
22
33
  external?: boolean;
34
+ /** Sequential steps — when defined and scale >= medium, write_doc requires step parameter */
35
+ steps?: DocStepDefinition[];
23
36
  }
24
37
  export interface ScaleDimension {
25
38
  small: string | number | boolean;
@@ -29,6 +42,8 @@ export interface ScaleDimension {
29
42
  export interface WorkflowDefinition {
30
43
  name: string;
31
44
  description: string;
45
+ version?: string;
46
+ author?: string;
32
47
  phases: PhaseDefinition[];
33
48
  docs: Record<string, DocDefinition>;
34
49
  scale_criteria: {
@@ -36,6 +51,47 @@ export interface WorkflowDefinition {
36
51
  dimensions: Record<string, ScaleDimension>;
37
52
  };
38
53
  }
54
+ /**
55
+ * A required section (heading) in a markdown document.
56
+ * Validation checks that the document contains a heading matching the primary
57
+ * pattern or one of the aliases.
58
+ */
59
+ export interface DocSchemaSection {
60
+ /** Primary heading text, e.g. "## 项目概述" */
61
+ heading: string;
62
+ /** Per-scale requirement: true = required, false = optional */
63
+ required: Record<ProjectScale, boolean>;
64
+ /** Alternative heading texts that satisfy this requirement */
65
+ aliases?: string[];
66
+ }
67
+ /**
68
+ * Schema definition for a document type.
69
+ * Used by write_doc to validate content structure before writing.
70
+ */
71
+ export interface DocSchema {
72
+ /** Required sections for markdown documents */
73
+ sections?: DocSchemaSection[];
74
+ /** Required HTML tags for html-format documents (e.g. ["html", "body", "nav"]) */
75
+ htmlTags?: string[];
76
+ /** Required top-level JSON fields (for JSON step artifacts) */
77
+ jsonFields?: DocSchemaJsonField[];
78
+ /** Minimum content length in characters (optional) */
79
+ minLength?: number;
80
+ }
81
+ /**
82
+ * A required top-level field in a JSON document.
83
+ * Used for validating structured step artifacts.
84
+ */
85
+ export interface DocSchemaJsonField {
86
+ /** Field name (top-level key in JSON) */
87
+ field: string;
88
+ /** Per-scale requirement: true = required, false = optional */
89
+ required: Record<ProjectScale, boolean>;
90
+ /** Expected type: "string", "array", "object", "number", "boolean" */
91
+ type?: string;
92
+ /** If type is "array", minimum number of items */
93
+ minItems?: number;
94
+ }
39
95
  export interface RoleCapability {
40
96
  /** Unique ID for this capability */
41
97
  id: string;
@@ -76,8 +132,8 @@ export interface ProjectState {
76
132
  projectDir: string;
77
133
  /** Workflow name, e.g. "dev" */
78
134
  workflow: string;
79
- /** Project scale determined by PM */
80
- scale: ProjectScale;
135
+ /** Project scale determined by PM (null until set via project_set_scale) */
136
+ scale: ProjectScale | null;
81
137
  /** Current phase id */
82
138
  currentPhase: string;
83
139
  /** State of each phase */
@@ -150,8 +206,9 @@ export interface CapabilityOverride {
150
206
  /** Additional notes for prompt generation (rarely needed) */
151
207
  notes?: string;
152
208
  }
153
- /** Agent type for spawning team member agents */
154
- export type AgentType = 'opencode' | 'openclaw' | 'claude-code' | 'codex';
209
+ /** Agent type for spawning team member agents (re-exported from @s_s/agent-kit) */
210
+ import type { AgentType } from '@s_s/agent-kit';
211
+ export type { AgentType };
155
212
  /** Per-role override configuration */
156
213
  export interface RoleOverride {
157
214
  /** Agent type to use for this role */
@@ -179,3 +236,23 @@ export interface OverrideConfig {
179
236
  /** Role overrides (agent, model, capabilities) */
180
237
  roles?: Record<string, RoleOverride>;
181
238
  }
239
+ /** Step completion record */
240
+ export interface DocStepRecord {
241
+ /** Step ID */
242
+ stepId: string;
243
+ /** When this step was completed */
244
+ completedAt: string;
245
+ /** File path of the step artifact (relative to project data dir) */
246
+ artifactPath: string;
247
+ }
248
+ /** Per-doc step tracking state */
249
+ export interface DocStepState {
250
+ /** Doc ID */
251
+ docId: string;
252
+ /** Completed steps (in order) */
253
+ completedSteps: DocStepRecord[];
254
+ /** Whether the final doc has been written (last step completed + merged) */
255
+ finalized: boolean;
256
+ /** Finalized at timestamp */
257
+ finalizedAt?: string;
258
+ }
@@ -1,13 +1,33 @@
1
1
  /**
2
- * Workflow loader — reads workflow definitions and role prompts from the
3
- * workflows/ directory bundled with Harmonia.
2
+ * Workflow loader — two-layer resolution for workflow definitions.
3
+ *
4
+ * Lookup priority:
5
+ * 1. <data_dir>/.workflows/<name>/ (user custom — can override built-in)
6
+ * 2. <package>/workflows/<name>/ (built-in fallback)
4
7
  */
5
8
  import type { LoadedWorkflow } from './types.js';
9
+ export declare class WorkflowNotFoundError extends Error {
10
+ constructor(name: string, searched: string[]);
11
+ }
6
12
  /**
7
- * Load a single workflow by name from the given workflows root directory.
13
+ * Resolve the actual directory for a workflow name.
14
+ * Custom dir takes priority over built-in dir.
15
+ * Returns the resolved directory path.
8
16
  */
9
- export declare function loadWorkflow(workflowsDir: string, name: string): Promise<LoadedWorkflow>;
17
+ export declare function resolveWorkflowDir(builtinDir: string, customDir: string, name: string): Promise<string>;
10
18
  /**
11
- * List all available workflow names.
19
+ * Load a single workflow by name using two-layer resolution.
20
+ *
21
+ * @param builtinDir - Package built-in workflows directory
22
+ * @param customDir - User custom workflows directory (<data_dir>/.workflows)
23
+ * @param name - Workflow name (directory name)
12
24
  */
13
- export declare function listWorkflows(workflowsDir: string): Promise<string[]>;
25
+ export declare function loadWorkflow(builtinDir: string, customDir: string, name: string): Promise<LoadedWorkflow>;
26
+ /**
27
+ * List all available workflow names, merging custom and built-in.
28
+ * Custom workflows override built-in ones with the same name.
29
+ *
30
+ * @param builtinDir - Package built-in workflows directory
31
+ * @param customDir - User custom workflows directory (<data_dir>/.workflows)
32
+ */
33
+ export declare function listWorkflows(builtinDir: string, customDir: string): Promise<string[]>;
@@ -1,10 +1,50 @@
1
1
  /**
2
- * Workflow loader — reads workflow definitions and role prompts from the
3
- * workflows/ directory bundled with Harmonia.
2
+ * Workflow loader — two-layer resolution for workflow definitions.
3
+ *
4
+ * Lookup priority:
5
+ * 1. <data_dir>/.workflows/<name>/ (user custom — can override built-in)
6
+ * 2. <package>/workflows/<name>/ (built-in fallback)
4
7
  */
5
- import { readFile, readdir } from 'node:fs/promises';
8
+ import { readFile, readdir, access } from 'node:fs/promises';
6
9
  import { join, parse } from 'node:path';
7
10
  import YAML from 'yaml';
11
+ // ─── Errors ───
12
+ export class WorkflowNotFoundError extends Error {
13
+ constructor(name, searched) {
14
+ const dirs = searched.map((d) => ` - ${d}`).join('\n');
15
+ super(`工作流 "${name}" 不存在。已搜索:\n${dirs}`);
16
+ this.name = 'WorkflowNotFoundError';
17
+ }
18
+ }
19
+ // ─── Internal helpers ───
20
+ /**
21
+ * Check if a file exists (async, no throw).
22
+ */
23
+ async function fileExists(path) {
24
+ try {
25
+ await access(path);
26
+ return true;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ /**
33
+ * Resolve the actual directory for a workflow name.
34
+ * Custom dir takes priority over built-in dir.
35
+ * Returns the resolved directory path.
36
+ */
37
+ export async function resolveWorkflowDir(builtinDir, customDir, name) {
38
+ const customPath = join(customDir, name, 'workflow.json');
39
+ if (await fileExists(customPath)) {
40
+ return join(customDir, name);
41
+ }
42
+ const builtinPath = join(builtinDir, name, 'workflow.json');
43
+ if (await fileExists(builtinPath)) {
44
+ return join(builtinDir, name);
45
+ }
46
+ throw new WorkflowNotFoundError(name, [join(customDir, name), join(builtinDir, name)]);
47
+ }
8
48
  /**
9
49
  * Parse a role markdown file.
10
50
  * Format: YAML frontmatter (---\n...\n---) followed by markdown prompt.
@@ -32,17 +72,28 @@ function parseRoleFile(id, content) {
32
72
  };
33
73
  return { id, frontmatter, prompt };
34
74
  }
75
+ // ─── Public API ───
35
76
  /**
36
- * Load a single workflow by name from the given workflows root directory.
77
+ * Load a single workflow by name using two-layer resolution.
78
+ *
79
+ * @param builtinDir - Package built-in workflows directory
80
+ * @param customDir - User custom workflows directory (<data_dir>/.workflows)
81
+ * @param name - Workflow name (directory name)
37
82
  */
38
- export async function loadWorkflow(workflowsDir, name) {
39
- const workflowDir = join(workflowsDir, name);
83
+ export async function loadWorkflow(builtinDir, customDir, name) {
84
+ const workflowDir = await resolveWorkflowDir(builtinDir, customDir, name);
40
85
  // Load workflow.json
41
86
  const workflowJson = await readFile(join(workflowDir, 'workflow.json'), 'utf-8');
42
87
  const definition = JSON.parse(workflowJson);
43
88
  // Load roles
44
89
  const rolesDir = join(workflowDir, 'roles');
45
- const roleFiles = await readdir(rolesDir);
90
+ let roleFiles = [];
91
+ try {
92
+ roleFiles = await readdir(rolesDir);
93
+ }
94
+ catch {
95
+ // roles/ directory is optional for custom workflows
96
+ }
46
97
  const roles = {};
47
98
  for (const file of roleFiles) {
48
99
  if (!file.endsWith('.md'))
@@ -54,10 +105,36 @@ export async function loadWorkflow(workflowsDir, name) {
54
105
  return { definition, roles };
55
106
  }
56
107
  /**
57
- * List all available workflow names.
108
+ * List all available workflow names, merging custom and built-in.
109
+ * Custom workflows override built-in ones with the same name.
110
+ *
111
+ * @param builtinDir - Package built-in workflows directory
112
+ * @param customDir - User custom workflows directory (<data_dir>/.workflows)
58
113
  */
59
- export async function listWorkflows(workflowsDir) {
60
- const entries = await readdir(workflowsDir, { withFileTypes: true });
61
- return entries.filter((e) => e.isDirectory()).map((e) => e.name);
114
+ export async function listWorkflows(builtinDir, customDir) {
115
+ const names = new Set();
116
+ // Built-in workflows
117
+ try {
118
+ const entries = await readdir(builtinDir, { withFileTypes: true });
119
+ for (const e of entries) {
120
+ if (e.isDirectory())
121
+ names.add(e.name);
122
+ }
123
+ }
124
+ catch {
125
+ // built-in dir missing is unexpected but not fatal
126
+ }
127
+ // Custom workflows (can add new or override built-in)
128
+ try {
129
+ const entries = await readdir(customDir, { withFileTypes: true });
130
+ for (const e of entries) {
131
+ if (e.isDirectory())
132
+ names.add(e.name);
133
+ }
134
+ }
135
+ catch {
136
+ // custom dir doesn't exist yet — that's fine
137
+ }
138
+ return [...names].sort();
62
139
  }
63
140
  //# sourceMappingURL=workflow.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/core/workflow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/core/workflow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Claude Code / Codex hook definitions.
3
+ *
4
+ * Claude Code hooks are shell scripts that receive JSON via stdin.
5
+ * They can block tool calls via exit code 2 or JSON { decision: "block" }.
6
+ * UserPromptSubmit stdout is injected as context visible to Claude.
7
+ *
8
+ * Hooks:
9
+ * 1. PreToolUse — boundary guard: block code edits and dev commands
10
+ * 2. UserPromptSubmit — proactive reminders: dispatch timeout, idle phase, pending reviews
11
+ *
12
+ * Project-agnostic: no project name/dir baked in.
13
+ * - Boundary guard uses tool names + code file extensions only
14
+ * - Reminders scan all projects under DATA_DIR
15
+ */
16
+ import type { HookParams } from './content.js';
17
+ /**
18
+ * Create Claude Code hook definitions using agent-kit's defineHooks.
19
+ */
20
+ export declare function createClaudeCodeHooks(params: HookParams): import("@s_s/agent-kit").HookSet<"claude-code">;
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Claude Code / Codex hook definitions.
3
+ *
4
+ * Claude Code hooks are shell scripts that receive JSON via stdin.
5
+ * They can block tool calls via exit code 2 or JSON { decision: "block" }.
6
+ * UserPromptSubmit stdout is injected as context visible to Claude.
7
+ *
8
+ * Hooks:
9
+ * 1. PreToolUse — boundary guard: block code edits and dev commands
10
+ * 2. UserPromptSubmit — proactive reminders: dispatch timeout, idle phase, pending reviews
11
+ *
12
+ * Project-agnostic: no project name/dir baked in.
13
+ * - Boundary guard uses tool names + code file extensions only
14
+ * - Reminders scan all projects under DATA_DIR
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';
18
+ /**
19
+ * Generate Claude Code PreToolUse hook script.
20
+ *
21
+ * Blocks:
22
+ * - Write/Edit tools targeting code files (by extension)
23
+ * - Bash tool running dev commands (npm test, node, etc.)
24
+ *
25
+ * Allows:
26
+ * - All Harmonia MCP tools (they go through MCP, not file tools)
27
+ * - Read-only tools (Read, Glob, Grep, etc.)
28
+ * - Write/Edit to non-code files (AGENTS.md, docs, etc.)
29
+ */
30
+ function generatePreToolUseScript(_params) {
31
+ const codeExtsPattern = CODE_EXTENSIONS.map((e) => `*${e}`).join('|');
32
+ const blockedCmdsChecks = BLOCKED_COMMANDS.map((cmd) => ` *"${cmd}"*) BLOCKED_CMD="${cmd}" ;;`).join('\n');
33
+ return `#!/bin/bash
34
+ # Harmonia PM boundary guard — PreToolUse hook
35
+ # Prevents PM from directly modifying code or running dev commands.
36
+ # Generated by Harmonia setup. Do not edit manually.
37
+
38
+ set -euo pipefail
39
+
40
+ # Read JSON from stdin
41
+ INPUT=$(cat -)
42
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
43
+ TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // empty')
44
+
45
+ # ── Helper: block with reason ──
46
+ block() {
47
+ echo "{\\"decision\\":\\"block\\",\\"reason\\":\\"$1\\"}"
48
+ exit 0
49
+ }
50
+
51
+ # ── Guard 1: Write/Edit tools — check file extension ──
52
+ case "$TOOL_NAME" in
53
+ Write|Edit|MultiEdit|write|edit)
54
+ # Extract file path from tool input
55
+ FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // .filePath // .path // empty')
56
+
57
+ if [ -z "$FILE_PATH" ]; then
58
+ exit 0 # No path to check, let it through
59
+ fi
60
+
61
+ # Block writes to code files (by extension)
62
+ case "$FILE_PATH" in
63
+ ${codeExtsPattern})
64
+ block "PM 不应直接修改代码文件。请通过 role_dispatch 将编码任务分配给 developer。文件: $FILE_PATH"
65
+ ;;
66
+ esac
67
+ ;;
68
+ esac
69
+
70
+ # ── Guard 2: Bash/Terminal — check for dev commands ──
71
+ case "$TOOL_NAME" in
72
+ Bash|bash|Terminal|terminal)
73
+ COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // .cmd // empty')
74
+
75
+ if [ -z "$COMMAND" ]; then
76
+ exit 0
77
+ fi
78
+
79
+ BLOCKED_CMD=""
80
+ case "$COMMAND" in
81
+ ${blockedCmdsChecks}
82
+ esac
83
+
84
+ if [ -n "$BLOCKED_CMD" ]; then
85
+ block "PM 不应直接执行开发命令 ($BLOCKED_CMD...)。请通过 role_dispatch 将任务分配给相应角色(developer/tester)。"
86
+ fi
87
+ ;;
88
+ esac
89
+
90
+ # All other tools — allow
91
+ exit 0
92
+ `;
93
+ }
94
+ /**
95
+ * Generate Claude Code UserPromptSubmit hook script.
96
+ *
97
+ * Scans all projects under DATA_DIR for:
98
+ * - Running dispatches that exceed timeout
99
+ * - Idle phase warning
100
+ * - Pending document reviews
101
+ */
102
+ function generateUserPromptSubmitScript(params) {
103
+ return `#!/bin/bash
104
+ # Harmonia proactive reminder — UserPromptSubmit hook
105
+ # Reads Harmonia data files and injects status reminders as context.
106
+ # Generated by Harmonia setup. Do not edit manually.
107
+
108
+ DATA_DIR="${params.dataDir}"
109
+
110
+ REMINDERS=""
111
+
112
+ add_reminder() {
113
+ if [ -n "$REMINDERS" ]; then
114
+ REMINDERS="$REMINDERS
115
+ $1"
116
+ else
117
+ REMINDERS="$1"
118
+ fi
119
+ }
120
+
121
+ NOW_EPOCH=$(date +%s)
122
+
123
+ # ── Scan all projects under DATA_DIR ──
124
+ if [ -d "$DATA_DIR" ]; then
125
+ for PROJECT_DATA in "$DATA_DIR"/*/; do
126
+ [ -d "$PROJECT_DATA" ] || continue
127
+ PROJECT_NAME=$(basename "$PROJECT_DATA")
128
+
129
+ # ── Check 1: Running dispatch timeout ──
130
+ DISPATCHES_FILE="$PROJECT_DATA/dispatches.json"
131
+ if [ -f "$DISPATCHES_FILE" ]; then
132
+ RUNNING=$(jq -r '.[] | select(.status == "running" or .status == "dispatched") | "\\(.id)|\\(.role)|\\(.updatedAt)"' "$DISPATCHES_FILE" 2>/dev/null || true)
133
+
134
+ while IFS='|' read -r DID DROLE DUPDATED; do
135
+ [ -z "$DID" ] && continue
136
+ UPDATED_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$DUPDATED" | cut -c1-19)" +%s 2>/dev/null || echo "0")
137
+ if [ "$UPDATED_EPOCH" != "0" ]; then
138
+ ELAPSED_MIN=$(( (NOW_EPOCH - UPDATED_EPOCH) / 60 ))
139
+ if [ "$ELAPSED_MIN" -ge ${DISPATCH_TIMEOUT_MINUTES} ]; then
140
+ add_reminder "- [$PROJECT_NAME] dispatch $DID ($DROLE) 已运行 $ELAPSED_MIN 分钟,建议调用 project_status 检查进度"
141
+ fi
142
+ fi
143
+ done <<< "$RUNNING"
144
+ fi
145
+
146
+ # ── Check 2: Pending document reviews ──
147
+ REVIEWS_FILE="$PROJECT_DATA/reviews.json"
148
+ if [ -f "$REVIEWS_FILE" ]; then
149
+ PENDING=$(jq -r 'to_entries[] | select(.value.status == "pending") | "\\(.key)|\\(.value.submittedAt)"' "$REVIEWS_FILE" 2>/dev/null || true)
150
+ PENDING_COUNT=0
151
+ PENDING_DOCS=""
152
+
153
+ while IFS='|' read -r DOC_ID SUBMITTED_AT; do
154
+ [ -z "$DOC_ID" ] && continue
155
+ SUBMITTED_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$SUBMITTED_AT" | cut -c1-19)" +%s 2>/dev/null || echo "0")
156
+ if [ "$SUBMITTED_EPOCH" != "0" ]; then
157
+ ELAPSED_MIN=$(( (NOW_EPOCH - SUBMITTED_EPOCH) / 60 ))
158
+ if [ "$ELAPSED_MIN" -ge ${REVIEW_PENDING_TIMEOUT_MINUTES} ]; then
159
+ PENDING_COUNT=$((PENDING_COUNT + 1))
160
+ PENDING_DOCS="$PENDING_DOCS $DOC_ID"
161
+ fi
162
+ fi
163
+ done <<< "$PENDING"
164
+
165
+ if [ "$PENDING_COUNT" -gt 0 ]; then
166
+ add_reminder "- [$PROJECT_NAME] $PENDING_COUNT 份文档待审核超过 ${REVIEW_PENDING_TIMEOUT_MINUTES} 分钟:$PENDING_DOCS — 请尽快处理(doc_approve / reject_doc)"
167
+ fi
168
+ fi
169
+
170
+ # ── Check 3: Phase idle check ──
171
+ STATE_FILE="$PROJECT_DATA/state.json"
172
+ if [ -f "$STATE_FILE" ]; then
173
+ 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
+
176
+ if [ -n "$UPDATED_AT" ] && [ -n "$CURRENT_PHASE" ]; then
177
+ STATE_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$UPDATED_AT" | cut -c1-19)" +%s 2>/dev/null || echo "0")
178
+ if [ "$STATE_EPOCH" != "0" ]; then
179
+ 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 检查项目状态"
182
+ fi
183
+ fi
184
+ fi
185
+ fi
186
+ done
187
+ fi
188
+
189
+ # ── Output reminders ──
190
+ if [ -n "$REMINDERS" ]; then
191
+ echo "<harmonia-reminder>"
192
+ echo "以下事项需要你的关注:"
193
+ echo ""
194
+ echo "$REMINDERS"
195
+ echo ""
196
+ echo "请根据提醒采取相应行动。"
197
+ echo "</harmonia-reminder>"
198
+ fi
199
+
200
+ exit 0
201
+ `;
202
+ }
203
+ /**
204
+ * Create Claude Code hook definitions using agent-kit's defineHooks.
205
+ */
206
+ export function createClaudeCodeHooks(params) {
207
+ return defineHooks('claude-code', [
208
+ {
209
+ events: ['PreToolUse'],
210
+ content: generatePreToolUseScript(params),
211
+ },
212
+ {
213
+ events: ['UserPromptSubmit'],
214
+ content: generateUserPromptSubmitScript(params),
215
+ },
216
+ ]);
217
+ }
218
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/hooks/claude-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EACH,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,GACjC,MAAM,cAAc,CAAC;AAEtB;;;;;;;;;;;GAWG;AACH,SAAS,wBAAwB,CAAC,OAAmB;IACjD,MAAM,eAAe,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtE,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,oBAAoB,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE5G,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8BH,eAAe;;;;;;;;;;;;;;;;;;EAkBrB,iBAAiB;;;;;;;;;;;CAWlB,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,8BAA8B,CAAC,MAAkB;IACtD,OAAO;;;;;YAKC,MAAM,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCA+BU,wBAAwB;;;;;;;;;;;;;;;;;;;oCAmBxB,8BAA8B;;;;;;;;kEAQA,8BAA8B;;;;;;;;;;;;;;iCAc/D,0BAA0B;;;;;;;;;;;;;;;;;;;;;CAqB1D,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAkB;IACpD,OAAO,WAAW,CAAC,aAAa,EAAE;QAC9B;YACI,MAAM,EAAE,CAAC,YAAY,CAAC;YACtB,OAAO,EAAE,wBAAwB,CAAC,MAAM,CAAC;SAC5C;QACD;YACI,MAAM,EAAE,CAAC,kBAAkB,CAAC;YAC5B,OAAO,EAAE,8BAA8B,CAAC,MAAM,CAAC;SAClD;KACJ,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Hook content generation — shared configuration and rule definitions.
3
+ *
4
+ * Hook scripts run on the agent side (shell scripts for Claude Code,
5
+ * TS plugins for OpenCode, handlers for OpenClaw). They need to know:
6
+ * - HARMONIA_DATA_DIR: where to read dispatches.json, state.json, etc.
7
+ *
8
+ * Project-specific info (name, dir) is NOT baked in — hooks are project-agnostic.
9
+ * Boundary guards work on tool names + code file extensions only.
10
+ * Proactive reminders scan all projects under the data directory.
11
+ */
12
+ /**
13
+ * Parameters needed to generate hook content.
14
+ * Passed at install time and embedded into the generated scripts.
15
+ */
16
+ export interface HookParams {
17
+ /** Harmonia data directory (absolute path) */
18
+ dataDir: string;
19
+ }
20
+ /**
21
+ * Tool names that PM should not call directly (code modification tools).
22
+ * These are the standard agent tool names across different platforms.
23
+ */
24
+ export declare const BLOCKED_TOOLS: readonly ["Write", "Edit", "MultiEdit", "write", "edit", "Bash", "bash", "Terminal", "terminal"];
25
+ /**
26
+ * Shell commands that indicate development work (PM should not run these).
27
+ */
28
+ export declare const BLOCKED_COMMANDS: readonly ["npm run", "npm test", "npm start", "npm run build", "npx ", "yarn ", "pnpm ", "bun ", "node ", "deno ", "python ", "cargo ", "go run", "go test", "make ", "gcc ", "g++ ", "javac ", "mvn ", "gradle "];
29
+ /**
30
+ * File extensions that indicate source code (PM should not modify these).
31
+ */
32
+ export declare const CODE_EXTENSIONS: readonly [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".rs", ".go", ".java", ".c", ".cpp", ".h", ".hpp", ".cs", ".rb", ".php", ".swift", ".kt", ".vue", ".svelte"];
33
+ /**
34
+ * Harmonia MCP tool names — these are always allowed since PM uses them
35
+ * through Harmonia's own tool system.
36
+ */
37
+ export declare const HARMONIA_TOOLS: readonly ["project_init", "project_set_scale", "project_status", "phase_update", "role_dispatch", "dispatch_report", "doc_write", "doc_read", "doc_list", "doc_approve", "reject_doc", "guard_set", "guard_get", "review_set_rule", "review_list"];
38
+ /** Dispatch running timeout — warn after this many minutes */
39
+ export declare const DISPATCH_TIMEOUT_MINUTES = 30;
40
+ /** Phase idle timeout — warn after this many minutes with no tool calls */
41
+ export declare const PHASE_IDLE_TIMEOUT_MINUTES = 15;
42
+ /** Review pending timeout — warn after this many minutes */
43
+ export declare const REVIEW_PENDING_TIMEOUT_MINUTES = 10;