@polymorphism-tech/morph-spec 7.9.4 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +2 -2
- package/README.md +31 -2
- package/bin/morph-spec.js +50 -2
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +38 -1
- package/docs/QUICKSTART.md +60 -1
- package/framework/CLAUDE.md +14 -3
- package/framework/MORPH.md +41 -5
- package/framework/agents.json +22 -3
- package/framework/commands/commit.md +166 -166
- package/framework/commands/morph-hotfix.md +39 -0
- package/framework/commands/morph-infra.md +215 -215
- package/framework/commands/morph-preflight.md +205 -205
- package/framework/commands/morph-troubleshoot.md +124 -124
- package/framework/hooks/README.md +25 -11
- package/framework/hooks/claude-code/core/gate-guard.js +4 -2
- package/framework/hooks/claude-code/core/loop-detect.js +2 -1
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +14 -5
- package/framework/hooks/claude-code/core/tasks-json-guard.js +4 -2
- package/framework/hooks/claude-code/core/validate-completion.js +1 -1
- package/framework/hooks/claude-code/core/worktree-context.js +160 -0
- package/framework/hooks/claude-code/statusline.py +679 -679
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/activity-logger.js +160 -160
- package/framework/hooks/shared/build-check.js +2 -2
- package/framework/hooks/shared/hook-response.js +17 -5
- package/framework/hooks/shared/payload-utils.js +42 -0
- package/framework/hooks/shared/state-reader.js +22 -2
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/rubrics/backend.json +41 -41
- package/framework/rules/ai-agents-standards.md +45 -45
- package/framework/rules/dotnet-templates.md +86 -86
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/infrastructure-standards.md +27 -27
- package/framework/rules/neon-standards.md +25 -25
- package/framework/rules/nextjs-standards.md +20 -20
- package/framework/rules/nextjs-templates.md +40 -40
- package/framework/schemas/feature.schema.json +95 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +49 -27
- package/framework/skills/core/morph-brainstorming/SKILL.md +9 -1
- package/framework/skills/core/morph-eval/SKILL.md +6 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +134 -0
- package/framework/skills/core/morph-implement/SKILL.md +17 -22
- package/framework/skills/core/morph-plan/SKILL.md +5 -1
- package/framework/skills/core/morph-proposal/SKILL.md +24 -1
- package/framework/skills/core/morph-review/SKILL.md +13 -16
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/standards/ai-agents/agent-session.md +160 -160
- package/framework/standards/ai-agents/batching.md +193 -193
- package/framework/standards/ai-agents/context-providers.md +188 -188
- package/framework/standards/ai-agents/mcp-server.md +250 -250
- package/framework/standards/ai-agents/mcp-tools.md +197 -197
- package/framework/standards/ai-agents/middleware-patterns.md +207 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
- package/framework/standards/ai-agents/modalities-vision.md +201 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
- package/framework/standards/ai-agents/production.md +275 -275
- package/framework/standards/ai-agents/prompts-in-db.md +214 -214
- package/framework/standards/ai-agents/providers/model-registry.md +158 -158
- package/framework/standards/ai-agents/service-tier-flex.md +135 -135
- package/framework/standards/ai-agents/structured-output.md +249 -249
- package/framework/standards/ai-agents/sweet-spot.md +109 -109
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +0 -62
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- package/framework/templates/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/index.js +13 -13
- package/src/commands/mcp/mcp-setup.js +519 -519
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +4 -2
- package/src/commands/project/init.js +46 -2
- package/src/commands/project/update.js +5 -3
- package/src/commands/project/worktree.js +1005 -46
- package/src/commands/state/advance.js +17 -2
- package/src/commands/state/approve.js +18 -1
- package/src/commands/state/create.js +158 -14
- package/src/commands/state/retype.js +126 -0
- package/src/commands/state/score.js +4 -0
- package/src/commands/tasks/dag.js +32 -0
- package/src/commands/validation/index.js +1 -0
- package/src/commands/validation/validate-feature.js +33 -5
- package/src/commands/validation/verify.js +190 -0
- package/src/core/state/phase-state-machine.js +197 -197
- package/src/core/state/state-manager.js +35 -10
- package/src/core/templates/template-renderer.js +477 -477
- package/src/core/work-types.js +79 -0
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/HookInspector.jsx +418 -418
- package/src/dashboard/app/pages/LiveState.jsx +11 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/hooks-scanner.js +41 -41
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/scanner/standards-scanner.js +26 -26
- package/src/dashboard/scanner/state-scanner.js +42 -0
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/agents/metrics-aggregator.js +56 -56
- package/src/lib/agents/metrics-calculator.js +125 -125
- package/src/lib/agents/output-collector.js +73 -73
- package/src/lib/detectors/claude-config-detector.js +152 -152
- package/src/lib/detectors/design-system-detector.js +231 -231
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/generators/architecture-scanner.js +639 -639
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/index.js +13 -13
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/mcp-installer.js +719 -719
- package/src/lib/installers/memory-installer.js +1 -1
- package/src/lib/risk-detector.js +16 -0
- package/src/lib/tasks/dispatch-planner.js +122 -4
- package/src/lib/tasks/mermaid-renderer.js +132 -0
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/test-runner.js +27 -6
- package/src/lib/validators/architecture/architecture-validator.js +259 -259
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/packages/package-validator.js +347 -347
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/validation-runner.js +349 -349
- package/src/lib/validators/verify-runner.js +322 -0
- package/src/lib/work-type-detector.js +143 -0
- package/src/scripts/setup-infra.js +32 -5
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +10 -6
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/file-copier.js +43 -0
- package/src/utils/hooks-installer.js +43 -34
- package/src/utils/skills-installer.js +174 -174
- package/framework/hooks/claude-code/core/subagent-report-guard.js +0 -95
- package/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +0 -113
- package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +0 -80
- package/src/lib/tasks/wave-resolver.js +0 -353
|
@@ -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 = '3.
|
|
18
|
+
const HOOKS_VERSION = '3.3.0';
|
|
19
19
|
|
|
20
20
|
/** Marker for old dispatch.js (v1) */
|
|
21
21
|
const OLD_DISPATCH_COMMAND = 'node framework/hooks/agent-teams/dispatch.js';
|
|
@@ -26,22 +26,24 @@ const OLD_DISPATCH_COMMAND = 'node framework/hooks/agent-teams/dispatch.js';
|
|
|
26
26
|
* state.json is NOT in deny because the agent edits it directly as a
|
|
27
27
|
* fallback to the state-sync hook (per v7 hybrid state model). The
|
|
28
28
|
* framework/ tree remains read-only — only the installer touches it.
|
|
29
|
+
*
|
|
30
|
+
* `Edit(path)` ONLY — never a matching `Write(path)`. Claude Code matches file
|
|
31
|
+
* permissions on `Edit(path)` rules alone; an `Edit` rule covers every
|
|
32
|
+
* file-editing tool, Write included, while a `Write(path)` rule matches nothing
|
|
33
|
+
* and is reported as a warning on every session start. Verified empirically, not
|
|
34
|
+
* assumed: with `Edit(.morph/framework/**)` as the ONLY deny entry, the Write
|
|
35
|
+
* tool was refused and the target file was never created.
|
|
29
36
|
*/
|
|
30
37
|
const MORPH_DENY = [
|
|
31
38
|
// Framework files — installer-managed, immutable at runtime
|
|
32
|
-
'Write(.morph/framework/**)',
|
|
33
39
|
'Edit(.morph/framework/**)',
|
|
34
40
|
// .claude/CLAUDE.md (managed copy — source is framework/CLAUDE.md)
|
|
35
|
-
'Write(.claude/CLAUDE.md)',
|
|
36
41
|
'Edit(.claude/CLAUDE.md)',
|
|
37
42
|
// .claude/rules/ (copied from framework/rules/)
|
|
38
|
-
'Write(.claude/rules/**)',
|
|
39
43
|
'Edit(.claude/rules/**)',
|
|
40
44
|
// .claude/agents/morph-* (generated by agents-installer)
|
|
41
|
-
'Write(.claude/agents/morph-*)',
|
|
42
45
|
'Edit(.claude/agents/morph-*)',
|
|
43
46
|
// .claude/skills/ (copied from framework/skills/)
|
|
44
|
-
'Write(.claude/skills/**)',
|
|
45
47
|
'Edit(.claude/skills/**)',
|
|
46
48
|
];
|
|
47
49
|
|
|
@@ -51,15 +53,16 @@ const MORPH_DENY = [
|
|
|
51
53
|
* Pre-authorizes operations the agent legitimately performs during the
|
|
52
54
|
* morph-spec flow, so the user does not get permission prompts on every
|
|
53
55
|
* proposal.md / spec.md / tasks.json write.
|
|
56
|
+
*
|
|
57
|
+
* `Edit(path)` ONLY, for the same reason as MORPH_DENY: the `Edit` rule already
|
|
58
|
+
* covers Write, and a `Write(path)` rule is inert noise.
|
|
54
59
|
*/
|
|
55
60
|
const MORPH_ALLOW = [
|
|
56
61
|
// Feature artifacts — the agent's working directory during proposal/plan/implement/review
|
|
57
|
-
'Write(.morph/features/**)',
|
|
58
62
|
'Edit(.morph/features/**)',
|
|
59
63
|
// state.json — fallback path when state-sync hook can't cover a case
|
|
60
64
|
'Edit(.morph/state.json)',
|
|
61
65
|
// Long-lived architectural context — agent may reconcile during brainstorming
|
|
62
|
-
'Write(.morph/context/**)',
|
|
63
66
|
'Edit(.morph/context/**)',
|
|
64
67
|
// Folder creation under .morph
|
|
65
68
|
'Bash(mkdir:.morph/**)',
|
|
@@ -74,6 +77,19 @@ const MORPH_PERMISSIONS = MORPH_DENY;
|
|
|
74
77
|
* Each entry maps to a Claude Code hook event.
|
|
75
78
|
*/
|
|
76
79
|
const MORPH_HOOKS = [
|
|
80
|
+
// === SessionStart — worktree feature identity (secondary worktree) or
|
|
81
|
+
// active-feature summary (primary root). Fulfills the SessionStart
|
|
82
|
+
// injection role long documented but never wired (inject-morph-context.js). ===
|
|
83
|
+
{
|
|
84
|
+
event: 'SessionStart',
|
|
85
|
+
matcher: null,
|
|
86
|
+
hooks: [{
|
|
87
|
+
type: 'command',
|
|
88
|
+
command: 'node framework/hooks/claude-code/core/worktree-context.js',
|
|
89
|
+
timeout: 5000
|
|
90
|
+
}]
|
|
91
|
+
},
|
|
92
|
+
|
|
77
93
|
// === PreToolUse: Write|Edit — phase + spec protection (silent on success) ===
|
|
78
94
|
{
|
|
79
95
|
event: 'PreToolUse',
|
|
@@ -160,34 +176,13 @@ const MORPH_HOOKS = [
|
|
|
160
176
|
}]
|
|
161
177
|
},
|
|
162
178
|
|
|
163
|
-
// === Stop — build check + session snapshot
|
|
164
|
-
// subagent-report-guard blocks (once) a dispatched sub-agent stopping
|
|
165
|
-
// without SendMessage — clause 1 of the dispatch protocol. In the
|
|
166
|
-
// orchestrator's session it is inert (marker never in a user message).
|
|
179
|
+
// === Stop — build check + session snapshot ===
|
|
167
180
|
{
|
|
168
181
|
event: 'Stop',
|
|
169
182
|
matcher: null,
|
|
170
|
-
hooks: [
|
|
171
|
-
{
|
|
172
|
-
type: 'command',
|
|
173
|
-
command: 'node framework/hooks/claude-code/core/validate-completion.js'
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
type: 'command',
|
|
177
|
-
command: 'node framework/hooks/claude-code/core/subagent-report-guard.js',
|
|
178
|
-
timeout: 5000
|
|
179
|
-
}
|
|
180
|
-
]
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
// === SubagentStop — dispatch-report guard for Task-tool subagents ===
|
|
184
|
-
{
|
|
185
|
-
event: 'SubagentStop',
|
|
186
|
-
matcher: null,
|
|
187
183
|
hooks: [{
|
|
188
184
|
type: 'command',
|
|
189
|
-
command: 'node framework/hooks/claude-code/core/
|
|
190
|
-
timeout: 5000
|
|
185
|
+
command: 'node framework/hooks/claude-code/core/validate-completion.js'
|
|
191
186
|
}]
|
|
192
187
|
}
|
|
193
188
|
];
|
|
@@ -266,11 +261,21 @@ export async function installClaudeHooks(targetPath) {
|
|
|
266
261
|
settings.permissions.deny = settings.permissions.deny || [];
|
|
267
262
|
settings.permissions.allow = settings.permissions.allow || [];
|
|
268
263
|
|
|
269
|
-
// Stale-entry cleanup: remove
|
|
270
|
-
//
|
|
264
|
+
// Stale-entry cleanup: remove morph-managed entries we no longer install.
|
|
265
|
+
// The filters below drop `MORPH_*.includes(p)` before re-adding, so an entry
|
|
266
|
+
// that LEFT those lists would otherwise survive forever in an existing
|
|
267
|
+
// install — the upgrade path has to name it explicitly to reclaim it.
|
|
268
|
+
// - state.json: was in deny pre-v7, now lives in allow.
|
|
269
|
+
// - Write(...): matched nothing and made Claude Code warn on every session
|
|
270
|
+
// start; the paired Edit(...) rule is what actually enforced them.
|
|
271
271
|
const STALE_DENY = [
|
|
272
272
|
'Write(.morph/state.json)',
|
|
273
273
|
'Edit(.morph/state.json)',
|
|
274
|
+
'Write(.morph/framework/**)',
|
|
275
|
+
'Write(.claude/CLAUDE.md)',
|
|
276
|
+
'Write(.claude/rules/**)',
|
|
277
|
+
'Write(.claude/agents/morph-*)',
|
|
278
|
+
'Write(.claude/skills/**)',
|
|
274
279
|
];
|
|
275
280
|
settings.permissions.deny = settings.permissions.deny.filter(
|
|
276
281
|
p => !MORPH_DENY.includes(p) && !STALE_DENY.includes(p)
|
|
@@ -279,8 +284,12 @@ export async function installClaudeHooks(targetPath) {
|
|
|
279
284
|
settings.permissions.deny.push(perm);
|
|
280
285
|
}
|
|
281
286
|
|
|
287
|
+
const STALE_ALLOW = [
|
|
288
|
+
'Write(.morph/features/**)',
|
|
289
|
+
'Write(.morph/context/**)',
|
|
290
|
+
];
|
|
282
291
|
settings.permissions.allow = settings.permissions.allow.filter(
|
|
283
|
-
p => !MORPH_ALLOW.includes(p)
|
|
292
|
+
p => !MORPH_ALLOW.includes(p) && !STALE_ALLOW.includes(p)
|
|
284
293
|
);
|
|
285
294
|
for (const perm of MORPH_ALLOW) {
|
|
286
295
|
settings.permissions.allow.push(perm);
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skills Installer
|
|
3
|
-
*
|
|
4
|
-
* Copies framework skills to .claude/skills/ during morph-spec init.
|
|
5
|
-
* Installs only the `core/` skill set — the canonical NLH skills referenced by
|
|
6
|
-
* MORPH.md (morph-brainstorming, morph-stack-scan, morph-plan, morph-implement,
|
|
7
|
-
* morph-eval, morph-review, morph-standards). The 3-tier system was collapsed
|
|
8
|
-
* into a single source of truth in framework/skills/core/.
|
|
9
|
-
*
|
|
10
|
-
* Source skills are directory-based: framework/skills/core/{skill-name}/SKILL.md
|
|
11
|
-
* (plus optional scripts/, references/, assets/ subdirectories).
|
|
12
|
-
*
|
|
13
|
-
* Directory-based skills have their entire directory copied so Claude Code can
|
|
14
|
-
* execute scripts and load references on demand.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { mkdirSync, copyFileSync, existsSync, readdirSync, statSync, readFileSync, rmSync } from 'fs';
|
|
18
|
-
import { join } from 'path';
|
|
19
|
-
import { fileURLToPath } from 'url';
|
|
20
|
-
import { parseStacks, shouldInstall } from '../lib/stack-filter.js';
|
|
21
|
-
|
|
22
|
-
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
23
|
-
const FRAMEWORK_SKILLS_DIR = join(__dirname, '..', '..', 'framework', 'skills');
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Skill levels to install in .claude/skills/.
|
|
27
|
-
* Only `core/` survives — single source of truth aligned with MORPH.md.
|
|
28
|
-
*/
|
|
29
|
-
const SKILL_LEVELS_TO_INSTALL = ['core'];
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Check if a skill should be installed based on its stacks: frontmatter.
|
|
33
|
-
* @param {string} skillContent - Raw SKILL.md content
|
|
34
|
-
* @param {string[]} projectTags - Stack tags from config
|
|
35
|
-
* @returns {boolean}
|
|
36
|
-
*/
|
|
37
|
-
export function shouldInstallSkill(skillContent, projectTags) {
|
|
38
|
-
return shouldInstall(parseStacks(skillContent), projectTags);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Recursively copy a directory tree from src to dest.
|
|
43
|
-
* Creates dest if it doesn't exist. Skips README.md files.
|
|
44
|
-
*
|
|
45
|
-
* @param {string} src - Source directory path
|
|
46
|
-
* @param {string} dest - Destination directory path
|
|
47
|
-
*/
|
|
48
|
-
function copyDirectory(src, dest) {
|
|
49
|
-
mkdirSync(dest, { recursive: true });
|
|
50
|
-
for (const entry of readdirSync(src)) {
|
|
51
|
-
if (entry === 'README.md') continue;
|
|
52
|
-
const srcPath = join(src, entry);
|
|
53
|
-
const destPath = join(dest, entry);
|
|
54
|
-
if (statSync(srcPath).isDirectory()) {
|
|
55
|
-
copyDirectory(srcPath, destPath);
|
|
56
|
-
} else {
|
|
57
|
-
copyFileSync(srcPath, destPath);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Walk srcDir and install skills into destDir.
|
|
64
|
-
*
|
|
65
|
-
* Handles two source formats:
|
|
66
|
-
* 1. Directory-based skill: srcDir/{skill-name}/SKILL.md
|
|
67
|
-
* → copies entire directory (SKILL.md + scripts/ + references/ + assets/)
|
|
68
|
-
* → destDir/{skill-name}/
|
|
69
|
-
*
|
|
70
|
-
* 2. Flat .md skill (legacy): srcDir/{skill-name}.md
|
|
71
|
-
* → destDir/{skill-name}/SKILL.md
|
|
72
|
-
*
|
|
73
|
-
* The `core/` level directory is traversed recursively. A directory is
|
|
74
|
-
* recognized as a skill directory when it contains a SKILL.md file.
|
|
75
|
-
*
|
|
76
|
-
* @param {string} srcDir - Source directory to walk
|
|
77
|
-
* @param {string} destDir - Destination base directory (.claude/skills/)
|
|
78
|
-
*/
|
|
79
|
-
function installSkillsFromDir(srcDir, destDir, projectTags) {
|
|
80
|
-
const entries = readdirSync(srcDir);
|
|
81
|
-
for (const entry of entries) {
|
|
82
|
-
const srcPath = join(srcDir, entry);
|
|
83
|
-
const stat = statSync(srcPath);
|
|
84
|
-
|
|
85
|
-
if (stat.isDirectory()) {
|
|
86
|
-
const skillMdPath = join(srcPath, 'SKILL.md');
|
|
87
|
-
if (existsSync(skillMdPath)) {
|
|
88
|
-
// Directory-based skill — check stack filter before copying
|
|
89
|
-
const content = readFileSync(skillMdPath, 'utf-8');
|
|
90
|
-
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
91
|
-
const skillDestDir = join(destDir, entry);
|
|
92
|
-
copyDirectory(srcPath, skillDestDir);
|
|
93
|
-
} else {
|
|
94
|
-
// Level/category directory — recurse
|
|
95
|
-
installSkillsFromDir(srcPath, destDir, projectTags);
|
|
96
|
-
}
|
|
97
|
-
} else if (entry.endsWith('.md') && entry !== 'README.md') {
|
|
98
|
-
// Legacy flat .md skill — check stack filter before copying
|
|
99
|
-
const content = readFileSync(srcPath, 'utf-8');
|
|
100
|
-
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
101
|
-
const skillName = entry.slice(0, -3);
|
|
102
|
-
const skillDir = join(destDir, skillName);
|
|
103
|
-
mkdirSync(skillDir, { recursive: true });
|
|
104
|
-
copyFileSync(srcPath, join(skillDir, 'SKILL.md'));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function collectSourceSkillNames() {
|
|
110
|
-
const names = new Set();
|
|
111
|
-
for (const level of SKILL_LEVELS_TO_INSTALL) {
|
|
112
|
-
const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
|
|
113
|
-
if (!existsSync(levelDir)) continue;
|
|
114
|
-
collectNamesFromDir(levelDir, names);
|
|
115
|
-
}
|
|
116
|
-
return names;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function collectNamesFromDir(dir, names) {
|
|
120
|
-
for (const entry of readdirSync(dir)) {
|
|
121
|
-
const fullPath = join(dir, entry);
|
|
122
|
-
if (!statSync(fullPath).isDirectory()) continue;
|
|
123
|
-
if (existsSync(join(fullPath, 'SKILL.md'))) {
|
|
124
|
-
names.add(entry);
|
|
125
|
-
} else {
|
|
126
|
-
collectNamesFromDir(fullPath, names);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Install morph framework skills to .claude/skills/ in the target project.
|
|
133
|
-
* Skills are copied into subdirectories as SKILL.md files so Claude Code can
|
|
134
|
-
* discover them natively via /<skill-name> syntax (e.g., /phase-design,
|
|
135
|
-
* /nextjs-expert). Directory-based skills also get their scripts/,
|
|
136
|
-
* references/, and assets/ subdirectories copied for on-demand loading.
|
|
137
|
-
*
|
|
138
|
-
* Stack filtering: if projectTags is provided, only skills whose `stacks:`
|
|
139
|
-
* frontmatter matches the project tags are installed. Skills without a
|
|
140
|
-
* `stacks:` field are always installed (universal).
|
|
141
|
-
*
|
|
142
|
-
* @param {string} projectDir - Target project root directory
|
|
143
|
-
* @param {Object} [options]
|
|
144
|
-
* @param {string[]} [options.projectTags=[]] - Stack tags from config
|
|
145
|
-
*/
|
|
146
|
-
export async function installSkills(projectDir, { projectTags = [] } = {}) {
|
|
147
|
-
const claudeSkillsDir = join(projectDir, '.claude', 'skills');
|
|
148
|
-
mkdirSync(claudeSkillsDir, { recursive: true });
|
|
149
|
-
|
|
150
|
-
for (const level of SKILL_LEVELS_TO_INSTALL) {
|
|
151
|
-
const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
|
|
152
|
-
if (!existsSync(levelDir)) continue;
|
|
153
|
-
installSkillsFromDir(levelDir, claudeSkillsDir, projectTags);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Collect installed morph-* skill names
|
|
157
|
-
const installed = new Set();
|
|
158
|
-
for (const entry of readdirSync(claudeSkillsDir)) {
|
|
159
|
-
if (existsSync(join(claudeSkillsDir, entry, 'SKILL.md'))) {
|
|
160
|
-
installed.add(entry);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Cleanup orphan morph-* skill dirs that exist in source but were filtered out
|
|
165
|
-
const allSourceSkills = collectSourceSkillNames();
|
|
166
|
-
for (const entry of readdirSync(claudeSkillsDir)) {
|
|
167
|
-
if (!entry.startsWith('morph-')) continue;
|
|
168
|
-
if (!installed.has(entry) && allSourceSkills.has(entry)) {
|
|
169
|
-
rmSync(join(claudeSkillsDir, entry), { recursive: true, force: true });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return { installed: installed.size };
|
|
174
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Skills Installer
|
|
3
|
+
*
|
|
4
|
+
* Copies framework skills to .claude/skills/ during morph-spec init.
|
|
5
|
+
* Installs only the `core/` skill set — the canonical NLH skills referenced by
|
|
6
|
+
* MORPH.md (morph-brainstorming, morph-stack-scan, morph-plan, morph-implement,
|
|
7
|
+
* morph-eval, morph-review, morph-standards). The 3-tier system was collapsed
|
|
8
|
+
* into a single source of truth in framework/skills/core/.
|
|
9
|
+
*
|
|
10
|
+
* Source skills are directory-based: framework/skills/core/{skill-name}/SKILL.md
|
|
11
|
+
* (plus optional scripts/, references/, assets/ subdirectories).
|
|
12
|
+
*
|
|
13
|
+
* Directory-based skills have their entire directory copied so Claude Code can
|
|
14
|
+
* execute scripts and load references on demand.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { mkdirSync, copyFileSync, existsSync, readdirSync, statSync, readFileSync, rmSync } from 'fs';
|
|
18
|
+
import { join } from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import { parseStacks, shouldInstall } from '../lib/stack-filter.js';
|
|
21
|
+
|
|
22
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
23
|
+
const FRAMEWORK_SKILLS_DIR = join(__dirname, '..', '..', 'framework', 'skills');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Skill levels to install in .claude/skills/.
|
|
27
|
+
* Only `core/` survives — single source of truth aligned with MORPH.md.
|
|
28
|
+
*/
|
|
29
|
+
const SKILL_LEVELS_TO_INSTALL = ['core'];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a skill should be installed based on its stacks: frontmatter.
|
|
33
|
+
* @param {string} skillContent - Raw SKILL.md content
|
|
34
|
+
* @param {string[]} projectTags - Stack tags from config
|
|
35
|
+
* @returns {boolean}
|
|
36
|
+
*/
|
|
37
|
+
export function shouldInstallSkill(skillContent, projectTags) {
|
|
38
|
+
return shouldInstall(parseStacks(skillContent), projectTags);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Recursively copy a directory tree from src to dest.
|
|
43
|
+
* Creates dest if it doesn't exist. Skips README.md files.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} src - Source directory path
|
|
46
|
+
* @param {string} dest - Destination directory path
|
|
47
|
+
*/
|
|
48
|
+
function copyDirectory(src, dest) {
|
|
49
|
+
mkdirSync(dest, { recursive: true });
|
|
50
|
+
for (const entry of readdirSync(src)) {
|
|
51
|
+
if (entry === 'README.md') continue;
|
|
52
|
+
const srcPath = join(src, entry);
|
|
53
|
+
const destPath = join(dest, entry);
|
|
54
|
+
if (statSync(srcPath).isDirectory()) {
|
|
55
|
+
copyDirectory(srcPath, destPath);
|
|
56
|
+
} else {
|
|
57
|
+
copyFileSync(srcPath, destPath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Walk srcDir and install skills into destDir.
|
|
64
|
+
*
|
|
65
|
+
* Handles two source formats:
|
|
66
|
+
* 1. Directory-based skill: srcDir/{skill-name}/SKILL.md
|
|
67
|
+
* → copies entire directory (SKILL.md + scripts/ + references/ + assets/)
|
|
68
|
+
* → destDir/{skill-name}/
|
|
69
|
+
*
|
|
70
|
+
* 2. Flat .md skill (legacy): srcDir/{skill-name}.md
|
|
71
|
+
* → destDir/{skill-name}/SKILL.md
|
|
72
|
+
*
|
|
73
|
+
* The `core/` level directory is traversed recursively. A directory is
|
|
74
|
+
* recognized as a skill directory when it contains a SKILL.md file.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} srcDir - Source directory to walk
|
|
77
|
+
* @param {string} destDir - Destination base directory (.claude/skills/)
|
|
78
|
+
*/
|
|
79
|
+
function installSkillsFromDir(srcDir, destDir, projectTags) {
|
|
80
|
+
const entries = readdirSync(srcDir);
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
const srcPath = join(srcDir, entry);
|
|
83
|
+
const stat = statSync(srcPath);
|
|
84
|
+
|
|
85
|
+
if (stat.isDirectory()) {
|
|
86
|
+
const skillMdPath = join(srcPath, 'SKILL.md');
|
|
87
|
+
if (existsSync(skillMdPath)) {
|
|
88
|
+
// Directory-based skill — check stack filter before copying
|
|
89
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
90
|
+
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
91
|
+
const skillDestDir = join(destDir, entry);
|
|
92
|
+
copyDirectory(srcPath, skillDestDir);
|
|
93
|
+
} else {
|
|
94
|
+
// Level/category directory — recurse
|
|
95
|
+
installSkillsFromDir(srcPath, destDir, projectTags);
|
|
96
|
+
}
|
|
97
|
+
} else if (entry.endsWith('.md') && entry !== 'README.md') {
|
|
98
|
+
// Legacy flat .md skill — check stack filter before copying
|
|
99
|
+
const content = readFileSync(srcPath, 'utf-8');
|
|
100
|
+
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
101
|
+
const skillName = entry.slice(0, -3);
|
|
102
|
+
const skillDir = join(destDir, skillName);
|
|
103
|
+
mkdirSync(skillDir, { recursive: true });
|
|
104
|
+
copyFileSync(srcPath, join(skillDir, 'SKILL.md'));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function collectSourceSkillNames() {
|
|
110
|
+
const names = new Set();
|
|
111
|
+
for (const level of SKILL_LEVELS_TO_INSTALL) {
|
|
112
|
+
const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
|
|
113
|
+
if (!existsSync(levelDir)) continue;
|
|
114
|
+
collectNamesFromDir(levelDir, names);
|
|
115
|
+
}
|
|
116
|
+
return names;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function collectNamesFromDir(dir, names) {
|
|
120
|
+
for (const entry of readdirSync(dir)) {
|
|
121
|
+
const fullPath = join(dir, entry);
|
|
122
|
+
if (!statSync(fullPath).isDirectory()) continue;
|
|
123
|
+
if (existsSync(join(fullPath, 'SKILL.md'))) {
|
|
124
|
+
names.add(entry);
|
|
125
|
+
} else {
|
|
126
|
+
collectNamesFromDir(fullPath, names);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Install morph framework skills to .claude/skills/ in the target project.
|
|
133
|
+
* Skills are copied into subdirectories as SKILL.md files so Claude Code can
|
|
134
|
+
* discover them natively via /<skill-name> syntax (e.g., /phase-design,
|
|
135
|
+
* /nextjs-expert). Directory-based skills also get their scripts/,
|
|
136
|
+
* references/, and assets/ subdirectories copied for on-demand loading.
|
|
137
|
+
*
|
|
138
|
+
* Stack filtering: if projectTags is provided, only skills whose `stacks:`
|
|
139
|
+
* frontmatter matches the project tags are installed. Skills without a
|
|
140
|
+
* `stacks:` field are always installed (universal).
|
|
141
|
+
*
|
|
142
|
+
* @param {string} projectDir - Target project root directory
|
|
143
|
+
* @param {Object} [options]
|
|
144
|
+
* @param {string[]} [options.projectTags=[]] - Stack tags from config
|
|
145
|
+
*/
|
|
146
|
+
export async function installSkills(projectDir, { projectTags = [] } = {}) {
|
|
147
|
+
const claudeSkillsDir = join(projectDir, '.claude', 'skills');
|
|
148
|
+
mkdirSync(claudeSkillsDir, { recursive: true });
|
|
149
|
+
|
|
150
|
+
for (const level of SKILL_LEVELS_TO_INSTALL) {
|
|
151
|
+
const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
|
|
152
|
+
if (!existsSync(levelDir)) continue;
|
|
153
|
+
installSkillsFromDir(levelDir, claudeSkillsDir, projectTags);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Collect installed morph-* skill names
|
|
157
|
+
const installed = new Set();
|
|
158
|
+
for (const entry of readdirSync(claudeSkillsDir)) {
|
|
159
|
+
if (existsSync(join(claudeSkillsDir, entry, 'SKILL.md'))) {
|
|
160
|
+
installed.add(entry);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Cleanup orphan morph-* skill dirs that exist in source but were filtered out
|
|
165
|
+
const allSourceSkills = collectSourceSkillNames();
|
|
166
|
+
for (const entry of readdirSync(claudeSkillsDir)) {
|
|
167
|
+
if (!entry.startsWith('morph-')) continue;
|
|
168
|
+
if (!installed.has(entry) && allSourceSkills.has(entry)) {
|
|
169
|
+
rmSync(join(claudeSkillsDir, entry), { recursive: true, force: true });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { installed: installed.size };
|
|
174
|
+
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* subagent-report-guard.js — Stop / SubagentStop hook
|
|
4
|
-
*
|
|
5
|
-
* Enforces clause 1 of the morph-spec dispatch protocol: a dispatched
|
|
6
|
-
* sub-agent's FINAL act is SendMessage to the orchestrator (main). Plain text
|
|
7
|
-
* in the last message never reaches the orchestrator, and an idle agent forces
|
|
8
|
-
* the costly nag-and-wait cycle (observed 2/2 in real apply sessions —
|
|
9
|
-
* MORPH_GHLBrain 2026-07-11).
|
|
10
|
-
*
|
|
11
|
-
* Detection: the session transcript (JSONL) contains the dispatch-protocol
|
|
12
|
-
* marker ("Protocolo obrigatório") inside a USER message — that is how the
|
|
13
|
-
* dispatch prompt arrives in the sub-agent's own transcript. In the
|
|
14
|
-
* ORCHESTRATOR's transcript the marker only appears inside assistant tool_use
|
|
15
|
-
* input (the Agent call that did the dispatch), so the orchestrator is never
|
|
16
|
-
* blocked by this guard. A SendMessage tool_use anywhere in an assistant
|
|
17
|
-
* message counts as the report.
|
|
18
|
-
*
|
|
19
|
-
* Action: block the stop ONCE ({"decision":"block"}) with the exact
|
|
20
|
-
* instruction to call SendMessage — the agent continues and reports.
|
|
21
|
-
* stop_hook_active guards against blocking loops. Fail-open on any error.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import { readFileSync, existsSync } from 'fs';
|
|
25
|
-
import { stateExists } from '../../shared/state-reader.js';
|
|
26
|
-
import { block, pass } from '../../shared/hook-response.js';
|
|
27
|
-
import { logHookActivity } from '../../shared/activity-logger.js';
|
|
28
|
-
|
|
29
|
-
const PROTOCOL_MARKER = 'Protocolo obrigatório';
|
|
30
|
-
|
|
31
|
-
function readStdin() {
|
|
32
|
-
try {
|
|
33
|
-
return JSON.parse(readFileSync(0, 'utf8') || '{}');
|
|
34
|
-
} catch {
|
|
35
|
-
return {};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function projectDir() {
|
|
40
|
-
return process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Scan a transcript for the dispatch-protocol marker (user messages only) and
|
|
45
|
-
* for a SendMessage tool call (assistant messages).
|
|
46
|
-
*
|
|
47
|
-
* @param {string} path - JSONL transcript path
|
|
48
|
-
* @returns {{ dispatched: boolean, reported: boolean }}
|
|
49
|
-
*/
|
|
50
|
-
function scanTranscript(path) {
|
|
51
|
-
const raw = readFileSync(path, 'utf8');
|
|
52
|
-
let dispatched = false;
|
|
53
|
-
let reported = false;
|
|
54
|
-
for (const line of raw.split('\n')) {
|
|
55
|
-
if (!line.trim()) continue;
|
|
56
|
-
let role = null;
|
|
57
|
-
try {
|
|
58
|
-
const entry = JSON.parse(line);
|
|
59
|
-
role = entry?.message?.role ?? entry?.type ?? null;
|
|
60
|
-
} catch {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (!dispatched && role === 'user' && line.includes(PROTOCOL_MARKER)) dispatched = true;
|
|
64
|
-
if (!reported && role === 'assistant' && line.includes('"name":"SendMessage"')) reported = true;
|
|
65
|
-
}
|
|
66
|
-
return { dispatched, reported };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
if (!stateExists(projectDir())) pass();
|
|
71
|
-
|
|
72
|
-
const input = readStdin();
|
|
73
|
-
|
|
74
|
-
// Never loop: a previous block already forced one continuation — let this
|
|
75
|
-
// stop through even if the agent still failed to report.
|
|
76
|
-
if (input.stop_hook_active) pass();
|
|
77
|
-
|
|
78
|
-
const transcriptPath = input.transcript_path;
|
|
79
|
-
if (!transcriptPath || !existsSync(transcriptPath)) pass();
|
|
80
|
-
|
|
81
|
-
const { dispatched, reported } = scanTranscript(transcriptPath);
|
|
82
|
-
if (!dispatched || reported) pass();
|
|
83
|
-
|
|
84
|
-
logHookActivity('subagent-report-guard', input.hook_event_name || 'Stop', 'blocked', projectDir());
|
|
85
|
-
block(
|
|
86
|
-
'[morph-spec] PROTOCOLO DE RETORNO — você foi despachado com o protocolo obrigatório ' +
|
|
87
|
-
'mas ainda não chamou SendMessage. Sua última mensagem em texto NÃO chega ao orquestrador. ' +
|
|
88
|
-
'Antes de encerrar, chame a tool SendMessage com to:"main" e o relatório completo no content: ' +
|
|
89
|
-
'(1) o que foi feito em 2-3 frases, (2) arquivos modificados, (3) problemas encontrados e ' +
|
|
90
|
-
'como foram resolvidos, (4) auto-score 0-10. O SendMessage é o seu ato final.'
|
|
91
|
-
);
|
|
92
|
-
} catch {
|
|
93
|
-
// Fail-open
|
|
94
|
-
process.exit(0);
|
|
95
|
-
}
|