@s_s/harmonia 1.0.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 (70) hide show
  1. package/README.md +3 -0
  2. package/build/core/dispatch.d.ts +38 -0
  3. package/build/core/dispatch.js +156 -0
  4. package/build/core/dispatch.js.map +1 -0
  5. package/build/core/docs.d.ts +19 -0
  6. package/build/core/docs.js +59 -0
  7. package/build/core/docs.js.map +1 -0
  8. package/build/core/overrides.d.ts +62 -0
  9. package/build/core/overrides.js +206 -0
  10. package/build/core/overrides.js.map +1 -0
  11. package/build/core/registry.d.ts +72 -0
  12. package/build/core/registry.js +121 -0
  13. package/build/core/registry.js.map +1 -0
  14. package/build/core/reviews.d.ts +26 -0
  15. package/build/core/reviews.js +83 -0
  16. package/build/core/reviews.js.map +1 -0
  17. package/build/core/state.d.ts +26 -0
  18. package/build/core/state.js +103 -0
  19. package/build/core/state.js.map +1 -0
  20. package/build/core/types.d.ts +181 -0
  21. package/build/core/types.js +6 -0
  22. package/build/core/types.js.map +1 -0
  23. package/build/core/workflow.d.ts +13 -0
  24. package/build/core/workflow.js +63 -0
  25. package/build/core/workflow.js.map +1 -0
  26. package/build/index.d.ts +15 -0
  27. package/build/index.js +57 -0
  28. package/build/index.js.map +1 -0
  29. package/build/setup/inject.d.ts +34 -0
  30. package/build/setup/inject.js +115 -0
  31. package/build/setup/inject.js.map +1 -0
  32. package/build/setup/templates.d.ts +21 -0
  33. package/build/setup/templates.js +177 -0
  34. package/build/setup/templates.js.map +1 -0
  35. package/build/tools/approve-doc.d.ts +6 -0
  36. package/build/tools/approve-doc.js +79 -0
  37. package/build/tools/approve-doc.js.map +1 -0
  38. package/build/tools/dispatch-role.d.ts +16 -0
  39. package/build/tools/dispatch-role.js +232 -0
  40. package/build/tools/dispatch-role.js.map +1 -0
  41. package/build/tools/doc-tools.d.ts +8 -0
  42. package/build/tools/doc-tools.js +102 -0
  43. package/build/tools/doc-tools.js.map +1 -0
  44. package/build/tools/get-project-status.d.ts +8 -0
  45. package/build/tools/get-project-status.js +230 -0
  46. package/build/tools/get-project-status.js.map +1 -0
  47. package/build/tools/get-role-prompt.d.ts +7 -0
  48. package/build/tools/get-role-prompt.js +95 -0
  49. package/build/tools/get-role-prompt.js.map +1 -0
  50. package/build/tools/override-tools.d.ts +6 -0
  51. package/build/tools/override-tools.js +129 -0
  52. package/build/tools/override-tools.js.map +1 -0
  53. package/build/tools/project-init.d.ts +6 -0
  54. package/build/tools/project-init.js +71 -0
  55. package/build/tools/project-init.js.map +1 -0
  56. package/build/tools/report-dispatch.d.ts +11 -0
  57. package/build/tools/report-dispatch.js +142 -0
  58. package/build/tools/report-dispatch.js.map +1 -0
  59. package/build/tools/setup-project.d.ts +8 -0
  60. package/build/tools/setup-project.js +88 -0
  61. package/build/tools/setup-project.js.map +1 -0
  62. package/build/tools/update-phase.d.ts +7 -0
  63. package/build/tools/update-phase.js +79 -0
  64. package/build/tools/update-phase.js.map +1 -0
  65. package/package.json +51 -0
  66. package/workflows/dev/roles/architect.md +66 -0
  67. package/workflows/dev/roles/developer.md +44 -0
  68. package/workflows/dev/roles/pm.md +99 -0
  69. package/workflows/dev/roles/tester.md +44 -0
  70. package/workflows/dev/workflow.json +134 -0
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # harmonia
2
+
3
+ 让不同角色和谐协作
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Dispatch & Session tracking — manages:
3
+ * <data_dir>/<project_name>/sessions.json
4
+ * <data_dir>/<project_name>/dispatches.json
5
+ *
6
+ * Sessions represent agent instances (can be reused across dispatches).
7
+ * Dispatches represent individual task assignments to a role.
8
+ * Relationship: Session 1:N Dispatch (a persistent session can receive multiple dispatches).
9
+ */
10
+ import type { AgentType, SessionRecord, SessionStatus, DispatchRecord, DispatchStatus } from './types.js';
11
+ export declare function readSessions(projectName: string): Promise<SessionRecord[]>;
12
+ export declare function createSession(projectName: string, role: string, agentType?: AgentType, label?: string): Promise<SessionRecord>;
13
+ export declare function updateSession(projectName: string, sessionId: string, updates: {
14
+ status?: SessionStatus;
15
+ agentSessionId?: string;
16
+ agentType?: AgentType;
17
+ label?: string;
18
+ }): Promise<SessionRecord>;
19
+ /**
20
+ * Find an idle session for a given role (for session reuse).
21
+ * Returns the most recently active idle session, or null if none found.
22
+ */
23
+ export declare function findIdleSession(projectName: string, role: string): Promise<SessionRecord | null>;
24
+ /**
25
+ * Find a session by its agent session ID (for correlating external sessions).
26
+ */
27
+ export declare function findSessionByAgentId(projectName: string, role: string, agentSessionId: string): Promise<SessionRecord | null>;
28
+ export declare function readDispatches(projectName: string): Promise<DispatchRecord[]>;
29
+ export declare function createDispatch(projectName: string, role: string, taskBrief: string, expectedOutputs: string[], sessionId?: string): Promise<DispatchRecord>;
30
+ export declare function updateDispatch(projectName: string, dispatchId: string, updates: {
31
+ status?: DispatchStatus;
32
+ sessionId?: string;
33
+ note?: string;
34
+ }): Promise<DispatchRecord>;
35
+ /**
36
+ * Get a single dispatch record by ID.
37
+ */
38
+ export declare function getDispatch(projectName: string, dispatchId: string): Promise<DispatchRecord | null>;
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Dispatch & Session tracking — manages:
3
+ * <data_dir>/<project_name>/sessions.json
4
+ * <data_dir>/<project_name>/dispatches.json
5
+ *
6
+ * Sessions represent agent instances (can be reused across dispatches).
7
+ * Dispatches represent individual task assignments to a role.
8
+ * Relationship: Session 1:N Dispatch (a persistent session can receive multiple dispatches).
9
+ */
10
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
11
+ import { join, dirname } from 'node:path';
12
+ import { getProjectDataDir } from './registry.js';
13
+ const SESSIONS_FILE = 'sessions.json';
14
+ const DISPATCHES_FILE = 'dispatches.json';
15
+ function sessionsPath(projectName) {
16
+ return join(getProjectDataDir(projectName), SESSIONS_FILE);
17
+ }
18
+ function dispatchesPath(projectName) {
19
+ return join(getProjectDataDir(projectName), DISPATCHES_FILE);
20
+ }
21
+ // ─── Session CRUD ───
22
+ export async function readSessions(projectName) {
23
+ try {
24
+ const content = await readFile(sessionsPath(projectName), 'utf-8');
25
+ return JSON.parse(content);
26
+ }
27
+ catch {
28
+ return [];
29
+ }
30
+ }
31
+ async function writeSessions(projectName, sessions) {
32
+ const filePath = sessionsPath(projectName);
33
+ await mkdir(dirname(filePath), { recursive: true });
34
+ await writeFile(filePath, JSON.stringify(sessions, null, 2) + '\n', 'utf-8');
35
+ }
36
+ function nextSessionId(sessions) {
37
+ const num = sessions.length + 1;
38
+ return `ses-${String(num).padStart(3, '0')}`;
39
+ }
40
+ export async function createSession(projectName, role, agentType, label) {
41
+ const sessions = await readSessions(projectName);
42
+ const now = new Date().toISOString();
43
+ const session = {
44
+ id: nextSessionId(sessions),
45
+ role,
46
+ agentType,
47
+ status: 'active',
48
+ label,
49
+ createdAt: now,
50
+ lastActiveAt: now,
51
+ };
52
+ sessions.push(session);
53
+ await writeSessions(projectName, sessions);
54
+ return session;
55
+ }
56
+ export async function updateSession(projectName, sessionId, updates) {
57
+ const sessions = await readSessions(projectName);
58
+ const session = sessions.find((s) => s.id === sessionId);
59
+ if (!session) {
60
+ throw new Error(`Session "${sessionId}" not found in project "${projectName}"`);
61
+ }
62
+ if (updates.status !== undefined)
63
+ session.status = updates.status;
64
+ if (updates.agentSessionId !== undefined)
65
+ session.agentSessionId = updates.agentSessionId;
66
+ if (updates.agentType !== undefined)
67
+ session.agentType = updates.agentType;
68
+ if (updates.label !== undefined)
69
+ session.label = updates.label;
70
+ session.lastActiveAt = new Date().toISOString();
71
+ await writeSessions(projectName, sessions);
72
+ return session;
73
+ }
74
+ /**
75
+ * Find an idle session for a given role (for session reuse).
76
+ * Returns the most recently active idle session, or null if none found.
77
+ */
78
+ export async function findIdleSession(projectName, role) {
79
+ const sessions = await readSessions(projectName);
80
+ const idle = sessions
81
+ .filter((s) => s.role === role && s.status === 'idle')
82
+ .sort((a, b) => b.lastActiveAt.localeCompare(a.lastActiveAt));
83
+ return idle[0] ?? null;
84
+ }
85
+ /**
86
+ * Find a session by its agent session ID (for correlating external sessions).
87
+ */
88
+ export async function findSessionByAgentId(projectName, role, agentSessionId) {
89
+ const sessions = await readSessions(projectName);
90
+ return sessions.find((s) => s.role === role && s.agentSessionId === agentSessionId) ?? null;
91
+ }
92
+ // ─── Dispatch CRUD ───
93
+ export async function readDispatches(projectName) {
94
+ try {
95
+ const content = await readFile(dispatchesPath(projectName), 'utf-8');
96
+ return JSON.parse(content);
97
+ }
98
+ catch {
99
+ return [];
100
+ }
101
+ }
102
+ async function writeDispatches(projectName, dispatches) {
103
+ const filePath = dispatchesPath(projectName);
104
+ await mkdir(dirname(filePath), { recursive: true });
105
+ await writeFile(filePath, JSON.stringify(dispatches, null, 2) + '\n', 'utf-8');
106
+ }
107
+ function nextDispatchId(dispatches) {
108
+ const num = dispatches.length + 1;
109
+ return `dispatch-${String(num).padStart(3, '0')}`;
110
+ }
111
+ export async function createDispatch(projectName, role, taskBrief, expectedOutputs, sessionId) {
112
+ const dispatches = await readDispatches(projectName);
113
+ const now = new Date().toISOString();
114
+ const dispatch = {
115
+ id: nextDispatchId(dispatches),
116
+ role,
117
+ taskBrief,
118
+ status: 'dispatched',
119
+ expectedOutputs,
120
+ createdAt: now,
121
+ updatedAt: now,
122
+ ...(sessionId ? { sessionId } : {}),
123
+ };
124
+ dispatches.push(dispatch);
125
+ await writeDispatches(projectName, dispatches);
126
+ return dispatch;
127
+ }
128
+ export async function updateDispatch(projectName, dispatchId, updates) {
129
+ const dispatches = await readDispatches(projectName);
130
+ const dispatch = dispatches.find((d) => d.id === dispatchId);
131
+ if (!dispatch) {
132
+ throw new Error(`Dispatch "${dispatchId}" not found in project "${projectName}"`);
133
+ }
134
+ const now = new Date().toISOString();
135
+ if (updates.status !== undefined) {
136
+ dispatch.status = updates.status;
137
+ if (updates.status === 'completed' || updates.status === 'failed' || updates.status === 'cancelled') {
138
+ dispatch.completedAt = now;
139
+ }
140
+ }
141
+ if (updates.sessionId !== undefined)
142
+ dispatch.sessionId = updates.sessionId;
143
+ if (updates.note !== undefined)
144
+ dispatch.note = updates.note;
145
+ dispatch.updatedAt = now;
146
+ await writeDispatches(projectName, dispatches);
147
+ return dispatch;
148
+ }
149
+ /**
150
+ * Get a single dispatch record by ID.
151
+ */
152
+ export async function getDispatch(projectName, dispatchId) {
153
+ const dispatches = await readDispatches(projectName);
154
+ return dispatches.find((d) => d.id === dispatchId) ?? null;
155
+ }
156
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/core/dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Document management — read/write files under <data_dir>/<project_name>/docs/
3
+ *
4
+ * Supports both .md and .html files based on doc format configuration.
5
+ */
6
+ import type { DocDefinition } from './types.js';
7
+ /**
8
+ * Write a document to <data_dir>/<project_name>/docs/<docId>.<ext>
9
+ */
10
+ export declare function writeDoc(projectName: string, docId: string, content: string, docDef?: DocDefinition): Promise<string>;
11
+ /**
12
+ * Read a document from <data_dir>/<project_name>/docs/<docId>.<ext>
13
+ * Tries both .md and .html extensions.
14
+ */
15
+ export declare function readDoc(projectName: string, docId: string): Promise<string>;
16
+ /**
17
+ * List all documents in <data_dir>/<project_name>/docs/
18
+ */
19
+ export declare function listDocs(projectName: string): Promise<string[]>;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Document management — read/write files under <data_dir>/<project_name>/docs/
3
+ *
4
+ * Supports both .md and .html files based on doc format configuration.
5
+ */
6
+ import { mkdir, readFile, writeFile, readdir } from 'node:fs/promises';
7
+ import { join } from 'node:path';
8
+ import { getProjectDataDir } from './registry.js';
9
+ function docsDir(projectName) {
10
+ return join(getProjectDataDir(projectName), 'docs');
11
+ }
12
+ /**
13
+ * Get file extension for a doc based on its definition.
14
+ */
15
+ function getDocExtension(docDef) {
16
+ return docDef?.format === 'html' ? '.html' : '.md';
17
+ }
18
+ /**
19
+ * Write a document to <data_dir>/<project_name>/docs/<docId>.<ext>
20
+ */
21
+ export async function writeDoc(projectName, docId, content, docDef) {
22
+ const dir = docsDir(projectName);
23
+ await mkdir(dir, { recursive: true });
24
+ const ext = getDocExtension(docDef);
25
+ const filePath = join(dir, `${docId}${ext}`);
26
+ await writeFile(filePath, content, 'utf-8');
27
+ return filePath;
28
+ }
29
+ /**
30
+ * Read a document from <data_dir>/<project_name>/docs/<docId>.<ext>
31
+ * Tries both .md and .html extensions.
32
+ */
33
+ export async function readDoc(projectName, docId) {
34
+ const dir = docsDir(projectName);
35
+ // Try .md first, then .html
36
+ for (const ext of ['.md', '.html']) {
37
+ try {
38
+ return await readFile(join(dir, `${docId}${ext}`), 'utf-8');
39
+ }
40
+ catch {
41
+ // try next extension
42
+ }
43
+ }
44
+ throw new Error(`Document "${docId}" not found`);
45
+ }
46
+ /**
47
+ * List all documents in <data_dir>/<project_name>/docs/
48
+ */
49
+ export async function listDocs(projectName) {
50
+ const dir = docsDir(projectName);
51
+ try {
52
+ const files = await readdir(dir);
53
+ return files.filter((f) => f.endsWith('.md') || f.endsWith('.html')).map((f) => f.replace(/\.(md|html)$/, ''));
54
+ }
55
+ catch {
56
+ return [];
57
+ }
58
+ }
59
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/core/docs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGlD,SAAS,OAAO,CAAC,WAAmB;IAChC,OAAO,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,MAAsB;IAC3C,OAAO,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC1B,WAAmB,EACnB,KAAa,EACb,OAAe,EACf,MAAsB;IAEtB,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACjC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,CAAC;IAC7C,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,WAAmB,EAAE,KAAa;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAEjC,4BAA4B;IAC5B,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC;YACD,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACL,qBAAqB;QACzB,CAAC;IACL,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,aAAa,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,WAAmB;IAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACjC,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC;IACnH,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Override configuration — three-layer merge system.
3
+ *
4
+ * Priority: project-level > global-level > workflow defaults
5
+ *
6
+ * Files:
7
+ * <data_dir>/overrides.json (global)
8
+ * <data_dir>/<project_name>/overrides.json (project)
9
+ */
10
+ import type { CapabilityOverride, DocDefinition, OverrideConfig } from './types.js';
11
+ /**
12
+ * Read global overrides (<data_dir>/overrides.json).
13
+ */
14
+ export declare function readGlobalOverrides(): Promise<OverrideConfig>;
15
+ /**
16
+ * Read project-level overrides (<data_dir>/<project_name>/overrides.json).
17
+ */
18
+ export declare function readProjectOverrides(projectName: string): Promise<OverrideConfig>;
19
+ /**
20
+ * Write global overrides.
21
+ */
22
+ export declare function writeGlobalOverrides(config: OverrideConfig): Promise<void>;
23
+ /**
24
+ * Write project-level overrides.
25
+ */
26
+ export declare function writeProjectOverrides(projectName: string, config: OverrideConfig): Promise<void>;
27
+ /**
28
+ * Get the fully merged override config for a project.
29
+ * Priority: project > global
30
+ */
31
+ export declare function getMergedOverrides(projectName: string): Promise<OverrideConfig>;
32
+ /**
33
+ * Resolve whether a specific doc requires review.
34
+ *
35
+ * Priority: project override > global override > workflow default
36
+ */
37
+ export declare function resolveDocReview(docId: string, docDef: DocDefinition, overrides: OverrideConfig): boolean;
38
+ /**
39
+ * Resolve the capability override for a specific role + capability.
40
+ * Returns null if no override is configured.
41
+ */
42
+ export declare function resolveCapabilityOverride(roleId: string, capabilityId: string, overrides: OverrideConfig): CapabilityOverride | null;
43
+ /**
44
+ * Set a single role capability override at project or global level.
45
+ */
46
+ export declare function setCapabilityOverride(scope: 'global' | 'project', projectName: string | null, roleId: string, capabilityId: string, override: CapabilityOverride): Promise<void>;
47
+ /**
48
+ * Set review override for a specific doc at project or global level.
49
+ */
50
+ export declare function setReviewOverride(scope: 'global' | 'project', projectName: string | null, docId: string, enabled: boolean): Promise<void>;
51
+ /**
52
+ * Resolve the agent/model configuration for a role.
53
+ * Returns { agent, model } from the merged overrides, or undefined fields if not set.
54
+ */
55
+ export declare function resolveRoleConfig(roleId: string, overrides: OverrideConfig): {
56
+ agent?: string;
57
+ model?: string;
58
+ };
59
+ /**
60
+ * Set agent/model config for a role at project or global level.
61
+ */
62
+ export declare function setRoleAgentConfig(scope: 'global' | 'project', projectName: string | null, roleId: string, agent?: string, model?: string): Promise<void>;
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Override configuration — three-layer merge system.
3
+ *
4
+ * Priority: project-level > global-level > workflow defaults
5
+ *
6
+ * Files:
7
+ * <data_dir>/overrides.json (global)
8
+ * <data_dir>/<project_name>/overrides.json (project)
9
+ */
10
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
11
+ import { join, dirname } from 'node:path';
12
+ import { getGlobalDir, getProjectDataDir } from './registry.js';
13
+ const OVERRIDES_FILE = 'overrides.json';
14
+ /**
15
+ * Read an override config file. Returns empty config if file doesn't exist.
16
+ */
17
+ async function readOverrideFile(filePath) {
18
+ try {
19
+ const content = await readFile(filePath, 'utf-8');
20
+ return JSON.parse(content);
21
+ }
22
+ catch {
23
+ return {};
24
+ }
25
+ }
26
+ /**
27
+ * Read global overrides (<data_dir>/overrides.json).
28
+ */
29
+ export async function readGlobalOverrides() {
30
+ return readOverrideFile(join(getGlobalDir(), OVERRIDES_FILE));
31
+ }
32
+ /**
33
+ * Read project-level overrides (<data_dir>/<project_name>/overrides.json).
34
+ */
35
+ export async function readProjectOverrides(projectName) {
36
+ return readOverrideFile(join(getProjectDataDir(projectName), OVERRIDES_FILE));
37
+ }
38
+ /**
39
+ * Write an override config file.
40
+ */
41
+ async function writeOverrideFile(filePath, config) {
42
+ await mkdir(dirname(filePath), { recursive: true });
43
+ await writeFile(filePath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
44
+ }
45
+ /**
46
+ * Write global overrides.
47
+ */
48
+ export async function writeGlobalOverrides(config) {
49
+ await writeOverrideFile(join(getGlobalDir(), OVERRIDES_FILE), config);
50
+ }
51
+ /**
52
+ * Write project-level overrides.
53
+ */
54
+ export async function writeProjectOverrides(projectName, config) {
55
+ await writeOverrideFile(join(getProjectDataDir(projectName), OVERRIDES_FILE), config);
56
+ }
57
+ /**
58
+ * Merge two override configs. `higher` takes priority over `lower`.
59
+ */
60
+ function mergeConfigs(lower, higher) {
61
+ const merged = {};
62
+ // Merge review: higher wins entirely if present
63
+ if (higher.review !== undefined) {
64
+ merged.review = higher.review;
65
+ }
66
+ else if (lower.review !== undefined) {
67
+ merged.review = lower.review;
68
+ }
69
+ // Merge roles: deep merge per role (agent, model, capabilities)
70
+ const lowerRoles = lower.roles ?? {};
71
+ const higherRoles = higher.roles ?? {};
72
+ const allRoleIds = new Set([...Object.keys(lowerRoles), ...Object.keys(higherRoles)]);
73
+ if (allRoleIds.size > 0) {
74
+ merged.roles = {};
75
+ for (const roleId of allRoleIds) {
76
+ const lo = lowerRoles[roleId] ?? {};
77
+ const hi = higherRoles[roleId] ?? {};
78
+ merged.roles[roleId] = {
79
+ ...lo,
80
+ ...hi,
81
+ // Deep merge capabilities
82
+ capabilities: {
83
+ ...(lo.capabilities ?? {}),
84
+ ...(hi.capabilities ?? {}),
85
+ },
86
+ };
87
+ // Clean up empty capabilities
88
+ if (Object.keys(merged.roles[roleId].capabilities).length === 0) {
89
+ delete merged.roles[roleId].capabilities;
90
+ }
91
+ }
92
+ }
93
+ return merged;
94
+ }
95
+ /**
96
+ * Get the fully merged override config for a project.
97
+ * Priority: project > global
98
+ */
99
+ export async function getMergedOverrides(projectName) {
100
+ const global = await readGlobalOverrides();
101
+ const project = await readProjectOverrides(projectName);
102
+ return mergeConfigs(global, project);
103
+ }
104
+ /**
105
+ * Resolve whether a specific doc requires review.
106
+ *
107
+ * Priority: project override > global override > workflow default
108
+ */
109
+ export function resolveDocReview(docId, docDef, overrides) {
110
+ const review = overrides.review;
111
+ // Override is a per-doc record
112
+ if (typeof review === 'object' && review !== null) {
113
+ if (docId in review) {
114
+ return review[docId];
115
+ }
116
+ // Not mentioned in override, fall through to workflow default
117
+ }
118
+ // Override is a global boolean toggle
119
+ if (typeof review === 'boolean') {
120
+ return review;
121
+ }
122
+ // No override — use workflow default
123
+ return docDef.review ?? false;
124
+ }
125
+ /**
126
+ * Resolve the capability override for a specific role + capability.
127
+ * Returns null if no override is configured.
128
+ */
129
+ export function resolveCapabilityOverride(roleId, capabilityId, overrides) {
130
+ return overrides.roles?.[roleId]?.capabilities?.[capabilityId] ?? null;
131
+ }
132
+ /**
133
+ * Set a single role capability override at project or global level.
134
+ */
135
+ export async function setCapabilityOverride(scope, projectName, roleId, capabilityId, override) {
136
+ const config = scope === 'global' ? await readGlobalOverrides() : await readProjectOverrides(projectName);
137
+ if (!config.roles) {
138
+ config.roles = {};
139
+ }
140
+ if (!config.roles[roleId]) {
141
+ config.roles[roleId] = {};
142
+ }
143
+ if (!config.roles[roleId].capabilities) {
144
+ config.roles[roleId].capabilities = {};
145
+ }
146
+ config.roles[roleId].capabilities[capabilityId] = override;
147
+ if (scope === 'global') {
148
+ await writeGlobalOverrides(config);
149
+ }
150
+ else {
151
+ await writeProjectOverrides(projectName, config);
152
+ }
153
+ }
154
+ /**
155
+ * Set review override for a specific doc at project or global level.
156
+ */
157
+ export async function setReviewOverride(scope, projectName, docId, enabled) {
158
+ const config = scope === 'global' ? await readGlobalOverrides() : await readProjectOverrides(projectName);
159
+ // Ensure review is a per-doc record
160
+ if (typeof config.review !== 'object' || config.review === null) {
161
+ config.review = {};
162
+ }
163
+ config.review[docId] = enabled;
164
+ if (scope === 'global') {
165
+ await writeGlobalOverrides(config);
166
+ }
167
+ else {
168
+ await writeProjectOverrides(projectName, config);
169
+ }
170
+ }
171
+ /**
172
+ * Resolve the agent/model configuration for a role.
173
+ * Returns { agent, model } from the merged overrides, or undefined fields if not set.
174
+ */
175
+ export function resolveRoleConfig(roleId, overrides) {
176
+ const role = overrides.roles?.[roleId];
177
+ if (!role)
178
+ return {};
179
+ return {
180
+ ...(role.agent ? { agent: role.agent } : {}),
181
+ ...(role.model ? { model: role.model } : {}),
182
+ };
183
+ }
184
+ /**
185
+ * Set agent/model config for a role at project or global level.
186
+ */
187
+ export async function setRoleAgentConfig(scope, projectName, roleId, agent, model) {
188
+ const config = scope === 'global' ? await readGlobalOverrides() : await readProjectOverrides(projectName);
189
+ if (!config.roles) {
190
+ config.roles = {};
191
+ }
192
+ if (!config.roles[roleId]) {
193
+ config.roles[roleId] = {};
194
+ }
195
+ if (agent)
196
+ config.roles[roleId].agent = agent;
197
+ if (model)
198
+ config.roles[roleId].model = model;
199
+ if (scope === 'global') {
200
+ await writeGlobalOverrides(config);
201
+ }
202
+ else {
203
+ await writeProjectOverrides(projectName, config);
204
+ }
205
+ }
206
+ //# sourceMappingURL=overrides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overrides.js","sourceRoot":"","sources":["../../src/core/overrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGhE,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAExC;;GAEG;AACH,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IAC5C,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAmB,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACrC,OAAO,gBAAgB,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,WAAmB;IAC1D,OAAO,gBAAgB,CAAC,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;AAClF,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,iBAAiB,CAAC,QAAgB,EAAE,MAAsB;IACrE,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/E,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAAsB;IAC7D,MAAM,iBAAiB,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;AAC1E,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,WAAmB,EAAE,MAAsB;IACnF,MAAM,iBAAiB,CAAC,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;AAC1F,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,KAAqB,EAAE,MAAsB;IAC/D,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,gDAAgD;IAChD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAClC,CAAC;SAAM,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACpC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACjC,CAAC;IAED,gEAAgE;IAChE,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;IACrC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;IACvC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAEtF,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;QAClB,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;YAC9B,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACrC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG;gBACnB,GAAG,EAAE;gBACL,GAAG,EAAE;gBACL,0BAA0B;gBAC1B,YAAY,EAAE;oBACV,GAAG,CAAC,EAAE,CAAC,YAAY,IAAI,EAAE,CAAC;oBAC1B,GAAG,CAAC,EAAE,CAAC,YAAY,IAAI,EAAE,CAAC;iBAC7B;aACJ,CAAC;YACF,8BAA8B;YAC9B,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,YAAa,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/D,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC;YAC7C,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,WAAmB;IACxD,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;IACxD,OAAO,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAqB,EAAE,SAAyB;IAC5F,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;IAEhC,+BAA+B;IAC/B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAChD,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;YAClB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;QACD,8DAA8D;IAClE,CAAC;IAED,sCAAsC;IACtC,IAAI,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,qCAAqC;IACrC,OAAO,MAAM,CAAC,MAAM,IAAI,KAAK,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACrC,MAAc,EACd,YAAoB,EACpB,SAAyB;IAEzB,OAAO,SAAS,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC;AAC3E,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACvC,KAA2B,EAC3B,WAA0B,EAC1B,MAAc,EACd,YAAoB,EACpB,QAA4B;IAE5B,MAAM,MAAM,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,mBAAmB,EAAE,CAAC,CAAC,CAAC,MAAM,oBAAoB,CAAC,WAAY,CAAC,CAAC;IAE3G,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;IAC9B,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,YAAa,CAAC,YAAY,CAAC,GAAG,QAAQ,CAAC;IAE5D,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACrB,MAAM,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;SAAM,CAAC;QACJ,MAAM,qBAAqB,CAAC,WAAY,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACnC,KAA2B,EAC3B,WAA0B,EAC1B,KAAa,EACb,OAAgB;IAEhB,MAAM,MAAM,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,mBAAmB,EAAE,CAAC,CAAC,CAAC,MAAM,oBAAoB,CAAC,WAAY,CAAC,CAAC;IAE3G,oCAAoC;IACpC,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAC9D,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC;IACvB,CAAC;IACD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAE/B,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACrB,MAAM,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;SAAM,CAAC;QACJ,MAAM,qBAAqB,CAAC,WAAY,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,SAAyB;IACvE,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,OAAO;QACH,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/C,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,KAA2B,EAC3B,WAA0B,EAC1B,MAAc,EACd,KAAc,EACd,KAAc;IAEd,MAAM,MAAM,GAAG,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,mBAAmB,EAAE,CAAC,CAAC,CAAC,MAAM,oBAAoB,CAAC,WAAY,CAAC,CAAC;IAE3G,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;IAC9B,CAAC;IACD,IAAI,KAAK;QAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,GAAG,KAA8B,CAAC;IACvE,IAAI,KAAK;QAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC;IAE9C,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACrB,MAAM,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;SAAM,CAAC;QACJ,MAAM,qBAAqB,CAAC,WAAY,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;AACL,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Global registry — manages Harmonia data directory and project name → directory mappings.
3
+ *
4
+ * Data directory follows system conventions (see getGlobalDir()):
5
+ * macOS: ~/Library/Application Support/harmonia
6
+ * Linux: $XDG_DATA_HOME/harmonia (defaults to ~/.local/share/harmonia)
7
+ * Windows: %APPDATA%/harmonia
8
+ * Override: HARMONIA_DATA_DIR env var
9
+ *
10
+ * Structure:
11
+ * <data_dir>/
12
+ * ├── registry.json # { projects: { "my-app": { dir: "/path/to/src", ... } } }
13
+ * ├── overrides.json # global overrides (optional)
14
+ * ├── my-app/
15
+ * │ ├── state.json
16
+ * │ ├── docs/
17
+ * │ ├── reviews.json
18
+ * │ ├── overrides.json # project-level overrides (optional)
19
+ * │ └── ...
20
+ * └── another-project/
21
+ * └── ...
22
+ */
23
+ export interface ProjectEntry {
24
+ /** Absolute path to the project source directory */
25
+ dir: string;
26
+ /** Workflow name used by this project */
27
+ workflow: string;
28
+ /** When the project was registered */
29
+ createdAt: string;
30
+ }
31
+ export interface Registry {
32
+ projects: Record<string, ProjectEntry>;
33
+ }
34
+ /**
35
+ * Get the global Harmonia data directory, following system conventions.
36
+ *
37
+ * Resolution order:
38
+ * 1. HARMONIA_DATA_DIR env var (explicit override)
39
+ * 2. Platform default:
40
+ * - macOS: ~/Library/Application Support/harmonia
41
+ * - Windows: %APPDATA%/harmonia
42
+ * - Linux: $XDG_DATA_HOME/harmonia (defaults to ~/.local/share/harmonia)
43
+ */
44
+ export declare function getGlobalDir(): string;
45
+ /**
46
+ * Get the data directory for a specific project within the global dir.
47
+ */
48
+ export declare function getProjectDataDir(projectName: string): string;
49
+ /**
50
+ * Read the global registry. Returns empty registry if file doesn't exist.
51
+ */
52
+ export declare function readRegistry(): Promise<Registry>;
53
+ /**
54
+ * Write the global registry to disk.
55
+ */
56
+ export declare function writeRegistry(registry: Registry): Promise<void>;
57
+ /**
58
+ * Register a new project. Creates the project data directory.
59
+ */
60
+ export declare function registerProject(projectName: string, projectDir: string, workflow: string): Promise<void>;
61
+ /**
62
+ * Look up a project by name. Returns null if not found.
63
+ */
64
+ export declare function getProject(projectName: string): Promise<ProjectEntry | null>;
65
+ /**
66
+ * List all registered project names.
67
+ */
68
+ export declare function listProjects(): Promise<string[]>;
69
+ /**
70
+ * Remove a project from the registry (does NOT delete files).
71
+ */
72
+ export declare function unregisterProject(projectName: string): Promise<void>;