@polymorphism-tech/morph-spec 4.22.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +57 -71
- package/README.md +2 -2
- package/bin/morph-spec.js +224 -491
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +55 -38
- package/framework/MORPH.md +244 -0
- package/framework/agents.json +127 -1198
- package/framework/commands/morph-apply.md +56 -240
- package/framework/commands/morph-archive.md +48 -85
- package/framework/commands/morph-preflight.md +7 -7
- package/framework/commands/morph-proposal.md +35 -191
- package/framework/commands/morph-status.md +19 -50
- package/framework/commands/morph-troubleshoot.md +2 -2
- package/framework/evals/rubrics/README.md +44 -0
- package/framework/evals/rubrics/architecture.md +46 -0
- package/framework/evals/rubrics/code-quality.md +50 -0
- package/framework/evals/rubrics/contracts.md +47 -0
- package/framework/evals/rubrics/test-coverage.md +50 -0
- package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
- package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
- package/framework/evals/suites/standard.yaml +5 -0
- package/framework/evals/trace-schema.md +67 -0
- package/framework/hooks/claude-code/core/README.md +3 -0
- package/framework/hooks/claude-code/core/denylist.json +14 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
- package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
- package/framework/hooks/shared/phase-utils.js +20 -31
- package/framework/hooks/shared/state-reader.js +4 -5
- package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
- package/framework/phases.json +41 -115
- package/framework/rubrics/eval-rubric.json +66 -0
- package/framework/skills/README.md +39 -64
- package/framework/skills/core/README.md +3 -0
- package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
- package/framework/skills/core/morph-eval/SKILL.md +57 -0
- package/framework/skills/core/morph-implement/SKILL.md +83 -0
- package/framework/skills/core/morph-plan/SKILL.md +64 -0
- package/framework/skills/core/morph-review/SKILL.md +62 -0
- package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
- package/framework/skills/core/morph-standards/SKILL.md +49 -0
- package/framework/templates/CLAUDE.md.core.guide.md +29 -0
- package/framework/templates/CLAUDE.md.core.template +22 -0
- package/framework/templates/REGISTRY.json +4 -4
- package/framework/workflows/configs/fast-track.json +5 -10
- package/framework/workflows/configs/full.json +4 -8
- package/framework/workflows/configs/quick.json +32 -0
- package/framework/workflows/configs/standard.json +57 -64
- package/package.json +7 -6
- package/src/commands/mcp/mcp-setup.js +58 -31
- package/src/commands/project/doctor.js +69 -70
- package/src/commands/project/init.js +26 -5
- package/src/commands/project/status.js +18 -19
- package/src/commands/state/approve.js +1 -1
- package/src/commands/validation/validate-feature.js +18 -112
- package/src/core/paths/output-schema.js +133 -152
- package/src/core/state/phase-state-machine.js +10 -24
- package/src/core/state/state-manager.js +17 -17
- package/src/core/workflows/workflow-detector.js +214 -31
- package/src/core/workflows/workflow-router.js +204 -0
- package/src/dashboard/app/pages/AgentMap.jsx +32 -33
- package/src/dashboard/app/pages/LiveState.jsx +1 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
- package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
- package/src/dashboard/app/pages/Workflows.jsx +2 -2
- package/src/dashboard/scanner/agents-scanner.js +30 -35
- package/src/lib/agents/metrics-aggregator.js +1 -1
- package/src/lib/agents/output-collector.js +7 -9
- package/src/lib/context/context-builder.js +289 -0
- package/src/lib/detectors/claude-config-detector.js +19 -3
- package/src/lib/generators/context-generator.js +20 -42
- package/src/lib/generators/index.js +1 -0
- package/src/lib/generators/mandate-generator.js +512 -0
- package/src/lib/generators/metadata-extractor.js +1 -1
- package/src/lib/generators/recap-generator.js +3 -4
- package/src/lib/installers/mcp-installer.js +300 -92
- package/src/lib/phase-chain/phase-validator.js +36 -40
- package/src/lib/stack/stack-profile.js +74 -91
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/wave-resolver.js +353 -0
- package/src/lib/validators/checkpoint-persistence.js +2 -2
- package/src/lib/validators/content/cross-phase-validator.js +281 -0
- package/src/lib/validators/content/index.js +1 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
- package/src/scripts/global-install.js +2 -27
- package/src/utils/agents-installer.js +224 -447
- package/src/utils/claude-settings-manager.js +57 -15
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +15 -117
- package/src/utils/skills-installer.js +12 -18
- package/bin/detect-agents.js +0 -226
- package/bin/task-manager.js +0 -1036
- package/framework/agents/README.md +0 -13
- package/framework/agents/ai-agents/ai-system-architect.md +0 -194
- package/framework/agents/ai-agents/vector-search-expert.md +0 -247
- package/framework/agents/architecture/architecture-expert.md +0 -98
- package/framework/agents/architecture/morph-spec-architect.md +0 -66
- package/framework/agents/architecture/packages-validator.md +0 -89
- package/framework/agents/architecture/security-expert.md +0 -79
- package/framework/agents/architecture/seo-growth-hacker.md +0 -168
- package/framework/agents/architecture/standards-architect.md +0 -153
- package/framework/agents/backend/dotnet-senior.md +0 -163
- package/framework/agents/backend/ef-modeler.md +0 -119
- package/framework/agents/backend/hangfire-orchestrator.md +0 -133
- package/framework/agents/backend/ms-agent-expert.md +0 -55
- package/framework/agents/backend/neon-expert.md +0 -379
- package/framework/agents/frontend/css-specialist.md +0 -89
- package/framework/agents/frontend/design-system-validator.md +0 -100
- package/framework/agents/frontend/gsap-animator.md +0 -131
- package/framework/agents/frontend/nextjs-component-validator.md +0 -97
- package/framework/agents/frontend/nextjs-expert.md +0 -201
- package/framework/agents/frontend/ui-designer.md +0 -259
- package/framework/agents/infrastructure/infra-architect.md +0 -215
- package/framework/agents/integrations/asaas-financial.md +0 -138
- package/framework/agents/integrations/neon-auth.md +0 -151
- package/framework/agents/integrations/resend-email.md +0 -127
- package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
- package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
- package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
- package/framework/hooks/shared/compact-restore.js +0 -123
- package/framework/hooks/shared/dispatch-helpers.js +0 -147
- package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
- package/framework/hooks/shared/stale-task-reset.js +0 -57
- package/framework/hooks/shared/worktree-helpers.js +0 -75
- package/framework/skills/level-0-meta/README.md +0 -7
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
- package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
- package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
- package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
- package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
- package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
- package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
- package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
- package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
- package/framework/skills/level-1-workflows/README.md +0 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
- package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
- package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
- package/framework/squad-templates/backend-only.json +0 -47
- package/framework/squad-templates/frontend-only.json +0 -56
- package/framework/squad-templates/full-stack.json +0 -74
- package/src/commands/agents/agent-metrics.js +0 -174
- package/src/commands/agents/audit-dispatch.js +0 -82
- package/src/commands/agents/detect-agents.js +0 -275
- package/src/commands/agents/dispatch-agents.js +0 -461
- package/src/commands/agents/index.js +0 -13
- package/src/commands/agents/manage-agents.js +0 -352
- package/src/commands/agents/project-metrics.js +0 -74
- package/src/commands/phase/phase-reset.js +0 -74
- package/src/commands/scope/escalate.js +0 -217
- package/src/commands/state/advance-phase.js +0 -600
- package/src/commands/state/phase-runner.js +0 -215
- package/src/commands/state/state.js +0 -444
- package/src/commands/task/expand.js +0 -100
- package/src/commands/tasks/index.js +0 -4
- package/src/commands/tasks/task.js +0 -105
- package/src/commands/templates/index.js +0 -5
- package/src/commands/templates/template-render.js +0 -93
- package/src/commands/trust/trust.js +0 -118
- package/src/lib/agents/agents-manager.js +0 -349
- package/src/lib/build/build-runner.js +0 -99
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
- package/src/lib/contracts/sprint-contract-generator.js +0 -137
- package/src/lib/scope/impact-analyzer.js +0 -106
- package/src/lib/trust/trust-manager.js +0 -157
- package/src/utils/tier-helpers.js +0 -24
- /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: nextjs-auth
|
|
2
|
+
description: OAuth2 authentication feature for Next.js + shadcn/ui application with Neon Auth
|
|
3
|
+
stack: nextjs-neon
|
|
4
|
+
|
|
5
|
+
request: |
|
|
6
|
+
Add user authentication to the Next.js application using Neon Auth (Better Auth).
|
|
7
|
+
Users should be able to sign in with Google OAuth, view their profile page
|
|
8
|
+
showing name, email, and avatar using shadcn/ui Card and Avatar components,
|
|
9
|
+
and sign out. Use TanStack Query for data fetching and cache management.
|
|
10
|
+
Protect authenticated routes with Next.js middleware.
|
|
11
|
+
Session data lives in the Neon PostgreSQL database.
|
|
12
|
+
|
|
13
|
+
phases_to_eval:
|
|
14
|
+
- proposal
|
|
15
|
+
- spec
|
|
16
|
+
- tasks
|
|
17
|
+
|
|
18
|
+
skip_implementation: true
|
|
19
|
+
|
|
20
|
+
dimensions:
|
|
21
|
+
proposal:
|
|
22
|
+
completeness: "Covers sign-in, profile with shadcn/ui components, sign-out, Neon Auth session storage, middleware route protection"
|
|
23
|
+
scope: "Appropriately scoped — no admin dashboard, no multi-provider auth beyond Google"
|
|
24
|
+
spec:
|
|
25
|
+
specificity: "Each FR has testable acceptance criteria with specific status codes, Neon Auth config, TanStack Query hooks"
|
|
26
|
+
data_model: "User and Session entities defined with Prisma schema or Drizzle schema for Neon PostgreSQL"
|
|
27
|
+
architecture: "Next.js App Router structure, API routes, middleware, shadcn/ui component boundaries, TanStack Query patterns"
|
|
28
|
+
completeness: "OAuth flow via Neon Auth, token refresh, error handling, logout flow, loading states with Framer Motion"
|
|
29
|
+
tasks:
|
|
30
|
+
granularity: "Each task 15-60 minutes, no mega-tasks"
|
|
31
|
+
coverage: "All spec FRs mapped to at least one task"
|
|
32
|
+
ordering: "Dependencies respected — Neon Auth setup before routes, routes before UI, UI before tests"
|
|
33
|
+
cross_phase:
|
|
34
|
+
fr_coverage_threshold: 0.95
|
|
35
|
+
mandate_completeness: 0.9
|
|
36
|
+
|
|
37
|
+
threshold: 8.0
|
|
38
|
+
max_iterations: 3
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Trace Schema — morph-spec NLH
|
|
2
|
+
|
|
3
|
+
## Local de armazenamento
|
|
4
|
+
|
|
5
|
+
`.morph/traces/YYYY-MM-DD-{feature}-{phase}.json`
|
|
6
|
+
|
|
7
|
+
Um arquivo por fase. Se a fase for executada em múltiplas sessões, o mesmo arquivo é atualizado incrementalmente.
|
|
8
|
+
|
|
9
|
+
## Formato JSON
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"schema_version": "1.0",
|
|
14
|
+
"session_id": "YYYY-MM-DD-HH-MM-SS",
|
|
15
|
+
"feature": "nome-da-feature",
|
|
16
|
+
"phase": "implement | plan | review",
|
|
17
|
+
"started_at": "ISO 8601",
|
|
18
|
+
"completed_at": "ISO 8601",
|
|
19
|
+
"tasks": [
|
|
20
|
+
{
|
|
21
|
+
"id": "T1",
|
|
22
|
+
"title": "descrição da task",
|
|
23
|
+
"status": "completed | failed | skipped",
|
|
24
|
+
"standards_consulted": ["id-do-standard-1"],
|
|
25
|
+
"agent_persona": "dotnet-senior | nextjs-expert | etc",
|
|
26
|
+
"sub_agent_dispatched": false,
|
|
27
|
+
"eval_score": 8.5,
|
|
28
|
+
"eval_dimensions": {
|
|
29
|
+
"architecture": 9.0,
|
|
30
|
+
"contracts": 8.0,
|
|
31
|
+
"code_quality": 8.5,
|
|
32
|
+
"test_coverage": 8.5
|
|
33
|
+
},
|
|
34
|
+
"files_modified": ["path/relativo/arquivo.cs"],
|
|
35
|
+
"duration_seconds": null
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"phase_summary": {
|
|
39
|
+
"tasks_completed": 3,
|
|
40
|
+
"tasks_failed": 0,
|
|
41
|
+
"avg_eval_score": 8.3,
|
|
42
|
+
"standards_most_used": ["architecture-vertical-slice"],
|
|
43
|
+
"sub_agents_dispatched": 0
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quando escrever
|
|
49
|
+
|
|
50
|
+
| Evento | O que logar |
|
|
51
|
+
|--------|-------------|
|
|
52
|
+
| Task completada | Adicionar entrada em `tasks[]` com todos os campos preenchidos |
|
|
53
|
+
| Eval executada (`morph-eval`) | Preencher `eval_score` e `eval_dimensions` da task correspondente |
|
|
54
|
+
| Fase concluída | Preencher `completed_at` e `phase_summary` |
|
|
55
|
+
|
|
56
|
+
## Regras de escrita
|
|
57
|
+
|
|
58
|
+
- O modelo escreve o trace como arquivo JSON ao final de cada task e ao final da fase.
|
|
59
|
+
- Se um campo não tiver valor disponível, usar `null` — nunca omitir o campo.
|
|
60
|
+
- Manter a estrutura completa para compatibilidade com análise de meta-harness.
|
|
61
|
+
- `session_id` usa o formato `YYYY-MM-DD-HH-MM-SS` do início da sessão atual.
|
|
62
|
+
- `standards_consulted` contém os `id`s do STANDARDS.json, não os paths.
|
|
63
|
+
- `files_modified` usa paths relativos à raiz do repositório.
|
|
64
|
+
|
|
65
|
+
## Motivação
|
|
66
|
+
|
|
67
|
+
Raw traces preservam o sinal completo de execução. Summaries comprimem informação e perdem sinais que o meta-harness usa para otimização. Regra: logar o trace completo — nunca substituir por resumo.
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* Ensures writes to .morph/features/{feature}/ go to the current phase's subfolder:
|
|
9
9
|
* proposal → only 0-proposal/
|
|
10
10
|
* uiux → only 1-ui/
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* plan → only 2-plan/
|
|
12
|
+
* implement → 3-implement/ + any source code (unrestricted)
|
|
13
|
+
* review → only 4-review/
|
|
14
14
|
*
|
|
15
15
|
* Files outside .morph/features/ are always allowed.
|
|
16
16
|
*
|
|
@@ -77,8 +77,8 @@ try {
|
|
|
77
77
|
block(
|
|
78
78
|
`MORPH-SPEC: Writing to '${targetDir}/' is not allowed during ${phaseLabel} phase.\n` +
|
|
79
79
|
`Current phase '${phase}' only permits writes to '${allowedDir}/'.\n\n` +
|
|
80
|
-
`
|
|
81
|
-
`
|
|
80
|
+
`If you need to write to a different phase folder, ensure the feature\n` +
|
|
81
|
+
`has advanced to the appropriate phase first (check gates in state.json).`
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PostToolUse Hook: TypeScript typecheck with debounce
|
|
5
|
+
*
|
|
6
|
+
* Event: PostToolUse | Matcher: Write|Edit
|
|
7
|
+
*
|
|
8
|
+
* Runs `npx tsc --noEmit` only for .ts/.tsx files, with a 3-second debounce
|
|
9
|
+
* so rapid sequential edits (e.g. multiple files in one task) don't each
|
|
10
|
+
* trigger a full typecheck. Silent on success — only injects on error.
|
|
11
|
+
*
|
|
12
|
+
* Debounce state: .morph/memory/typecheck-debounce.json
|
|
13
|
+
*
|
|
14
|
+
* Fail-open: exits 0 on any error.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { execSync } from 'child_process';
|
|
18
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
19
|
+
import { join } from 'path';
|
|
20
|
+
import { readStdin } from '../../shared/stdin-reader.js';
|
|
21
|
+
import { pass, injectContext } from '../../shared/hook-response.js';
|
|
22
|
+
import { stateExists } from '../../shared/state-reader.js';
|
|
23
|
+
|
|
24
|
+
const DEBOUNCE_MS = 3000;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
if (!stateExists()) pass();
|
|
28
|
+
|
|
29
|
+
const payload = await readStdin();
|
|
30
|
+
const toolName = payload?.tool_name || '';
|
|
31
|
+
const filePath = payload?.tool_input?.file_path || payload?.tool_input?.path || '';
|
|
32
|
+
|
|
33
|
+
if (!['Write', 'Edit'].includes(toolName)) pass();
|
|
34
|
+
if (!filePath.match(/\.tsx?$/)) pass();
|
|
35
|
+
|
|
36
|
+
// Debounce: skip if a typecheck ran within the last 3 seconds
|
|
37
|
+
const cwd = process.cwd();
|
|
38
|
+
const memDir = join(cwd, '.morph', 'memory');
|
|
39
|
+
const debouncePath = join(memDir, 'typecheck-debounce.json');
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
if (existsSync(debouncePath)) {
|
|
43
|
+
const { lastRun } = JSON.parse(readFileSync(debouncePath, 'utf-8'));
|
|
44
|
+
if (Date.now() - new Date(lastRun).getTime() < DEBOUNCE_MS) pass();
|
|
45
|
+
}
|
|
46
|
+
} catch { /* corrupt file — proceed */ }
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
mkdirSync(memDir, { recursive: true });
|
|
50
|
+
writeFileSync(debouncePath, JSON.stringify({ lastRun: new Date().toISOString() }), 'utf-8');
|
|
51
|
+
} catch { /* non-blocking */ }
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
execSync('npx tsc --noEmit', { cwd, stdio: 'pipe', timeout: 30000 });
|
|
55
|
+
pass();
|
|
56
|
+
} catch (err) {
|
|
57
|
+
const stderr = err.stderr?.toString() || '';
|
|
58
|
+
const stdout = err.stdout?.toString() || '';
|
|
59
|
+
const output = (stderr + stdout).trim();
|
|
60
|
+
const fileName = filePath.split(/[\\/]/).pop();
|
|
61
|
+
const relevant = output.split('\n')
|
|
62
|
+
.filter(l => l.includes(fileName) || l.includes('error TS'))
|
|
63
|
+
.slice(0, 10)
|
|
64
|
+
.join('\n');
|
|
65
|
+
injectContext(`TypeScript errors after editing ${fileName}:\n${relevant || output.split('\n').slice(0, 10).join('\n')}`);
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
pass();
|
|
69
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { readStdin } from '../../shared/stdin-reader.js';
|
|
6
|
+
import { pass, block } from '../../shared/hook-response.js';
|
|
7
|
+
import { stateExists } from '../../shared/state-reader.js';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
if (!stateExists()) pass();
|
|
13
|
+
|
|
14
|
+
const payload = await readStdin();
|
|
15
|
+
const command = payload?.tool_input?.command || '';
|
|
16
|
+
if (!command) pass();
|
|
17
|
+
|
|
18
|
+
const denylist = JSON.parse(readFileSync(join(__dirname, 'denylist.json'), 'utf-8'));
|
|
19
|
+
|
|
20
|
+
for (const pattern of denylist.patterns) {
|
|
21
|
+
if (command.includes(pattern)) {
|
|
22
|
+
block(`Comando bloqueado pela denylist: padrão '${pattern}' detectado.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pass();
|
|
27
|
+
} catch {
|
|
28
|
+
pass();
|
|
29
|
+
}
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* Event: PreToolUse | Matcher: Write|Edit
|
|
7
7
|
*
|
|
8
8
|
* Blocks Write/Edit to spec artifacts after their approval gate has passed:
|
|
9
|
-
* - 2-
|
|
10
|
-
* - 4-tasks/tasks.md → blocked if 'tasks' gate approved
|
|
9
|
+
* - 2-plan/spec.md, contracts.cs, tasks.md, etc. → blocked if 'plan' gate approved
|
|
11
10
|
* - 1-ui/design-system.md, etc. → blocked if 'uiux' gate approved
|
|
12
11
|
*
|
|
13
12
|
* Fail-open: exits 0 on any error.
|
|
@@ -58,10 +57,9 @@ try {
|
|
|
58
57
|
block(
|
|
59
58
|
`MORPH-SPEC: '${filename}' is locked — the '${requiredGate}' gate has been approved.\n` +
|
|
60
59
|
`Editing approved specs breaks the spec contract.\n\n` +
|
|
61
|
-
`If changes are truly needed
|
|
62
|
-
`
|
|
63
|
-
`
|
|
64
|
-
` 3. Re-approve: morph-spec approve ${featureName} ${requiredGate}`
|
|
60
|
+
`If changes are truly needed, ask the user to revoke the gate approval\n` +
|
|
61
|
+
`in .morph/state.json (set approvals.${requiredGate} to false), make\n` +
|
|
62
|
+
`your edits, then re-present the gate for approval.`
|
|
65
63
|
);
|
|
66
64
|
}
|
|
67
65
|
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Stop Hook:
|
|
4
|
+
* Stop Hook: Phase Transition Alert + Build Check + Session Snapshot
|
|
5
5
|
*
|
|
6
6
|
* Event: Stop
|
|
7
7
|
*
|
|
8
|
-
* 1. Detects transitions into the `implement` phase and recommends /compact
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* 1. Detects transitions into the `implement` phase and recommends /compact.
|
|
9
|
+
* 2. Runs stack-aware build check — warns only on failure (silent on pass).
|
|
10
|
+
* 3. Saves session snapshot for next-session orientation.
|
|
11
11
|
*
|
|
12
12
|
* Phase transition tracking: stores last-seen phase per feature in
|
|
13
|
-
* .morph/memory/phase-watch.json so the alert fires exactly once
|
|
14
|
-
* the first Stop after the transition occurs.
|
|
15
|
-
*
|
|
16
|
-
* Uses stop_hook_active env check to prevent infinite loops.
|
|
13
|
+
* .morph/memory/phase-watch.json so the alert fires exactly once.
|
|
17
14
|
*
|
|
18
15
|
* Fail-open: exits 0 on any error.
|
|
19
16
|
*/
|
|
@@ -23,7 +20,7 @@ import { join } from 'path';
|
|
|
23
20
|
import { runBuildCheck } from '../../shared/build-check.js';
|
|
24
21
|
import { saveSessionSnapshot } from '../../shared/session-snapshot.js';
|
|
25
22
|
import {
|
|
26
|
-
stateExists, getActiveFeature, getMostRecentFeature,
|
|
23
|
+
stateExists, getActiveFeature, getMostRecentFeature, derivePhaseForFeature,
|
|
27
24
|
} from '../../shared/state-reader.js';
|
|
28
25
|
import { injectContext, pass } from '../../shared/hook-response.js';
|
|
29
26
|
import { logHookActivity, logEvent } from '../../shared/activity-logger.js';
|
|
@@ -31,8 +28,8 @@ import { logHookActivity, logEvent } from '../../shared/activity-logger.js';
|
|
|
31
28
|
// Phases where compaction is strongly recommended before starting
|
|
32
29
|
const COMPACT_TRIGGER_PHASES = new Set(['implement']);
|
|
33
30
|
|
|
34
|
-
// Phases where compaction is suggested when arriving
|
|
35
|
-
const HEAVY_SOURCE_PHASES = new Set(['
|
|
31
|
+
// Phases where compaction is suggested when arriving (heavy spec phases)
|
|
32
|
+
const HEAVY_SOURCE_PHASES = new Set(['plan', 'uiux']);
|
|
36
33
|
|
|
37
34
|
try {
|
|
38
35
|
// Prevent infinite loop
|
|
@@ -76,11 +73,11 @@ try {
|
|
|
76
73
|
`⚡ Phase advanced: ${lastPhase} → implement`,
|
|
77
74
|
``,
|
|
78
75
|
`💡 Recommended before starting implementation:`,
|
|
79
|
-
` Run /compact to free up context —
|
|
76
|
+
` Run /compact to free up context — spec.md, mandate.md and tasks.md`,
|
|
80
77
|
` loaded during planning take significant space. A compact now gives`,
|
|
81
78
|
` Claude a clean slate focused on writing code.`,
|
|
82
79
|
``,
|
|
83
|
-
` After /compact, resume with: morph-
|
|
80
|
+
` After /compact, resume with: /morph-apply ${name}`,
|
|
84
81
|
);
|
|
85
82
|
}
|
|
86
83
|
|
|
@@ -111,14 +108,10 @@ try {
|
|
|
111
108
|
// Save session state for next session's "getting up to speed" routine.
|
|
112
109
|
try {
|
|
113
110
|
if (active) {
|
|
114
|
-
const { name
|
|
115
|
-
const taskList = feature.taskList || [];
|
|
111
|
+
const { name } = active;
|
|
116
112
|
saveSessionSnapshot({
|
|
117
113
|
feature: name,
|
|
118
114
|
phase: currentPhase,
|
|
119
|
-
tasksCompleted: taskList.filter(t => t.status === 'completed').map(t => t.id),
|
|
120
|
-
tasksRemaining: taskList.filter(t => t.status === 'pending').map(t => t.id),
|
|
121
|
-
tasksInProgress: taskList.filter(t => t.status === 'in_progress').map(t => t.id),
|
|
122
115
|
buildStatus: 'skip',
|
|
123
116
|
lastEditedFiles: [],
|
|
124
117
|
}, cwd);
|
|
@@ -128,73 +121,11 @@ try {
|
|
|
128
121
|
}
|
|
129
122
|
// ────────────────────────────────────────────────────────────────────────
|
|
130
123
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// Incomplete tasks during implement phase
|
|
134
|
-
if (currentPhase === 'implement' && feature.tasks) {
|
|
135
|
-
const remaining = (feature.tasks.total || 0) - (feature.tasks.completed || 0);
|
|
136
|
-
if (remaining > 0) {
|
|
137
|
-
warnings.push(`${remaining} task(s) remaining for feature '${name}'`);
|
|
138
|
-
if (feature.tasks.inProgress > 0) {
|
|
139
|
-
warnings.push(` ${feature.tasks.inProgress} task(s) still in progress`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Missing outputs in spec phases
|
|
145
|
-
if (['proposal', 'design', 'tasks', 'uiux'].includes(currentPhase)) {
|
|
146
|
-
const missing = getMissingOutputs(name);
|
|
147
|
-
if (missing.length > 0) {
|
|
148
|
-
warnings.push(`Missing outputs for '${name}' (${currentPhase} phase):`);
|
|
149
|
-
for (const output of missing.slice(0, 5)) {
|
|
150
|
-
warnings.push(` - ${output.type}: ${output.path}`);
|
|
151
|
-
}
|
|
152
|
-
if (missing.length > 5) {
|
|
153
|
-
warnings.push(` ... and ${missing.length - 5} more`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Pending approval gates
|
|
159
|
-
if (feature.approvalGates) {
|
|
160
|
-
const pendingGates = Object.entries(feature.approvalGates)
|
|
161
|
-
.filter(([, gate]) => !gate.approved && !gate.timestamp)
|
|
162
|
-
.map(([gateName]) => gateName);
|
|
163
|
-
|
|
164
|
-
const gatePhaseMap = { proposal: 'proposal', uiux: 'uiux', design: 'design', tasks: 'tasks' };
|
|
165
|
-
const relevant = pendingGates.filter(gate => gatePhaseMap[gate] !== undefined);
|
|
166
|
-
|
|
167
|
-
if (relevant.length > 0) {
|
|
168
|
-
warnings.push(`Pending approvals: ${relevant.join(', ')}`);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ── MEMORY.md reminder ────────────────────────────────────────────────────
|
|
173
|
-
const WORK_PHASES = new Set(['design', 'tasks', 'implement']);
|
|
174
|
-
const memoryLines = [];
|
|
175
|
-
if (WORK_PHASES.has(currentPhase)) {
|
|
176
|
-
memoryLines.push('📝 Sessão encerrando. Considere salvar no MEMORY.md:');
|
|
177
|
-
memoryLines.push(' • Padrões de domínio descobertos (regras, invariants, edge cases)');
|
|
178
|
-
memoryLines.push(' • Decisões arquiteturais tomadas nesta sessão (e o motivo)');
|
|
179
|
-
memoryLines.push(' • Erros frequentes desta stack/projeto e como resolvê-los');
|
|
180
|
-
memoryLines.push(' • Nuances do projeto que diferem do padrão (convenções, libs específicas)');
|
|
181
|
-
memoryLines.push(' • Bugs encontrados e a causa raiz');
|
|
182
|
-
memoryLines.push(' Se nada relevante foi descoberto nesta sessão, ignore este lembrete.');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (warnings.length === 0 && memoryLines.length === 0) pass();
|
|
186
|
-
|
|
187
|
-
const allLines = [...warnings];
|
|
188
|
-
if (memoryLines.length > 0) {
|
|
189
|
-
if (allLines.length > 0) allLines.push('');
|
|
190
|
-
allLines.push(...memoryLines);
|
|
191
|
-
}
|
|
124
|
+
if (warnings.length === 0) pass();
|
|
192
125
|
|
|
193
126
|
const message = [
|
|
194
127
|
'MORPH-SPEC:',
|
|
195
|
-
...
|
|
196
|
-
'',
|
|
197
|
-
`Status: morph-spec status ${name}`,
|
|
128
|
+
...warnings.map(w => ` ${w}`),
|
|
198
129
|
].join('\n');
|
|
199
130
|
|
|
200
131
|
logHookActivity('validate-completion', 'Stop', `warnings(${warnings.length})`);
|
|
@@ -8,71 +8,60 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/** Phase order */
|
|
11
|
-
export const PHASE_ORDER = ["proposal","uiux","
|
|
11
|
+
export const PHASE_ORDER = ["proposal","uiux","plan","implement","review"];
|
|
12
12
|
|
|
13
13
|
/** Map phase → allowed output subdirectory */
|
|
14
14
|
export const PHASE_DIRS = {
|
|
15
15
|
proposal: '0-proposal',
|
|
16
16
|
uiux: '1-ui',
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
implement: '5-implement',
|
|
21
|
-
sync: '5-implement',
|
|
17
|
+
plan: '2-plan',
|
|
18
|
+
implement: '3-implement',
|
|
19
|
+
review: '4-review',
|
|
22
20
|
};
|
|
23
21
|
|
|
24
22
|
/** Map output type (camelCase) → phase that produces it */
|
|
25
23
|
export const OUTPUT_PHASE_MAP = {
|
|
26
24
|
proposal: 'proposal',
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
contracts: 'design',
|
|
30
|
-
contractsTs: 'design',
|
|
31
|
-
contractsVsa: 'design',
|
|
32
|
-
plan: 'plan',
|
|
33
|
-
tasks: 'tasks',
|
|
25
|
+
stackScan: 'proposal',
|
|
26
|
+
migrationPlan: 'proposal',
|
|
34
27
|
uiDesignSystem: 'uiux',
|
|
35
28
|
uiMockups: 'uiux',
|
|
36
29
|
uiComponents: 'uiux',
|
|
37
30
|
uiFlows: 'uiux',
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
spec: 'plan',
|
|
32
|
+
mandate: 'plan',
|
|
33
|
+
tasks: 'plan',
|
|
34
|
+
decisions: 'plan',
|
|
40
35
|
recap: 'implement',
|
|
36
|
+
reviewReport: 'review',
|
|
41
37
|
};
|
|
42
38
|
|
|
43
39
|
/** Map filename → output type (camelCase) */
|
|
44
40
|
export const FILENAME_TO_OUTPUT = {
|
|
45
41
|
'proposal.md': 'proposal',
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'contracts.cs': 'contracts',
|
|
49
|
-
'contracts.ts': 'contractsTs',
|
|
50
|
-
'contracts-vsa.cs': 'contractsVsa',
|
|
51
|
-
'plan.md': 'plan',
|
|
52
|
-
'tasks.md': 'tasks',
|
|
42
|
+
'stack-scan.md': 'stackScan',
|
|
43
|
+
'migration-plan.md': 'migrationPlan',
|
|
53
44
|
'design-system.md': 'uiDesignSystem',
|
|
54
45
|
'mockups.md': 'uiMockups',
|
|
55
46
|
'components.md': 'uiComponents',
|
|
56
47
|
'flows.md': 'uiFlows',
|
|
57
|
-
'
|
|
48
|
+
'spec.md': 'spec',
|
|
49
|
+
'mandate.md': 'mandate',
|
|
50
|
+
'tasks.md': 'tasks',
|
|
58
51
|
'decisions.md': 'decisions',
|
|
59
52
|
'recap.md': 'recap',
|
|
53
|
+
'review-report.md': 'reviewReport',
|
|
60
54
|
};
|
|
61
55
|
|
|
62
56
|
/** Protected spec files and the approval gate that locks them */
|
|
63
57
|
export const PROTECTED_SPEC_FILES = {
|
|
64
|
-
'schema-analysis.md': 'design',
|
|
65
|
-
'spec.md': 'design',
|
|
66
|
-
'contracts.cs': 'design',
|
|
67
|
-
'contracts.ts': 'design',
|
|
68
|
-
'contracts-vsa.cs': 'design',
|
|
69
|
-
'plan.md': 'plan',
|
|
70
|
-
'tasks.md': 'tasks',
|
|
71
58
|
'design-system.md': 'uiux',
|
|
72
59
|
'mockups.md': 'uiux',
|
|
73
60
|
'components.md': 'uiux',
|
|
74
61
|
'flows.md': 'uiux',
|
|
75
|
-
'
|
|
62
|
+
'spec.md': 'plan',
|
|
63
|
+
'mandate.md': 'plan',
|
|
64
|
+
'tasks.md': 'plan',
|
|
76
65
|
};
|
|
77
66
|
|
|
78
67
|
/**
|
|
@@ -107,10 +107,9 @@ export function getFeature(featureName, projectPath) {
|
|
|
107
107
|
export function derivePhaseForFeature(featureName, projectPath) {
|
|
108
108
|
const basePath = join(projectPath || process.cwd(), '.morph/features', featureName);
|
|
109
109
|
const phaseMap = [
|
|
110
|
-
['
|
|
111
|
-
['
|
|
112
|
-
['
|
|
113
|
-
['2-design', 'design'],
|
|
110
|
+
['4-review', 'review'],
|
|
111
|
+
['3-implement', 'implement'],
|
|
112
|
+
['2-plan', 'plan'],
|
|
114
113
|
['1-ui', 'uiux'],
|
|
115
114
|
['0-proposal', 'proposal'],
|
|
116
115
|
];
|
|
@@ -136,7 +135,7 @@ export function getFeaturePhase(featureName, projectPath) {
|
|
|
136
135
|
/**
|
|
137
136
|
* Get approval status for a gate.
|
|
138
137
|
* @param {string} featureName
|
|
139
|
-
* @param {string} gate - 'proposal'|'uiux'|'
|
|
138
|
+
* @param {string} gate - 'proposal'|'uiux'|'plan'|'review'
|
|
140
139
|
* @param {string} [projectPath]
|
|
141
140
|
* @returns {boolean}
|
|
142
141
|
*/
|