@polymorphism-tech/morph-spec 4.10.9 → 4.11.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 +3 -3
- package/LICENSE +71 -71
- package/README.md +5 -11
- package/bin/morph-spec.js +23 -1
- package/bin/task-manager.js +5 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +2 -8
- package/framework/agents/README.md +14 -14
- package/framework/agents/architecture/standards-architect.md +159 -159
- package/framework/agents/frontend/css-specialist.md +1 -1
- package/framework/agents/infrastructure/azure-architect.md +149 -149
- package/framework/agents/infrastructure/azure-deploy-specialist.md +4 -4
- package/framework/agents/infrastructure/infra-architect.md +48 -48
- package/framework/commands/morph-apply.md +18 -18
- package/framework/commands/morph-preflight.md +3 -3
- package/framework/commands/morph-proposal.md +15 -15
- package/framework/commands/morph-status.md +3 -3
- package/framework/commands/morph-troubleshoot.md +1 -1
- package/framework/hooks/README.md +204 -204
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +2 -2
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +3 -3
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +2 -2
- package/framework/hooks/git/pre-commit/agents.sh +1 -1
- package/framework/hooks/shared/phase-utils.js +139 -139
- package/framework/rules/csharp-standards.md +13 -13
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/infrastructure-standards.md +16 -16
- package/framework/rules/testing-standards.md +14 -14
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +8 -8
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +3 -3
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +11 -11
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +3 -3
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +4 -4
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +25 -25
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +8 -8
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +7 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +4 -4
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +23 -23
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +29 -29
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -4
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +9 -9
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +17 -17
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +12 -12
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +16 -16
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +5 -5
- package/framework/standards/STANDARDS.json +2338 -2041
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
- package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +206 -0
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/README.md +814 -814
- package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
- package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
- package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
- package/framework/templates/code/dotnet/contracts/README.md +74 -74
- package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
- package/framework/templates/code/dotnet/frontend/component.razor +239 -239
- package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
- package/framework/templates/code/dotnet/jobs/job.cs +171 -171
- package/framework/templates/code/dotnet/test.cs +239 -239
- package/framework/templates/code/sql/rls-policy.sql +57 -57
- package/framework/templates/code/sql/supabase-migration.sql +100 -100
- package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
- package/framework/templates/code/typescript/contracts.ts +168 -168
- package/framework/templates/context/CONTEXT.md +181 -181
- package/framework/templates/docs/proposal.md +182 -182
- package/framework/templates/docs/user-stories.md +34 -34
- package/framework/templates/examples/design-system-examples.md +357 -357
- package/framework/templates/examples/spec-examples.md +90 -90
- package/framework/templates/feature/decisions.md +187 -187
- package/framework/templates/feature/recap.md +146 -146
- package/framework/templates/feature/tasks.md +199 -199
- package/framework/templates/infrastructure/azure/README.md +286 -286
- package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
- package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
- package/framework/workflows/README.md +18 -18
- package/framework/workflows/docs/enforcement-pipeline.md +1 -1
- package/framework/workflows/docs/full-morph.md +2 -2
- package/package.json +1 -1
- package/src/commands/agents/dispatch-agents.js +17 -0
- package/src/commands/agents/index.js +13 -13
- package/src/commands/agents/project-metrics.js +74 -0
- package/src/commands/project/doctor.js +3 -4
- package/src/commands/project/install-plugin-cmd.js +28 -28
- package/src/commands/project/setup-infra-cmd.js +12 -12
- package/src/commands/project/status.js +8 -1
- package/src/commands/scope/escalate.js +5 -3
- package/src/commands/standards/context-budget.js +89 -0
- package/src/commands/tasks/index.js +4 -4
- package/src/commands/templates/index.js +5 -5
- package/src/commands/utils/index.js +3 -3
- package/src/commands/validation/index.js +4 -4
- package/src/commands/validation/validate-feature.js +32 -3
- package/src/core/state/phase-state-machine.js +217 -217
- package/src/lib/agents/metrics-aggregator.js +56 -0
- package/src/lib/generators/design-system-generator.js +298 -298
- package/src/lib/generators/index.js +10 -10
- package/src/lib/generators/metadata-extractor.js +387 -387
- package/src/lib/stack-filter.js +65 -65
- package/src/lib/standards/digest-builder.js +57 -3
- package/src/lib/standards/index.js +7 -7
- package/src/lib/standards/standards-context-injector.js +302 -302
- package/src/lib/validators/architecture/architecture-validator.js +387 -387
- package/src/lib/validators/architecture/index.js +7 -7
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
- package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
- package/src/lib/validators/blazor/blazor-validator.js +369 -369
- package/src/lib/validators/blazor/index.js +9 -9
- package/src/lib/validators/content/content-validator.js +287 -287
- package/src/lib/validators/content/index.js +7 -7
- package/src/lib/validators/contracts/index.js +7 -7
- package/src/lib/validators/css/css-validator.js +352 -352
- package/src/lib/validators/css/index.js +7 -7
- package/src/lib/validators/design-system/design-system-validator.js +231 -231
- package/src/lib/validators/design-system/index.js +7 -7
- package/src/lib/validators/nextjs/index.js +6 -6
- package/src/lib/validators/nextjs/next-component-validator.js +183 -183
- package/src/lib/validators/packages/index.js +7 -7
- package/src/lib/validators/packages/package-validator.js +360 -360
- package/src/lib/validators/shared/index.js +12 -12
- package/src/lib/validators/shared/issue-counter.js +18 -18
- package/src/lib/validators/shared/result-formatter.js +124 -124
- package/src/lib/validators/ui/index.js +7 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
- package/src/scripts/global-install.js +112 -58
- package/src/scripts/install-plugin.js +126 -126
- package/src/utils/claude-md-injector.js +90 -90
- package/src/utils/file-copier.js +200 -200
- package/src/utils/logger.js +32 -32
- package/src/utils/skills-installer.js +178 -178
- package/src/utils/version-checker.js +169 -175
|
@@ -371,7 +371,7 @@ Keywords like: `bicep`, `azure`, `deploy`, `docker`, `kubernetes`, `infrastructu
|
|
|
371
371
|
|
|
372
372
|
```bash
|
|
373
373
|
# When creating a feature
|
|
374
|
-
|
|
374
|
+
morph-spec feature create user-pagination \
|
|
375
375
|
--request "add pagination to users table"
|
|
376
376
|
|
|
377
377
|
# System automatically:
|
|
@@ -385,7 +385,7 @@ npx morph-spec feature create user-pagination \
|
|
|
385
385
|
|
|
386
386
|
```bash
|
|
387
387
|
# Analyze request without creating feature
|
|
388
|
-
|
|
388
|
+
morph-spec detect-workflow "implement complete authentication system"
|
|
389
389
|
|
|
390
390
|
# Output:
|
|
391
391
|
Detected Workflow: full-morph (92% confidence)
|
|
@@ -407,7 +407,7 @@ Alternative Workflows:
|
|
|
407
407
|
### JSON Output (for automation)
|
|
408
408
|
|
|
409
409
|
```bash
|
|
410
|
-
|
|
410
|
+
morph-spec detect-workflow "add pagination" --json
|
|
411
411
|
|
|
412
412
|
# Output:
|
|
413
413
|
{
|
|
@@ -435,17 +435,17 @@ npx morph-spec detect-workflow "add pagination" --json
|
|
|
435
435
|
|
|
436
436
|
```bash
|
|
437
437
|
# Force specific workflow
|
|
438
|
-
|
|
438
|
+
morph-spec state set user-auth workflow full-morph
|
|
439
439
|
|
|
440
440
|
# Re-detect after request changes
|
|
441
|
-
|
|
441
|
+
morph-spec state detect-workflow user-auth \
|
|
442
442
|
"implement basic login only (no OAuth)"
|
|
443
443
|
```
|
|
444
444
|
|
|
445
445
|
### View Current Workflow
|
|
446
446
|
|
|
447
447
|
```bash
|
|
448
|
-
|
|
448
|
+
morph-spec state get user-auth workflow
|
|
449
449
|
|
|
450
450
|
# Output:
|
|
451
451
|
Workflow: standard
|
|
@@ -594,7 +594,7 @@ Create `framework/workflows/docs/my-workflow.md` explaining the workflow.
|
|
|
594
594
|
**3. Test Detection:**
|
|
595
595
|
|
|
596
596
|
```bash
|
|
597
|
-
|
|
597
|
+
morph-spec detect-workflow "request with custom-keyword"
|
|
598
598
|
```
|
|
599
599
|
|
|
600
600
|
### Modifying Existing Workflows
|
|
@@ -675,7 +675,7 @@ Phases: Quick Analysis → Implement
|
|
|
675
675
|
|
|
676
676
|
**User Experience:**
|
|
677
677
|
```bash
|
|
678
|
-
|
|
678
|
+
morph-spec feature create login-typo --request "fix typo in login button"
|
|
679
679
|
|
|
680
680
|
# Output:
|
|
681
681
|
✓ Detected workflow: fast-track
|
|
@@ -683,7 +683,7 @@ npx morph-spec feature create login-typo --request "fix typo in login button"
|
|
|
683
683
|
📝 Write quick analysis (1 paragraph) and implement
|
|
684
684
|
|
|
685
685
|
# User writes quick analysis, then:
|
|
686
|
-
|
|
686
|
+
morph-spec phase advance login-typo
|
|
687
687
|
|
|
688
688
|
# Output:
|
|
689
689
|
⏩ Skipping: setup, uiux, design, clarify, tasks, sync
|
|
@@ -704,7 +704,7 @@ Phases: PROPOSAL → DESIGN → TASKS → IMPLEMENT
|
|
|
704
704
|
|
|
705
705
|
**User Experience:**
|
|
706
706
|
```bash
|
|
707
|
-
|
|
707
|
+
morph-spec feature create user-pagination \
|
|
708
708
|
--request "add pagination to users table"
|
|
709
709
|
|
|
710
710
|
# Output:
|
|
@@ -727,7 +727,7 @@ Phases: ALL (9 phases mandatory)
|
|
|
727
727
|
|
|
728
728
|
**User Experience:**
|
|
729
729
|
```bash
|
|
730
|
-
|
|
730
|
+
morph-spec feature create user-auth \
|
|
731
731
|
--request "implement complete authentication system with OAuth"
|
|
732
732
|
|
|
733
733
|
# Output:
|
|
@@ -750,7 +750,7 @@ Phases: PROTOTYPE ANALYSIS → TASKS → IMPLEMENT
|
|
|
750
750
|
|
|
751
751
|
**User Experience:**
|
|
752
752
|
```bash
|
|
753
|
-
|
|
753
|
+
morph-spec feature create dashboard-screens \
|
|
754
754
|
--request "implement screens from Figma design"
|
|
755
755
|
|
|
756
756
|
# Output:
|
|
@@ -774,7 +774,7 @@ Phases: VISUAL ANALYSIS → DESIGN SYSTEM → IMPLEMENT
|
|
|
774
774
|
|
|
775
775
|
**User Experience:**
|
|
776
776
|
```bash
|
|
777
|
-
|
|
777
|
+
morph-spec feature create dashboard-redesign \
|
|
778
778
|
--request "redesign dashboard to look more modern"
|
|
779
779
|
|
|
780
780
|
# Output:
|
|
@@ -794,13 +794,13 @@ npx morph-spec feature create dashboard-redesign \
|
|
|
794
794
|
|
|
795
795
|
**Solution 1: Provide more context**
|
|
796
796
|
```bash
|
|
797
|
-
|
|
797
|
+
morph-spec feature create simple-fix \
|
|
798
798
|
--request "add pagination (simple, already have paginator component)"
|
|
799
799
|
```
|
|
800
800
|
|
|
801
801
|
**Solution 2: Manual override**
|
|
802
802
|
```bash
|
|
803
|
-
|
|
803
|
+
morph-spec state set my-feature workflow standard
|
|
804
804
|
```
|
|
805
805
|
|
|
806
806
|
**Solution 3: Improve keywords**
|
|
@@ -849,7 +849,7 @@ If this happens often, add keywords to workflow config:
|
|
|
849
849
|
**Alternative:**
|
|
850
850
|
Use `detect-workflow` first to see both options:
|
|
851
851
|
```bash
|
|
852
|
-
|
|
852
|
+
morph-spec detect-workflow "add authentication"
|
|
853
853
|
|
|
854
854
|
# Review both workflows
|
|
855
855
|
# Decide based on:
|
|
@@ -891,13 +891,13 @@ Create `framework/workflows/configs/blazor-standard.json`:
|
|
|
891
891
|
**Solution 1: Force phase**
|
|
892
892
|
```bash
|
|
893
893
|
# Manually go to UIUX phase
|
|
894
|
-
|
|
894
|
+
morph-spec phase set my-feature uiux
|
|
895
895
|
```
|
|
896
896
|
|
|
897
897
|
**Solution 2: Override workflow**
|
|
898
898
|
```bash
|
|
899
899
|
# Use full-morph (includes all phases)
|
|
900
|
-
|
|
900
|
+
morph-spec state set my-feature workflow full-morph
|
|
901
901
|
```
|
|
902
902
|
|
|
903
903
|
**Solution 3: Document in decisions.md**
|
|
@@ -113,7 +113,7 @@ export function loadStandardsForAgent(agentId, projectPath) {
|
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
115
|
# Standards auto-injected when running detect-agents with --json
|
|
116
|
-
|
|
116
|
+
morph-spec detect-agents --json "create blazor dashboard"
|
|
117
117
|
|
|
118
118
|
# Output includes standardsSummary for each agent:
|
|
119
119
|
{
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
6. Ativar agentes baseado em stack + keywords
|
|
19
19
|
|
|
20
20
|
### FASE 0: PROPOSAL
|
|
21
|
-
1. `
|
|
21
|
+
1. `morph-spec detect "{user-request}"`
|
|
22
22
|
2. Analisar solicitação, identificar stack, estimar complexidade
|
|
23
23
|
3. Gerar proposal.md, registrar agentes no state.json
|
|
24
24
|
- **⛔ PAUSA:** 3 itens (1 ação + 1 pergunta + 1 sugestão)
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
|
|
56
56
|
### FASE 5: IMPLEMENT
|
|
57
57
|
1. Implementar task por task
|
|
58
|
-
2. `
|
|
58
|
+
2. `morph-spec task done {feature} {task-id}` após cada task
|
|
59
59
|
3. Framework gerencia checkpoints automaticamente (a cada 3 tasks)
|
|
60
60
|
4. Gerar recap.md ao final
|
|
61
61
|
|
package/package.json
CHANGED
|
@@ -22,6 +22,7 @@ import { fileURLToPath } from 'url';
|
|
|
22
22
|
import { loadState, stateExists } from '../../core/state/state-manager.js';
|
|
23
23
|
import { buildAgentBriefing, buildValidatorPrompt } from '../../lib/standards/digest-builder.js';
|
|
24
24
|
import { agentIdToSubagentType } from '../../utils/agent-id-mapper.js';
|
|
25
|
+
import { shouldInstall, getProjectTags } from '../../lib/stack-filter.js';
|
|
25
26
|
import { parseAndGroupTasks } from '../../lib/tasks/task-parser.js';
|
|
26
27
|
export { parseAndGroupTasks } from '../../lib/tasks/task-parser.js';
|
|
27
28
|
import chalk from 'chalk';
|
|
@@ -222,6 +223,16 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
222
223
|
// Non-blocking — fail silent, skills block will be omitted
|
|
223
224
|
}
|
|
224
225
|
|
|
226
|
+
// Load project config for stack filtering
|
|
227
|
+
const configPath = join(projectPath, '.morph', 'config.json');
|
|
228
|
+
let projectTags = [];
|
|
229
|
+
try {
|
|
230
|
+
if (existsSync(configPath)) {
|
|
231
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
232
|
+
projectTags = getProjectTags(config);
|
|
233
|
+
}
|
|
234
|
+
} catch { /* non-blocking */ }
|
|
235
|
+
|
|
225
236
|
// Active agents from state (keyword-detected during setup phase)
|
|
226
237
|
const activeAgentIds = new Set(feature.activeAgents || []);
|
|
227
238
|
|
|
@@ -259,6 +270,12 @@ export async function buildDispatchConfig(projectPath, featureName, phase, opts
|
|
|
259
270
|
// Phase relevance check (tier-4 bypass in validate mode)
|
|
260
271
|
if (!isRelevantForPhase(agentId, agentData, phase) && !(isValidateMode && isTier4)) continue;
|
|
261
272
|
|
|
273
|
+
// Stack filtering (Tier 1/2 exempt — always dispatched)
|
|
274
|
+
if (agentData.tier >= 3 && projectTags.length > 0) {
|
|
275
|
+
const agentStacks = agentData.stacks || [];
|
|
276
|
+
if (!shouldInstall(agentStacks, projectTags)) continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
262
279
|
const group = getAgentGroup(agentId, agentData);
|
|
263
280
|
|
|
264
281
|
// Build task prompt: validator prompt for tier-4 in validate mode, else spawn_prompt
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Commands
|
|
3
|
-
*/
|
|
4
|
-
export { dispatchAgentsCommand, buildDispatchConfig } from './dispatch-agents.js';
|
|
5
|
-
export { auditDispatchCommand } from './audit-dispatch.js';
|
|
6
|
-
export { detectAgentsCommand, detectAgents } from './detect-agents.js';
|
|
7
|
-
export {
|
|
8
|
-
agentListCommand,
|
|
9
|
-
agentShowCommand,
|
|
10
|
-
agentAddCommand,
|
|
11
|
-
agentEditCommand,
|
|
12
|
-
agentRemoveCommand,
|
|
13
|
-
} from './manage-agents.js';
|
|
1
|
+
/**
|
|
2
|
+
* Agent Commands
|
|
3
|
+
*/
|
|
4
|
+
export { dispatchAgentsCommand, buildDispatchConfig } from './dispatch-agents.js';
|
|
5
|
+
export { auditDispatchCommand } from './audit-dispatch.js';
|
|
6
|
+
export { detectAgentsCommand, detectAgents } from './detect-agents.js';
|
|
7
|
+
export {
|
|
8
|
+
agentListCommand,
|
|
9
|
+
agentShowCommand,
|
|
10
|
+
agentAddCommand,
|
|
11
|
+
agentEditCommand,
|
|
12
|
+
agentRemoveCommand,
|
|
13
|
+
} from './manage-agents.js';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* project-metrics — Project-level metrics aggregation CLI
|
|
3
|
+
*
|
|
4
|
+
* Aggregates feature data from state.json into a project-wide summary.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* morph-spec project-metrics [--json] [--save]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { loadState, stateExists } from '../../core/state/state-manager.js';
|
|
13
|
+
import { aggregateProjectMetrics } from '../../lib/agents/metrics-aggregator.js';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* CLI command: morph-spec project-metrics [--json] [--save]
|
|
18
|
+
*/
|
|
19
|
+
export async function projectMetricsCommand(options = {}) {
|
|
20
|
+
if (!stateExists()) {
|
|
21
|
+
console.error(chalk.red('No state.json found. Run morph-spec state init first.'));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const state = loadState(false);
|
|
27
|
+
if (!state) {
|
|
28
|
+
console.error(chalk.red('Failed to load state.json.'));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const metrics = aggregateProjectMetrics(state);
|
|
33
|
+
|
|
34
|
+
if (options.save) {
|
|
35
|
+
const morphDir = join(process.cwd(), '.morph');
|
|
36
|
+
if (!existsSync(morphDir)) {
|
|
37
|
+
mkdirSync(morphDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
const outPath = join(morphDir, 'metrics.json');
|
|
40
|
+
writeFileSync(outPath, JSON.stringify(metrics, null, 2) + '\n', 'utf8');
|
|
41
|
+
console.log(chalk.green(`Metrics saved to ${outPath}`));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (options.json) {
|
|
45
|
+
console.log(JSON.stringify(metrics, null, 2));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Human-readable table
|
|
50
|
+
const s = metrics.summary;
|
|
51
|
+
console.log(chalk.bold('\n Project Metrics'));
|
|
52
|
+
console.log(' ' + '─'.repeat(50));
|
|
53
|
+
console.log(` Features: ${s.totalFeatures} (${s.completedFeatures} completed)`);
|
|
54
|
+
console.log(` Tasks: ${s.completedTasks}/${s.totalTasks} (${s.completionRate}%)`);
|
|
55
|
+
console.log(` Avg tasks/feat: ${s.avgTasksPerFeature}`);
|
|
56
|
+
console.log(` Dispatches: ${s.totalDispatches}`);
|
|
57
|
+
|
|
58
|
+
if (metrics.features.length > 0) {
|
|
59
|
+
console.log(chalk.bold('\n Features:'));
|
|
60
|
+
console.log(` ${'Name'.padEnd(24)} ${'Phase'.padEnd(14)} ${'Tasks'.padEnd(10)} Dispatches`);
|
|
61
|
+
console.log(' ' + '─'.repeat(60));
|
|
62
|
+
for (const f of metrics.features) {
|
|
63
|
+
const taskStr = `${f.completedTasks}/${f.taskCount}`;
|
|
64
|
+
console.log(
|
|
65
|
+
` ${f.name.padEnd(24)} ${chalk.cyan(f.phase.padEnd(14))} ${taskStr.padEnd(10)} ${f.dispatchCount}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
console.log();
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -947,13 +947,12 @@ export async function doctorCommand(options = {}) {
|
|
|
947
947
|
logger.dim(' PowerShell (as Administrator):');
|
|
948
948
|
console.log(chalk.gray(` [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";${prefix}", "User")`));
|
|
949
949
|
logger.blank();
|
|
950
|
-
logger.dim('
|
|
951
|
-
logger.dim(' npx @polymorphism-tech/morph-spec init');
|
|
950
|
+
logger.dim(' Then open a new terminal.');
|
|
952
951
|
logger.blank();
|
|
953
952
|
} else if (!inPath) {
|
|
954
953
|
logger.info('morph-spec not in PATH:');
|
|
955
|
-
logger.dim('
|
|
956
|
-
logger.dim('
|
|
954
|
+
logger.dim(' Open a new terminal — the installer adds npm global to $PROFILE automatically.');
|
|
955
|
+
logger.dim(' If it persists, add manually: npm config get prefix → add to PATH');
|
|
957
956
|
logger.blank();
|
|
958
957
|
}
|
|
959
958
|
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
// src/commands/project/install-plugin-cmd.js
|
|
2
|
-
import { installPlugin } from '../../scripts/install-plugin.js';
|
|
3
|
-
import { logger } from '../../utils/logger.js';
|
|
4
|
-
|
|
5
|
-
export async function installPluginCommand(pluginName, _options) {
|
|
6
|
-
if (!pluginName) {
|
|
7
|
-
logger.error('Plugin name required. Usage: morph-spec install-plugin <name>');
|
|
8
|
-
logger.dim(' Available: superpowers, context7');
|
|
9
|
-
process.exit(1);
|
|
10
|
-
}
|
|
11
|
-
try {
|
|
12
|
-
const result = await installPlugin(pluginName);
|
|
13
|
-
if (result.alreadyInstalled) {
|
|
14
|
-
logger.dim(` ✓ ${pluginName} already installed (${result.version})`);
|
|
15
|
-
} else {
|
|
16
|
-
logger.success(` ✓ ${pluginName} installed (${result.version})`);
|
|
17
|
-
logger.dim(' Restart Claude Code to activate.');
|
|
18
|
-
}
|
|
19
|
-
} catch (err) {
|
|
20
|
-
logger.error(`Failed to install plugin "${pluginName}": ${err.message}`);
|
|
21
|
-
logger.blank();
|
|
22
|
-
logger.warn('Manual installation:');
|
|
23
|
-
logger.dim(' 1. Claude Code → Settings (Cmd/Ctrl+,) → Extensions');
|
|
24
|
-
logger.dim(` 2. Browse → search "${pluginName}" → Install`);
|
|
25
|
-
logger.dim(' 3. Restart Claude Code');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
1
|
+
// src/commands/project/install-plugin-cmd.js
|
|
2
|
+
import { installPlugin } from '../../scripts/install-plugin.js';
|
|
3
|
+
import { logger } from '../../utils/logger.js';
|
|
4
|
+
|
|
5
|
+
export async function installPluginCommand(pluginName, _options) {
|
|
6
|
+
if (!pluginName) {
|
|
7
|
+
logger.error('Plugin name required. Usage: morph-spec install-plugin <name>');
|
|
8
|
+
logger.dim(' Available: superpowers, context7');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const result = await installPlugin(pluginName);
|
|
13
|
+
if (result.alreadyInstalled) {
|
|
14
|
+
logger.dim(` ✓ ${pluginName} already installed (${result.version})`);
|
|
15
|
+
} else {
|
|
16
|
+
logger.success(` ✓ ${pluginName} installed (${result.version})`);
|
|
17
|
+
logger.dim(' Restart Claude Code to activate.');
|
|
18
|
+
}
|
|
19
|
+
} catch (err) {
|
|
20
|
+
logger.error(`Failed to install plugin "${pluginName}": ${err.message}`);
|
|
21
|
+
logger.blank();
|
|
22
|
+
logger.warn('Manual installation:');
|
|
23
|
+
logger.dim(' 1. Claude Code → Settings (Cmd/Ctrl+,) → Extensions');
|
|
24
|
+
logger.dim(` 2. Browse → search "${pluginName}" → Install`);
|
|
25
|
+
logger.dim(' 3. Restart Claude Code');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { setupInfra } from '../../scripts/setup-infra.js';
|
|
2
|
-
import { logger } from '../../utils/logger.js';
|
|
3
|
-
|
|
4
|
-
export async function setupInfraCommand(options) {
|
|
5
|
-
const targetPath = options.path || process.cwd();
|
|
6
|
-
try {
|
|
7
|
-
await setupInfra(targetPath);
|
|
8
|
-
} catch (err) {
|
|
9
|
-
logger.error(`setup-infra failed: ${err.message}`);
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
1
|
+
import { setupInfra } from '../../scripts/setup-infra.js';
|
|
2
|
+
import { logger } from '../../utils/logger.js';
|
|
3
|
+
|
|
4
|
+
export async function setupInfraCommand(options) {
|
|
5
|
+
const targetPath = options.path || process.cwd();
|
|
6
|
+
try {
|
|
7
|
+
await setupInfra(targetPath);
|
|
8
|
+
} catch (err) {
|
|
9
|
+
logger.error(`setup-infra failed: ${err.message}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -236,7 +236,14 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
236
236
|
process.exit(1);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
let feature;
|
|
240
|
+
try {
|
|
241
|
+
feature = getFeature(featureName);
|
|
242
|
+
} catch (err) {
|
|
243
|
+
console.error(chalk.red(`✗ Error: ${err.message}`));
|
|
244
|
+
console.error(chalk.gray('Run \'morph-spec doctor\' to diagnose or fix your state.json'));
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
240
247
|
if (!feature) {
|
|
241
248
|
console.error(chalk.red(`✗ Error: Feature '${featureName}' not found`));
|
|
242
249
|
console.error(chalk.gray(`Run 'morph-spec state get ${featureName}' to check if feature exists`));
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import chalk from 'chalk';
|
|
14
14
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
15
15
|
import { join } from 'path';
|
|
16
|
-
import { loadState, saveState } from '../../core/state/state-manager.js';
|
|
16
|
+
import { loadState, saveState, derivePhase } from '../../core/state/state-manager.js';
|
|
17
17
|
import { analyzeImpact } from '../../lib/scope/impact-analyzer.js';
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -35,8 +35,10 @@ export async function escalateScope(featureName, options) {
|
|
|
35
35
|
return { success: false, error: `Feature "${featureName}" not found in state.json` };
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const featurePath = join(process.cwd(), '.morph', 'features', featureName);
|
|
39
|
+
const currentPhase = feature.phase || derivePhase(featurePath);
|
|
40
|
+
if (currentPhase !== 'implement') {
|
|
41
|
+
return { success: false, error: `Scope escalation only available during implement phase (current: ${currentPhase})` };
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
// Read tasks.md
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context-budget — Token budget tracking for standards context
|
|
3
|
+
*
|
|
4
|
+
* Shows estimated token usage for agent briefings built from STANDARDS.json digests.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* morph-spec context-budget [--phase <phase>] [--json]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync, existsSync } from 'fs';
|
|
11
|
+
import { join, dirname } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
import { buildAgentBriefing, estimateTokens, clearCache } from '../../lib/standards/digest-builder.js';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const AGENTS_JSON_PATH = join(__dirname, '../../../framework/agents.json');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* CLI command: morph-spec context-budget [--phase <phase>] [--json]
|
|
21
|
+
*/
|
|
22
|
+
export async function contextBudgetCommand(options = {}) {
|
|
23
|
+
const phase = options.phase || 'implement';
|
|
24
|
+
|
|
25
|
+
if (!existsSync(AGENTS_JSON_PATH)) {
|
|
26
|
+
console.error(chalk.red('framework/agents.json not found.'));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
clearCache();
|
|
32
|
+
const agentsData = JSON.parse(readFileSync(AGENTS_JSON_PATH, 'utf8'));
|
|
33
|
+
const allAgents = agentsData.agents || {};
|
|
34
|
+
|
|
35
|
+
const results = [];
|
|
36
|
+
|
|
37
|
+
for (const [agentId, agentData] of Object.entries(allAgents)) {
|
|
38
|
+
if (agentId.startsWith('_comment')) continue;
|
|
39
|
+
if (!agentData.standards || agentData.standards.length === 0) continue;
|
|
40
|
+
|
|
41
|
+
const briefing = buildAgentBriefing(agentId, phase);
|
|
42
|
+
if (!briefing) continue;
|
|
43
|
+
|
|
44
|
+
const tokens = estimateTokens(briefing);
|
|
45
|
+
results.push({
|
|
46
|
+
agentId,
|
|
47
|
+
tier: agentData.tier,
|
|
48
|
+
standardsCount: agentData.standards.length,
|
|
49
|
+
tokens,
|
|
50
|
+
overBudget: tokens > 8000,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Sort by tokens descending
|
|
55
|
+
results.sort((a, b) => b.tokens - a.tokens);
|
|
56
|
+
|
|
57
|
+
const totalTokens = results.reduce((sum, r) => sum + r.tokens, 0);
|
|
58
|
+
const overBudget = results.filter(r => r.overBudget);
|
|
59
|
+
|
|
60
|
+
if (options.json) {
|
|
61
|
+
console.log(JSON.stringify({ phase, totalTokens, overBudgetCount: overBudget.length, agents: results }, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Human-readable
|
|
66
|
+
console.log(chalk.bold(`\n Context Budget — phase: ${phase}`));
|
|
67
|
+
console.log(' ' + '─'.repeat(62));
|
|
68
|
+
console.log(` ${'Agent'.padEnd(32)} ${'Tier'.padEnd(6)} ${'Stds'.padEnd(6)} Tokens`);
|
|
69
|
+
console.log(' ' + '─'.repeat(62));
|
|
70
|
+
|
|
71
|
+
for (const r of results) {
|
|
72
|
+
const tokenStr = r.overBudget ? chalk.red(String(r.tokens)) : String(r.tokens);
|
|
73
|
+
const flag = r.overBudget ? ' ⚠' : '';
|
|
74
|
+
console.log(
|
|
75
|
+
` ${r.agentId.padEnd(32)} ${String(r.tier).padEnd(6)} ${String(r.standardsCount).padEnd(6)} ${tokenStr}${flag}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(' ' + '─'.repeat(62));
|
|
80
|
+
console.log(` Total: ${results.length} agents, ${totalTokens} tokens`);
|
|
81
|
+
if (overBudget.length > 0) {
|
|
82
|
+
console.log(chalk.yellow(` ⚠ ${overBudget.length} agent(s) exceed 8000 token threshold`));
|
|
83
|
+
}
|
|
84
|
+
console.log();
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task Management Commands
|
|
3
|
-
*/
|
|
4
|
-
export { taskCommand } from './task.js';
|
|
1
|
+
/**
|
|
2
|
+
* Task Management Commands
|
|
3
|
+
*/
|
|
4
|
+
export { taskCommand } from './task.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template Commands
|
|
3
|
-
*/
|
|
4
|
-
export { templateRenderCommand } from './template-render.js';
|
|
5
|
-
export { generateContractsCommand, parseSchemaAnalysisMd } from './generate-contracts.js';
|
|
1
|
+
/**
|
|
2
|
+
* Template Commands
|
|
3
|
+
*/
|
|
4
|
+
export { templateRenderCommand } from './template-render.js';
|
|
5
|
+
export { generateContractsCommand, parseSchemaAnalysisMd } from './generate-contracts.js';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility Commands — no active commands after CLI simplification
|
|
3
|
-
*/
|
|
1
|
+
/**
|
|
2
|
+
* Utility Commands — no active commands after CLI simplification
|
|
3
|
+
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validation Commands
|
|
3
|
-
*/
|
|
4
|
-
export { validateFeatureCommand } from './validate-feature.js';
|
|
1
|
+
/**
|
|
2
|
+
* Validation Commands
|
|
3
|
+
*/
|
|
4
|
+
export { validateFeatureCommand } from './validate-feature.js';
|
|
@@ -70,6 +70,19 @@ const PHASE_OUTPUTS = {
|
|
|
70
70
|
implement: ['spec.md', 'contracts.cs', 'tasks.md']
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Phase-to-folder mapping (filesystem convention)
|
|
75
|
+
*/
|
|
76
|
+
const PHASE_FOLDERS = {
|
|
77
|
+
proposal: '0-proposal',
|
|
78
|
+
uiux: '2-ui',
|
|
79
|
+
design: '1-design',
|
|
80
|
+
clarify: '1-design',
|
|
81
|
+
plan: '3-plan',
|
|
82
|
+
tasks: '4-tasks',
|
|
83
|
+
implement: '5-implement',
|
|
84
|
+
};
|
|
85
|
+
|
|
73
86
|
/**
|
|
74
87
|
* Validate a single output file
|
|
75
88
|
*/
|
|
@@ -254,12 +267,28 @@ export async function validateFeatureCommand(featureName, options = {}) {
|
|
|
254
267
|
process.exit(1);
|
|
255
268
|
}
|
|
256
269
|
} else {
|
|
257
|
-
// Validate all existing outputs
|
|
258
|
-
outputFiles =
|
|
270
|
+
// Validate all existing outputs across all phase folders
|
|
271
|
+
outputFiles = [];
|
|
272
|
+
const entries = fs.readdirSync(featurePath, { withFileTypes: true });
|
|
273
|
+
for (const entry of entries) {
|
|
274
|
+
if (entry.isDirectory()) {
|
|
275
|
+
const subDir = path.join(featurePath, entry.name);
|
|
276
|
+
const files = fs.readdirSync(subDir).filter(f => f.endsWith('.md') || f.endsWith('.cs'));
|
|
277
|
+
outputFiles.push(...files.map(f => ({ file: f, dir: subDir })));
|
|
278
|
+
} else if (entry.name.endsWith('.md') || entry.name.endsWith('.cs')) {
|
|
279
|
+
outputFiles.push({ file: entry.name, dir: featurePath });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
259
282
|
}
|
|
260
283
|
|
|
284
|
+
// Resolve phase folder for path lookup
|
|
285
|
+
const phaseFolder = phase ? PHASE_FOLDERS[phase] : null;
|
|
286
|
+
const searchPath = phaseFolder ? path.join(featurePath, phaseFolder) : featurePath;
|
|
287
|
+
|
|
261
288
|
// Run validation
|
|
262
|
-
const results =
|
|
289
|
+
const results = phase
|
|
290
|
+
? outputFiles.map(file => validateOutput(searchPath, file))
|
|
291
|
+
: outputFiles.map(({ file, dir }) => validateOutput(dir, file));
|
|
263
292
|
const schemaWarnings = crossValidateSchema(featurePath);
|
|
264
293
|
|
|
265
294
|
// Aggregate results
|