@polymorphism-tech/morph-spec 4.8.14 → 4.8.16
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 +2 -2
- package/bin/morph-spec.js +23 -2
- package/bin/task-manager.js +202 -14
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents.json +113 -116
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +48 -2
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +151 -0
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +6 -0
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +6 -0
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +27 -0
- package/framework/hooks/claude-code/stop/validate-completion.js +17 -2
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +87 -0
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +58 -0
- package/framework/hooks/shared/phase-utils.js +1 -1
- package/framework/hooks/shared/state-reader.js +1 -0
- package/framework/skills/README.md +1 -0
- package/framework/skills/level-0-meta/brainstorming/SKILL.md +2 -0
- package/framework/skills/level-0-meta/code-review/SKILL.md +16 -0
- package/framework/skills/level-0-meta/code-review/references/review-guidelines.md +100 -0
- package/framework/skills/level-0-meta/code-review/scripts/scan-csharp.mjs +36 -6
- package/framework/skills/level-0-meta/code-review-nextjs/SKILL.md +16 -0
- package/framework/skills/level-0-meta/code-review-nextjs/scripts/scan-nextjs.mjs +189 -0
- package/framework/skills/level-0-meta/frontend-review/SKILL.md +359 -0
- package/framework/skills/level-0-meta/frontend-review/scripts/scan-accessibility.mjs +376 -0
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +3 -2
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +10 -8
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +70 -0
- package/framework/skills/level-0-meta/post-implementation/SKILL.md +315 -0
- package/framework/skills/level-0-meta/post-implementation/scripts/detect-dev-server.mjs +153 -0
- package/framework/skills/level-0-meta/post-implementation/scripts/detect-stack.mjs +234 -0
- package/framework/skills/level-0-meta/terminal-title/SKILL.md +61 -0
- package/framework/skills/level-0-meta/terminal-title/scripts/set_title.sh +65 -0
- package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +13 -206
- package/framework/skills/level-0-meta/tool-usage-guide/references/tools-per-phase.md +213 -0
- package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +2 -0
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +4 -7
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +16 -110
- package/framework/skills/level-1-workflows/phase-design/references/architecture-analysis-guide.md +89 -0
- package/framework/skills/level-1-workflows/phase-design/references/spec-authoring-guide.md +55 -0
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +153 -118
- package/framework/skills/level-1-workflows/phase-implement/references/vsa-implementation-guide.md +92 -0
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +1 -2
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +11 -158
- package/framework/skills/level-1-workflows/phase-tasks/references/task-planning-patterns.md +172 -0
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +42 -3
- package/framework/squad-templates/backend-only.json +14 -1
- package/framework/squad-templates/frontend-only.json +14 -1
- package/framework/squad-templates/full-stack.json +25 -8
- package/framework/standards/STANDARDS.json +631 -86
- package/framework/standards/frontend/design-system/aesthetic-direction.md +213 -0
- package/framework/templates/project/validate.js +122 -0
- package/framework/workflows/configs/zero-touch.json +7 -0
- package/package.json +1 -1
- package/src/commands/agents/dispatch-agents.js +53 -10
- package/src/commands/state/advance-phase.js +56 -0
- package/src/commands/state/index.js +2 -1
- package/src/commands/state/phase-runner.js +215 -0
- package/src/commands/tasks/task.js +23 -2
- package/src/core/paths/output-schema.js +1 -1
- package/src/lib/generators/recap-generator.js +16 -0
- package/src/lib/orchestration/team-orchestrator.js +171 -89
- package/src/lib/phase-chain/eligibility-checker.js +243 -0
- package/src/lib/standards/digest-builder.js +231 -0
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +39 -0
- package/src/lib/validators/nextjs/next-component-validator.js +2 -0
- package/src/lib/validators/validation-runner.js +2 -2
- package/src/utils/file-copier.js +2 -0
- package/src/utils/hooks-installer.js +31 -7
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard Digest Builder
|
|
3
|
+
*
|
|
4
|
+
* Composes agent briefings from STANDARDS.json digests.
|
|
5
|
+
* Reduces context per agent from ~2000-8000 tokens (file reads)
|
|
6
|
+
* to ~400-600 tokens (digest inline).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* buildAgentBriefing(agentId, phase) → string
|
|
10
|
+
* Injected into dispatch taskPrompt as "\n\nConstraints:\n" + briefing
|
|
11
|
+
*
|
|
12
|
+
* Standards refs in agents.json support two formats:
|
|
13
|
+
* Legacy: "core/coding.md" (treated as scope:'all', priority:'required')
|
|
14
|
+
* Scoped: { id, scope, priority, anchor }
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFileSync, existsSync } from 'fs';
|
|
18
|
+
import { join, dirname } from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
|
|
21
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
|
|
23
|
+
const AGENTS_JSON_PATH = join(__dirname, '../../../framework/agents.json');
|
|
24
|
+
const STANDARDS_JSON_PATH = join(__dirname, '../../../framework/standards/STANDARDS.json');
|
|
25
|
+
|
|
26
|
+
let _agentsCache = null;
|
|
27
|
+
let _standardsCache = null;
|
|
28
|
+
|
|
29
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
+
// Loaders
|
|
31
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
/** Load and cache agents.json */
|
|
34
|
+
function loadAgents() {
|
|
35
|
+
if (_agentsCache) return _agentsCache;
|
|
36
|
+
if (!existsSync(AGENTS_JSON_PATH)) return { agents: {} };
|
|
37
|
+
try {
|
|
38
|
+
_agentsCache = JSON.parse(readFileSync(AGENTS_JSON_PATH, 'utf8'));
|
|
39
|
+
return _agentsCache;
|
|
40
|
+
} catch {
|
|
41
|
+
return { agents: {} };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Load STANDARDS.json and build path/id lookup maps (module-level cache) */
|
|
46
|
+
function loadStandards() {
|
|
47
|
+
if (_standardsCache) return _standardsCache;
|
|
48
|
+
if (!existsSync(STANDARDS_JSON_PATH)) {
|
|
49
|
+
_standardsCache = { byPath: new Map(), byId: new Map() };
|
|
50
|
+
return _standardsCache;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const raw = JSON.parse(readFileSync(STANDARDS_JSON_PATH, 'utf8'));
|
|
54
|
+
const byPath = new Map();
|
|
55
|
+
const byId = new Map();
|
|
56
|
+
for (const entry of raw.standards || []) {
|
|
57
|
+
byPath.set(entry.path, entry);
|
|
58
|
+
byId.set(entry.id, entry);
|
|
59
|
+
}
|
|
60
|
+
_standardsCache = { byPath, byId };
|
|
61
|
+
return _standardsCache;
|
|
62
|
+
} catch {
|
|
63
|
+
_standardsCache = { byPath: new Map(), byId: new Map() };
|
|
64
|
+
return _standardsCache;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
// Helpers
|
|
70
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Normalize a standards reference to canonical shape.
|
|
74
|
+
* Supports legacy string format and new scoped object format.
|
|
75
|
+
*
|
|
76
|
+
* @param {string|Object} ref
|
|
77
|
+
* @returns {{ id?: string, path?: string, scope: string, priority: string, anchor?: string }}
|
|
78
|
+
*/
|
|
79
|
+
function normalizeRef(ref) {
|
|
80
|
+
if (typeof ref === 'string') {
|
|
81
|
+
return { path: ref, scope: 'all', priority: 'required' };
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
id: ref.id,
|
|
85
|
+
path: ref.path,
|
|
86
|
+
scope: ref.scope || 'all',
|
|
87
|
+
priority: ref.priority || 'required',
|
|
88
|
+
anchor: ref.anchor,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve a normalized ref to a STANDARDS.json entry.
|
|
94
|
+
* Tries id lookup first, then exact path, then suffix match for short paths.
|
|
95
|
+
*
|
|
96
|
+
* @param {{ id?: string, path?: string }} ref
|
|
97
|
+
* @param {{ byPath: Map, byId: Map }} standards
|
|
98
|
+
* @returns {Object|null}
|
|
99
|
+
*/
|
|
100
|
+
function resolveEntry(ref, standards) {
|
|
101
|
+
if (ref.id) {
|
|
102
|
+
const entry = standards.byId.get(ref.id);
|
|
103
|
+
if (entry) return entry;
|
|
104
|
+
}
|
|
105
|
+
if (ref.path) {
|
|
106
|
+
const entry = standards.byPath.get(ref.path);
|
|
107
|
+
if (entry) return entry;
|
|
108
|
+
|
|
109
|
+
// Some agents use short paths like "azure.md" — try suffix match
|
|
110
|
+
for (const [, e] of standards.byPath) {
|
|
111
|
+
if (e.path.endsWith('/' + ref.path)) return e;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Format a single standards entry as a briefing line.
|
|
119
|
+
*
|
|
120
|
+
* priority='required' with digest → inline digest text
|
|
121
|
+
* priority='reference' → path + optional anchor only
|
|
122
|
+
* no digest → path reference fallback
|
|
123
|
+
*
|
|
124
|
+
* @param {Object} entry - STANDARDS.json entry
|
|
125
|
+
* @param {string} priority - 'required' | 'reference'
|
|
126
|
+
* @param {string} [anchor] - specific anchor key from entry.anchors
|
|
127
|
+
* @returns {string}
|
|
128
|
+
*/
|
|
129
|
+
function formatBriefingLine(entry, priority, anchor) {
|
|
130
|
+
const anchorFrag = anchor && entry.anchors?.[anchor] ? entry.anchors[anchor] : '';
|
|
131
|
+
|
|
132
|
+
if (priority === 'required' && entry.digest) {
|
|
133
|
+
const ref = anchorFrag ? ` (ref: ${entry.path}${anchorFrag})` : '';
|
|
134
|
+
return `[${entry.name.toUpperCase()}] ${entry.digest}${ref}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (priority === 'reference') {
|
|
138
|
+
return `[REF: ${entry.path}${anchorFrag}]`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// No digest — inject path reference as fallback
|
|
142
|
+
return `[REF: ${entry.path}${anchorFrag}]`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
146
|
+
// Public API
|
|
147
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Build an agent briefing string for injection into taskPrompt.
|
|
151
|
+
* Returns empty string when agent not found, has no standards, or all refs
|
|
152
|
+
* are filtered by scope.
|
|
153
|
+
*
|
|
154
|
+
* @param {string} agentId - Agent ID from agents.json
|
|
155
|
+
* @param {string} phase - Current phase (design|tasks|implement)
|
|
156
|
+
* @returns {string} Formatted briefing or ''
|
|
157
|
+
*/
|
|
158
|
+
export function buildAgentBriefing(agentId, phase) {
|
|
159
|
+
const agents = loadAgents();
|
|
160
|
+
const agentData = agents.agents?.[agentId];
|
|
161
|
+
if (!agentData) return '';
|
|
162
|
+
|
|
163
|
+
const standardsRefs = agentData.standards || [];
|
|
164
|
+
if (standardsRefs.length === 0) return '';
|
|
165
|
+
|
|
166
|
+
const standards = loadStandards();
|
|
167
|
+
const lines = [];
|
|
168
|
+
|
|
169
|
+
for (const rawRef of standardsRefs) {
|
|
170
|
+
const ref = normalizeRef(rawRef);
|
|
171
|
+
|
|
172
|
+
// Scope filtering: skip refs scoped to a different phase
|
|
173
|
+
if (ref.scope !== 'all' && ref.scope !== phase) continue;
|
|
174
|
+
|
|
175
|
+
const entry = resolveEntry(ref, standards);
|
|
176
|
+
if (!entry) {
|
|
177
|
+
// No entry found in registry — emit a path reference as fallback
|
|
178
|
+
const path = ref.path || ref.id;
|
|
179
|
+
if (path) lines.push(`[REF: ${path}]`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const line = formatBriefingLine(entry, ref.priority, ref.anchor);
|
|
184
|
+
if (line) lines.push(line);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return lines.length > 0 ? lines.join('\n') : '';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Build a read-only validation task prompt for Tier-4 validator agents.
|
|
192
|
+
* Uses hook_behavior.validates[] from agents.json.
|
|
193
|
+
*
|
|
194
|
+
* @param {string} agentId - Agent ID (must be tier 4 with hook_behavior)
|
|
195
|
+
* @param {string} [taskId] - Optional task ID being validated
|
|
196
|
+
* @returns {string} Validator task prompt or ''
|
|
197
|
+
*/
|
|
198
|
+
export function buildValidatorPrompt(agentId, taskId) {
|
|
199
|
+
const agents = loadAgents();
|
|
200
|
+
const agentData = agents.agents?.[agentId];
|
|
201
|
+
if (!agentData?.hook_behavior) return '';
|
|
202
|
+
|
|
203
|
+
const validates = agentData.hook_behavior.validates || [];
|
|
204
|
+
const severity = agentData.hook_behavior.severity || 'error';
|
|
205
|
+
const blocksOnFail = agentData.hook_behavior.blocks_on_fail ?? true;
|
|
206
|
+
const taskSuffix = taskId ? ` for task ${taskId}` : '';
|
|
207
|
+
|
|
208
|
+
const lines = [
|
|
209
|
+
`You are ${agentData.title || agentId} (Tier 4 Validator). Mode: READ-ONLY validation${taskSuffix}.`,
|
|
210
|
+
`DO NOT edit any files. Review the current implementation for the following violations:`,
|
|
211
|
+
``,
|
|
212
|
+
`Checks (severity: ${severity}):`,
|
|
213
|
+
...validates.map(v => ` - ${v}`),
|
|
214
|
+
``,
|
|
215
|
+
`Output a single JSON line: { "passed": boolean, "issues": [{ "file": "path/to/file", "line": 0, "message": "description", "rule": "rule-name" }] }`,
|
|
216
|
+
blocksOnFail
|
|
217
|
+
? `BLOCKING: if any issues found, implementation must be fixed before task can complete.`
|
|
218
|
+
: `NON-BLOCKING: issues are warnings only — task completion is not blocked.`,
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
return lines.join('\n');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Invalidate module-level caches.
|
|
226
|
+
* Used in tests to ensure a fresh load of agents.json / STANDARDS.json.
|
|
227
|
+
*/
|
|
228
|
+
export function clearCache() {
|
|
229
|
+
_agentsCache = null;
|
|
230
|
+
_standardsCache = null;
|
|
231
|
+
}
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* @module blazor-concurrency-analyzer
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
import { glob } from 'glob';
|
|
10
12
|
import { countIssues } from '../shared/index.js';
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -174,6 +176,9 @@ export function checkScopedInSingleton(content, filePath) {
|
|
|
174
176
|
const issues = [];
|
|
175
177
|
const lines = content.split('\n');
|
|
176
178
|
|
|
179
|
+
// Type guard: filePath must be a string (validation-runner may pass options object)
|
|
180
|
+
if (typeof filePath !== 'string') return [];
|
|
181
|
+
|
|
177
182
|
// Check if class might be a singleton (common naming patterns)
|
|
178
183
|
const isSingletonCandidate =
|
|
179
184
|
filePath.includes('BackgroundService') ||
|
|
@@ -267,8 +272,42 @@ export function analyzeConcurrency(content, filePath) {
|
|
|
267
272
|
}
|
|
268
273
|
|
|
269
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Project-level concurrency validator. Scans all .cs files in projectPath.
|
|
277
|
+
* Follows the same interface as other project-level validators (validateNextComponentFiles, etc.)
|
|
278
|
+
*
|
|
279
|
+
* @param {string} projectPath - Project root path
|
|
280
|
+
* @param {Object} [_options]
|
|
281
|
+
* @returns {Promise<{errors: number, warnings: number, issues: Array}>}
|
|
282
|
+
*/
|
|
283
|
+
export async function validateConcurrencyFiles(projectPath, _options = {}) {
|
|
284
|
+
const result = { errors: 0, warnings: 0, issues: [] };
|
|
285
|
+
const pattern = projectPath.replace(/\\/g, '/') + '/**/*.cs';
|
|
286
|
+
const files = await glob(pattern, { ignore: ['**/node_modules/**', '**/obj/**', '**/bin/**'] });
|
|
287
|
+
|
|
288
|
+
for (const filePath of files) {
|
|
289
|
+
const content = readFileSync(filePath, 'utf8');
|
|
290
|
+
const relPath = filePath.replace(projectPath.replace(/\\/g, '/') + '/', '');
|
|
291
|
+
const fileIssues = analyzeConcurrency(content, relPath);
|
|
292
|
+
for (const issue of fileIssues) {
|
|
293
|
+
result.issues.push({
|
|
294
|
+
level: issue.type,
|
|
295
|
+
message: issue.message,
|
|
296
|
+
file: issue.file,
|
|
297
|
+
line: issue.line,
|
|
298
|
+
solution: issue.suggestion,
|
|
299
|
+
});
|
|
300
|
+
if (issue.type === 'error') result.errors++;
|
|
301
|
+
else result.warnings++;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
|
|
270
308
|
export default {
|
|
271
309
|
analyzeConcurrency,
|
|
310
|
+
validateConcurrencyFiles,
|
|
272
311
|
checkScopedDbContextInBackground,
|
|
273
312
|
checkHangfireWithScopedServices,
|
|
274
313
|
checkScopedInSingleton,
|
|
@@ -86,7 +86,9 @@ export function validateNextComponent(content, filePath) {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// Check 2: hooks used without use client
|
|
89
|
+
// .ts files (custom hooks, utilities) never render JSX — skip this check
|
|
89
90
|
if (!hasUseClient) {
|
|
91
|
+
if (!filePath.endsWith('.tsx')) return issues;
|
|
90
92
|
const usedHooks = CLIENT_HOOKS.filter(hook => new RegExp(`\\b${hook}\\b`).test(content));
|
|
91
93
|
if (usedHooks.length > 0) {
|
|
92
94
|
issues.push({
|
|
@@ -202,8 +202,8 @@ async function runSingleValidator(validatorId, projectPath, featureName, options
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
case 'blazor-concurrency': {
|
|
205
|
-
const {
|
|
206
|
-
return await
|
|
205
|
+
const { validateConcurrencyFiles } = await import('./blazor/blazor-concurrency-analyzer.js');
|
|
206
|
+
return await validateConcurrencyFiles(projectPath, options);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
case 'blazor-state': {
|
package/src/utils/file-copier.js
CHANGED
|
@@ -136,6 +136,7 @@ export async function updateGitignore(projectPath) {
|
|
|
136
136
|
'# MORPH-SPEC',
|
|
137
137
|
'.morph/framework/',
|
|
138
138
|
'.morph/memory/',
|
|
139
|
+
'.morph/plans/',
|
|
139
140
|
'.morph/analytics/',
|
|
140
141
|
'.claude/commands/',
|
|
141
142
|
'.claude/rules/',
|
|
@@ -143,6 +144,7 @@ export async function updateGitignore(projectPath) {
|
|
|
143
144
|
'.claude/agents/',
|
|
144
145
|
'.claude/CLAUDE.md',
|
|
145
146
|
'.claude/settings.local.json',
|
|
147
|
+
'.mcp.json',
|
|
146
148
|
''
|
|
147
149
|
];
|
|
148
150
|
|
|
@@ -15,7 +15,7 @@ import { homedir } from 'os';
|
|
|
15
15
|
import { execSync } from 'child_process';
|
|
16
16
|
|
|
17
17
|
/** Current hooks schema version — bump when hook definitions change */
|
|
18
|
-
const HOOKS_VERSION = '2.
|
|
18
|
+
const HOOKS_VERSION = '2.6.0';
|
|
19
19
|
|
|
20
20
|
/** Marker for old dispatch.js (v1) */
|
|
21
21
|
const OLD_DISPATCH_COMMAND = 'node framework/hooks/agent-teams/dispatch.js';
|
|
@@ -67,10 +67,16 @@ const MORPH_HOOKS = [
|
|
|
67
67
|
{
|
|
68
68
|
event: 'UserPromptSubmit',
|
|
69
69
|
matcher: null,
|
|
70
|
-
hooks: [
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
hooks: [
|
|
71
|
+
{
|
|
72
|
+
type: 'command',
|
|
73
|
+
command: 'node framework/hooks/claude-code/user-prompt/enrich-prompt.js'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
type: 'command',
|
|
77
|
+
command: 'node framework/hooks/claude-code/user-prompt/set-terminal-title.js'
|
|
78
|
+
}
|
|
79
|
+
]
|
|
74
80
|
},
|
|
75
81
|
|
|
76
82
|
// === PreToolUse: Write|Edit ===
|
|
@@ -155,6 +161,20 @@ Otherwise respond: {"ok": true}`
|
|
|
155
161
|
type: 'command',
|
|
156
162
|
command: 'node framework/hooks/claude-code/notification/approval-reminder.js'
|
|
157
163
|
}]
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
// === TeammateIdle ===
|
|
167
|
+
// Fires when an Agent Team teammate finishes and is about to go idle.
|
|
168
|
+
// Exit 2 = send feedback to teammate to continue working (validation failed).
|
|
169
|
+
// Exit 0 = teammate may become idle (validation passed).
|
|
170
|
+
// Requires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 (set in env above).
|
|
171
|
+
{
|
|
172
|
+
event: 'TeammateIdle',
|
|
173
|
+
matcher: null,
|
|
174
|
+
hooks: [{
|
|
175
|
+
type: 'command',
|
|
176
|
+
command: 'node framework/hooks/claude-code/teammate-idle/teammate-idle.js'
|
|
177
|
+
}]
|
|
158
178
|
}
|
|
159
179
|
];
|
|
160
180
|
|
|
@@ -241,8 +261,12 @@ export async function installClaudeHooks(targetPath) {
|
|
|
241
261
|
// JSON Schema for IDE auto-complete and validation
|
|
242
262
|
settings['$schema'] = 'https://json.schemastore.org/claude-code-settings.json';
|
|
243
263
|
|
|
244
|
-
// Environment variables — always set MORPH_SPEC_ACTIVE, preserve user additions
|
|
245
|
-
settings.env = {
|
|
264
|
+
// Environment variables — always set MORPH_SPEC_ACTIVE + Agent Teams flag, preserve user additions
|
|
265
|
+
settings.env = {
|
|
266
|
+
...settings.env,
|
|
267
|
+
MORPH_SPEC_ACTIVE: 'true',
|
|
268
|
+
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: '1',
|
|
269
|
+
};
|
|
246
270
|
|
|
247
271
|
// Attribution — only set if not already customized by user
|
|
248
272
|
if (!settings.attribution) {
|