@s_s/harmonia 1.0.0 → 1.1.1
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 +396 -2
- package/build/cli/setup.d.ts +21 -0
- package/build/cli/setup.js +71 -0
- package/build/cli/setup.js.map +1 -0
- package/build/core/dispatch.d.ts +10 -0
- package/build/core/dispatch.js +21 -0
- package/build/core/dispatch.js.map +1 -1
- package/build/core/docs.d.ts +13 -0
- package/build/core/docs.js +32 -0
- package/build/core/docs.js.map +1 -1
- package/build/core/registry.d.ts +1 -1
- package/build/core/registry.js +5 -16
- package/build/core/registry.js.map +1 -1
- package/build/core/schema.d.ts +38 -0
- package/build/core/schema.js +187 -0
- package/build/core/schema.js.map +1 -0
- package/build/core/state.d.ts +11 -1
- package/build/core/state.js +23 -2
- package/build/core/state.js.map +1 -1
- package/build/core/steps.d.ts +34 -0
- package/build/core/steps.js +113 -0
- package/build/core/steps.js.map +1 -0
- package/build/core/types.d.ts +81 -4
- package/build/core/workflow.d.ts +26 -6
- package/build/core/workflow.js +88 -11
- package/build/core/workflow.js.map +1 -1
- package/build/hooks/claude-code.d.ts +20 -0
- package/build/hooks/claude-code.js +218 -0
- package/build/hooks/claude-code.js.map +1 -0
- package/build/hooks/content.d.ts +43 -0
- package/build/hooks/content.js +109 -0
- package/build/hooks/content.js.map +1 -0
- package/build/hooks/install.d.ts +40 -0
- package/build/hooks/install.js +63 -0
- package/build/hooks/install.js.map +1 -0
- package/build/hooks/openclaw.d.ts +24 -0
- package/build/hooks/openclaw.js +219 -0
- package/build/hooks/openclaw.js.map +1 -0
- package/build/hooks/opencode.d.ts +29 -0
- package/build/hooks/opencode.js +226 -0
- package/build/hooks/opencode.js.map +1 -0
- package/build/index.d.ts +4 -7
- package/build/index.js +80 -42
- package/build/index.js.map +1 -1
- package/build/setup/inject.d.ts +22 -18
- package/build/setup/inject.js +42 -93
- package/build/setup/inject.js.map +1 -1
- package/build/setup/templates.d.ts +12 -16
- package/build/setup/templates.js +52 -69
- package/build/setup/templates.js.map +1 -1
- package/build/tools/approve-doc.d.ts +1 -1
- package/build/tools/approve-doc.js +4 -4
- package/build/tools/approve-doc.js.map +1 -1
- package/build/tools/dispatch-role.d.ts +2 -2
- package/build/tools/dispatch-role.js +41 -11
- package/build/tools/dispatch-role.js.map +1 -1
- package/build/tools/doc-tools.d.ts +11 -3
- package/build/tools/doc-tools.js +257 -13
- package/build/tools/doc-tools.js.map +1 -1
- package/build/tools/get-project-status.d.ts +4 -2
- package/build/tools/get-project-status.js +165 -50
- package/build/tools/get-project-status.js.map +1 -1
- package/build/tools/get-role-prompt.d.ts +2 -2
- package/build/tools/get-role-prompt.js +4 -4
- package/build/tools/get-role-prompt.js.map +1 -1
- package/build/tools/override-tools.d.ts +1 -1
- package/build/tools/override-tools.js +4 -4
- package/build/tools/override-tools.js.map +1 -1
- package/build/tools/project-init.d.ts +5 -1
- package/build/tools/project-init.js +92 -32
- package/build/tools/project-init.js.map +1 -1
- package/build/tools/report-dispatch.d.ts +6 -3
- package/build/tools/report-dispatch.js +45 -8
- package/build/tools/report-dispatch.js.map +1 -1
- package/build/tools/set-scale.d.ts +6 -0
- package/build/tools/set-scale.js +92 -0
- package/build/tools/set-scale.js.map +1 -0
- package/build/tools/setup-project.d.ts +1 -1
- package/build/tools/setup-project.js +33 -5
- package/build/tools/setup-project.js.map +1 -1
- package/build/tools/update-phase.d.ts +8 -3
- package/build/tools/update-phase.js +85 -20
- package/build/tools/update-phase.js.map +1 -1
- package/package.json +2 -1
- package/workflows/dev/roles/architect.md +1 -1
- package/workflows/dev/roles/pm.md +5 -5
- package/workflows/dev/roles/tester.md +1 -1
- package/workflows/dev/schemas/api-design.json +25 -0
- package/workflows/dev/schemas/data-model.json +20 -0
- package/workflows/dev/schemas/deploy.json +20 -0
- package/workflows/dev/schemas/fsd.json +25 -0
- package/workflows/dev/schemas/prd.completeness-check.json +24 -0
- package/workflows/dev/schemas/prd.draft.json +15 -0
- package/workflows/dev/schemas/prd.final.json +30 -0
- package/workflows/dev/schemas/prd.json +30 -0
- package/workflows/dev/schemas/prd.requirements.json +25 -0
- package/workflows/dev/schemas/project-plan.json +20 -0
- package/workflows/dev/schemas/prototype.json +4 -0
- package/workflows/dev/schemas/retrospective.json +20 -0
- package/workflows/dev/schemas/risk-assessment.json +15 -0
- package/workflows/dev/schemas/task-breakdown.coarse.json +15 -0
- package/workflows/dev/schemas/task-breakdown.dependencies.json +20 -0
- package/workflows/dev/schemas/task-breakdown.detailed.json +10 -0
- package/workflows/dev/schemas/task-breakdown.final.json +10 -0
- package/workflows/dev/schemas/task-breakdown.json +10 -0
- package/workflows/dev/schemas/tech-design.analysis.json +25 -0
- package/workflows/dev/schemas/tech-design.api-contract.json +20 -0
- package/workflows/dev/schemas/tech-design.draft.json +15 -0
- package/workflows/dev/schemas/tech-design.final.json +30 -0
- package/workflows/dev/schemas/tech-design.json +30 -0
- package/workflows/dev/schemas/test-plan.json +20 -0
- package/workflows/dev/schemas/test-report.json +25 -0
- package/workflows/dev/schemas/user-stories.json +10 -0
- package/workflows/dev/workflow.json +85 -5
package/build/core/types.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|
package/build/core/workflow.d.ts
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workflow loader —
|
|
3
|
-
*
|
|
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
|
-
*
|
|
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
|
|
17
|
+
export declare function resolveWorkflowDir(builtinDir: string, customDir: string, name: string): Promise<string>;
|
|
10
18
|
/**
|
|
11
|
-
*
|
|
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
|
|
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[]>;
|
package/build/core/workflow.js
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workflow loader —
|
|
3
|
-
*
|
|
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
|
|
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(
|
|
39
|
-
const workflowDir =
|
|
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
|
-
|
|
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(
|
|
60
|
-
const
|
|
61
|
-
|
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/core/workflow.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,iBAAiB;AAEjB,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC5C,YAAY,IAAY,EAAE,QAAkB;QACxC,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,KAAK,CAAC,QAAQ,IAAI,eAAe,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACxC,CAAC;CACJ;AAED,2BAA2B;AAE3B;;GAEG;AACH,KAAK,UAAU,UAAU,CAAC,IAAY;IAClC,IAAI,CAAC;QACD,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAkB,EAAE,SAAiB,EAAE,IAAY;IACxF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;IAC1D,IAAI,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;IAC5D,IAAI,MAAM,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,OAAO,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,IAAI,qBAAqB,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,EAAU,EAAE,OAAe;IAC9C,MAAM,OAAO,GAAG,oCAAoC,CAAC;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAErC,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,OAAO;YACH,EAAE;YACF,WAAW,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;YAClE,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE;SACzB,CAAC;IACN,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAmC,CAAC;IACvE,MAAM,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;IAExB,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,YAAiC,CAAC,CAAC,CAAC,SAAS,CAAC;IAExG,MAAM,WAAW,GAAoB;QACjC,KAAK,EAAG,EAAE,CAAC,KAAgB,IAAI,QAAQ;QACvC,OAAO,EAAG,EAAE,CAAC,OAAsC,IAAI,MAAM;QAC7D,QAAQ,EAAG,EAAE,CAAC,QAAoB,IAAI,KAAK;QAC3C,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5C,CAAC;IAEF,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC;AAED,qBAAqB;AAErB;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB,EAAE,SAAiB,EAAE,IAAY;IAClF,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAE1E,qBAAqB;IACrB,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;IACjF,MAAM,UAAU,GAAuB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAEhE,aAAa;IACb,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5C,IAAI,SAAS,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC;QACD,SAAS,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACL,oDAAoD;IACxD,CAAC;IACD,MAAM,KAAK,GAAmC,EAAE,CAAC;IAEjD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QAChC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9D,KAAK,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB,EAAE,SAAiB;IACrE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhC,qBAAqB;IACrB,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACnE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,WAAW,EAAE;gBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,mDAAmD;IACvD,CAAC;IAED,sDAAsD;IACtD,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,WAAW,EAAE;gBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,6CAA6C;IACjD,CAAC;IAED,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -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;
|