@polymorphism-tech/morph-spec 9.0.0 → 9.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 +8 -9
- package/README.md +38 -36
- package/bin/morph-spec.js +4 -62
- package/bin/validate.js +1 -39
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +1 -1
- package/framework/MORPH.md +1 -1
- package/framework/agents.json +12 -4
- package/framework/commands/morph-apply.md +1 -1
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +6 -17
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +2 -16
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
- package/framework/hooks/claude-code/core/gate-guard.js +7 -67
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
- package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
- package/framework/hooks/claude-code/core/state-sync.js +11 -67
- package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
- package/framework/hooks/claude-code/core/validate-completion.js +11 -57
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
- package/framework/hooks/claude-code/core/worktree-context.js +10 -110
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +6 -75
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +6 -51
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +6 -25
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +2 -15
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +1 -3
- package/framework/hooks/shared/gate-question.js +1 -7
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +1 -42
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +4 -23
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/state-reader.js +13 -183
- package/framework/hooks/shared/state-writer.js +8 -52
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -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 +2 -2
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +2 -2
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +29 -12
- package/framework/standards/STANDARDS.json +34 -12
- package/framework/standards/STANDARDS.meta.json +16 -2
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +2 -2
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +1 -1
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/REGISTRY.json +9 -9
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +17 -104
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +5 -57
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor.js +48 -302
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +3 -32
- package/src/commands/project/init.js +4 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/standards.js +1 -62
- package/src/commands/project/status.js +0 -53
- package/src/commands/project/update.js +31 -239
- package/src/commands/project/worktree.js +108 -882
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +14 -85
- package/src/commands/state/approve.js +14 -96
- package/src/commands/state/archive.js +12 -64
- package/src/commands/state/auto-approval.js +2 -5
- package/src/commands/state/create.js +22 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +98 -475
- package/src/commands/state/gate-check.js +22 -132
- package/src/commands/state/gate-decision.js +3 -39
- package/src/commands/state/handoff.js +3 -30
- package/src/commands/state/list.js +14 -84
- package/src/commands/state/report-check.js +8 -58
- package/src/commands/state/retype.js +4 -26
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +2 -5
- package/src/commands/validation/validate-feature.js +5 -94
- package/src/commands/validation/verify.js +15 -98
- package/src/core/paths/output-schema.js +2 -101
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +0 -27
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +22 -304
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +0 -1
- package/src/core/work-types.js +1 -36
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
- package/src/dashboard/app/components/CommandPalette.jsx +0 -7
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +1 -5
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
- package/src/dashboard/app/pages/Features.jsx +1 -8
- package/src/dashboard/app/pages/Guide.jsx +0 -1
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +10 -69
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +0 -6
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +10 -66
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +2 -6
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +4 -19
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +4 -50
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +9 -53
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +4 -37
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +1 -0
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +3 -15
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +1 -1
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +4 -27
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/dotnet-introspection.js +20 -106
- package/src/lib/mutation/subset.js +1 -4
- package/src/lib/plan/artifact-vocabulary.js +5 -25
- package/src/lib/plan/checks.js +5 -31
- package/src/lib/plan/plan-parser.js +1 -6
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +5 -23
- package/src/lib/risk-detector.js +39 -173
- package/src/lib/specs/adr-index.js +6 -34
- package/src/lib/specs/frontmatter.js +2 -15
- package/src/lib/specs/gfm-table.js +1 -7
- package/src/lib/specs/revocations.js +1 -14
- package/src/lib/specs/screen-checklist.js +47 -124
- package/src/lib/stack/dotnet-build-ignores.js +2 -6
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +1 -8
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +1 -7
- package/src/lib/standards/sections.js +2 -13
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +21 -169
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -240
- package/src/lib/tasks/test-runner.js +43 -341
- package/src/lib/telemetry/metrics.js +7 -36
- package/src/lib/telemetry/telemetry-reader.js +13 -125
- package/src/lib/telemetry/verify-event.js +2 -30
- package/src/lib/validators/code-budget/code-budget-validator.js +3 -43
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +11 -101
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +5 -63
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +52 -358
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -136
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +13 -95
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -147
- package/src/utils/agents-installer.js +28 -275
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -52
- package/src/utils/file-copier.js +11 -95
- package/src/utils/hooks-installer.js +21 -298
- package/src/utils/skills-installer.js +4 -101
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -9
- package/src/commands/utils/index.js +0 -3
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/old-format.js +0 -68
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,18 +1,3 @@
|
|
|
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-plan, morph-uiux, morph-apply, morph-review, morph-standards…). The 3-tier system was collapsed
|
|
7
|
-
* into a single source of truth in framework/skills/core/.
|
|
8
|
-
*
|
|
9
|
-
* Source skills are directory-based: framework/skills/core/{skill-name}/SKILL.md
|
|
10
|
-
* (plus optional scripts/, references/, assets/ subdirectories).
|
|
11
|
-
*
|
|
12
|
-
* Directory-based skills have their entire directory copied so Claude Code can
|
|
13
|
-
* execute scripts and load references on demand.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
1
|
import { existsSync, readdirSync, statSync, readFileSync } from 'fs';
|
|
17
2
|
import { basename, join } from 'path';
|
|
18
3
|
import { fileURLToPath } from 'url';
|
|
@@ -22,59 +7,16 @@ import { realEffects } from '../lib/installers/effects.js';
|
|
|
22
7
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
23
8
|
const FRAMEWORK_SKILLS_DIR = join(__dirname, '..', '..', 'framework', 'skills');
|
|
24
9
|
|
|
25
|
-
/**
|
|
26
|
-
* Skill levels to install in .claude/skills/.
|
|
27
|
-
* Only `core/` survives — single source of truth aligned with MORPH.md.
|
|
28
|
-
*/
|
|
29
10
|
const SKILL_LEVELS_TO_INSTALL = ['core'];
|
|
30
11
|
|
|
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
12
|
export function shouldInstallSkill(skillContent, projectTags) {
|
|
38
13
|
return shouldInstall(parseStacks(skillContent), projectTags);
|
|
39
14
|
}
|
|
40
15
|
|
|
41
|
-
/**
|
|
42
|
-
* Copy a skill directory tree from src to dest, skipping README.md files.
|
|
43
|
-
*
|
|
44
|
-
* UM efeito por skill (não um por arquivo): é assim que o instalador pensa
|
|
45
|
-
* ("instalar a skill X"), e é o que o relatório do `--dry-run` precisa dizer.
|
|
46
|
-
*
|
|
47
|
-
* @param {string} src - Source directory path
|
|
48
|
-
* @param {string} dest - Destination directory path
|
|
49
|
-
* @param {ReturnType<import('../lib/installers/effects.js').createEffects>} fx
|
|
50
|
-
*/
|
|
51
16
|
function copySkillDirectory(src, dest, fx) {
|
|
52
17
|
fx.copyDir(src, dest, { filter: (from) => basename(from) !== 'README.md' });
|
|
53
18
|
}
|
|
54
19
|
|
|
55
|
-
/**
|
|
56
|
-
* Walk srcDir and install skills into destDir.
|
|
57
|
-
*
|
|
58
|
-
* Handles two source formats:
|
|
59
|
-
* 1. Directory-based skill: srcDir/{skill-name}/SKILL.md
|
|
60
|
-
* → copies entire directory (SKILL.md + scripts/ + references/ + assets/)
|
|
61
|
-
* → destDir/{skill-name}/
|
|
62
|
-
*
|
|
63
|
-
* 2. Flat .md skill (legacy): srcDir/{skill-name}.md
|
|
64
|
-
* → destDir/{skill-name}/SKILL.md
|
|
65
|
-
*
|
|
66
|
-
* The `core/` level directory is traversed recursively. A directory is
|
|
67
|
-
* recognized as a skill directory when it contains a SKILL.md file.
|
|
68
|
-
*
|
|
69
|
-
* @param {string} srcDir - Source directory to walk
|
|
70
|
-
* @param {string} destDir - Destination base directory (.claude/skills/)
|
|
71
|
-
* @param {string[]} projectTags - Stack tags from config
|
|
72
|
-
* @param {Set<string>} installedNow - Accumulator: skill names this call
|
|
73
|
-
* actually (re)installed. NOT derived by re-scanning destDir afterwards —
|
|
74
|
-
* a disk scan can't distinguish "installed this run" from "leftover from a
|
|
75
|
-
* previous run with different projectTags", which is exactly the stale
|
|
76
|
-
* entry a stack change needs cleaned up.
|
|
77
|
-
*/
|
|
78
20
|
function installSkillsFromDir(srcDir, destDir, projectTags, installedNow, fx) {
|
|
79
21
|
const entries = readdirSync(srcDir);
|
|
80
22
|
for (const entry of entries) {
|
|
@@ -84,20 +26,15 @@ function installSkillsFromDir(srcDir, destDir, projectTags, installedNow, fx) {
|
|
|
84
26
|
if (stat.isDirectory()) {
|
|
85
27
|
const skillMdPath = join(srcPath, 'SKILL.md');
|
|
86
28
|
if (existsSync(skillMdPath)) {
|
|
87
|
-
// Directory-based skill — check stack filter before copying.
|
|
88
|
-
// O filtro é PURO e continua sendo o único: previsão e execução
|
|
89
|
-
// percorrem esta mesma linha.
|
|
90
29
|
const content = readFileSync(skillMdPath, 'utf-8');
|
|
91
30
|
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
92
31
|
const skillDestDir = join(destDir, entry);
|
|
93
32
|
copySkillDirectory(srcPath, skillDestDir, fx);
|
|
94
33
|
installedNow.add(entry);
|
|
95
34
|
} else {
|
|
96
|
-
// Level/category directory — recurse
|
|
97
35
|
installSkillsFromDir(srcPath, destDir, projectTags, installedNow, fx);
|
|
98
36
|
}
|
|
99
37
|
} else if (entry.endsWith('.md') && entry !== 'README.md') {
|
|
100
|
-
// Legacy flat .md skill — check stack filter before copying
|
|
101
38
|
const content = readFileSync(srcPath, 'utf-8');
|
|
102
39
|
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
103
40
|
const skillName = entry.slice(0, -3);
|
|
@@ -131,32 +68,12 @@ function collectNamesFromDir(dir, names) {
|
|
|
131
68
|
}
|
|
132
69
|
}
|
|
133
70
|
|
|
134
|
-
/**
|
|
135
|
-
* Install morph framework skills to .claude/skills/ in the target project.
|
|
136
|
-
* Skills are copied into subdirectories as SKILL.md files so Claude Code can
|
|
137
|
-
* discover them natively via /<skill-name> syntax (e.g., /phase-design,
|
|
138
|
-
* /nextjs-expert). Directory-based skills also get their scripts/,
|
|
139
|
-
* references/, and assets/ subdirectories copied for on-demand loading.
|
|
140
|
-
*
|
|
141
|
-
* Stack filtering: if projectTags is provided, only skills whose `stacks:`
|
|
142
|
-
* frontmatter matches the project tags are installed. Skills without a
|
|
143
|
-
* `stacks:` field are always installed (universal).
|
|
144
|
-
*
|
|
145
|
-
* @param {string} projectDir - Target project root directory
|
|
146
|
-
* @param {Object} [options]
|
|
147
|
-
* @param {string[]} [options.projectTags=[]] - Stack tags from config
|
|
148
|
-
* @param {ReturnType<import('../lib/installers/effects.js').createEffects>} [options.fx]
|
|
149
|
-
* Coletor de efeitos; o default executa e registra numa lista descartada.
|
|
150
|
-
*/
|
|
151
71
|
export async function installSkills(projectDir, { projectTags = [], fx = realEffects() } = {}) {
|
|
152
72
|
const claudeSkillsDir = join(projectDir, '.claude', 'skills');
|
|
153
73
|
fx.ensureDir(claudeSkillsDir);
|
|
154
74
|
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
// can't tell "installed this run" apart from "leftover from a previous
|
|
158
|
-
// run with different projectTags", which is exactly the stale entry a
|
|
159
|
-
// stack change (or a re-run that narrows the stack) needs cleaned up.
|
|
75
|
+
// Tracked as we go: a disk scan can't tell this run's installs from leftovers
|
|
76
|
+
// of a run with other projectTags, which a stack change must clean up.
|
|
160
77
|
const installedNow = new Set();
|
|
161
78
|
for (const level of SKILL_LEVELS_TO_INSTALL) {
|
|
162
79
|
const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
|
|
@@ -164,22 +81,8 @@ export async function installSkills(projectDir, { projectTags = [], fx = realEff
|
|
|
164
81
|
installSkillsFromDir(levelDir, claudeSkillsDir, projectTags, installedNow, fx);
|
|
165
82
|
}
|
|
166
83
|
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
// run — i.e. it was filtered out by a `stacks:` mismatch (project.stack
|
|
170
|
-
// changed, or a re-run narrowed the tags). Previously scoped to `morph-`
|
|
171
|
-
// prefixed names only, which silently exempted every non-`morph-` skill
|
|
172
|
-
// (the Umbraco pair) from ever being cleaned up: install once with
|
|
173
|
-
// `umbraco` in the stack, drop the stack later, and they'd survive every
|
|
174
|
-
// reinstall/update forever. Names outside allSourceSkills (user-added,
|
|
175
|
-
// non-framework skills) are never touched.
|
|
176
|
-
//
|
|
177
|
-
// Em `dryRun` este laço NÃO emite nada, e a razão está na D6: numa execução
|
|
178
|
-
// real do `update` ele sempre vê um diretório VAZIO, porque
|
|
179
|
-
// `cleanFrameworkDirs()` acabou de apagar `.claude/skills/` inteiro. Num dry
|
|
180
|
-
// ingênuo nada foi apagado, ele veria o diretório POPULADO, e o relatório
|
|
181
|
-
// preveria remoções que a execução real nunca faz — errado na direção que
|
|
182
|
-
// assusta o usuário. O clean subsome este laço, provadamente.
|
|
84
|
+
// Removes known framework skills not reinstalled this run; user skills are never touched.
|
|
85
|
+
// Silent in dryRun: the real update's cleanFrameworkDirs() already emptied the dir.
|
|
183
86
|
if (!fx.dryRun) {
|
|
184
87
|
const allSourceSkills = collectSourceSkillNames();
|
|
185
88
|
for (const entry of readdirSync(claudeSkillsDir)) {
|
|
@@ -6,10 +6,6 @@ import { realEffects } from '../lib/installers/effects.js';
|
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* Get the current CLI version from package.json
|
|
11
|
-
* @returns {string} Version string (e.g., "1.0.4")
|
|
12
|
-
*/
|
|
13
9
|
export function getInstalledCLIVersion() {
|
|
14
10
|
try {
|
|
15
11
|
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
@@ -20,11 +16,6 @@ export function getInstalledCLIVersion() {
|
|
|
20
16
|
}
|
|
21
17
|
}
|
|
22
18
|
|
|
23
|
-
/**
|
|
24
|
-
* Get the latest version from npm registry
|
|
25
|
-
* @param {string} packageName - The package name (default: @polymorphism-tech/morph-spec)
|
|
26
|
-
* @returns {Promise<string|null>} Latest version or null if failed
|
|
27
|
-
*/
|
|
28
19
|
export async function getLatestNPMVersion(packageName = '@polymorphism-tech/morph-spec') {
|
|
29
20
|
try {
|
|
30
21
|
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
|
|
@@ -36,11 +27,6 @@ export async function getLatestNPMVersion(packageName = '@polymorphism-tech/morp
|
|
|
36
27
|
}
|
|
37
28
|
}
|
|
38
29
|
|
|
39
|
-
/**
|
|
40
|
-
* Get the MORPH version installed in the project
|
|
41
|
-
* @param {string} projectPath - Path to the project
|
|
42
|
-
* @returns {Promise<object|null>} Version info object or null if not found
|
|
43
|
-
*/
|
|
44
30
|
export async function getProjectMorphVersion(projectPath) {
|
|
45
31
|
try {
|
|
46
32
|
const versionPath = join(projectPath, '.morph', '.morphversion');
|
|
@@ -53,12 +39,6 @@ export async function getProjectMorphVersion(projectPath) {
|
|
|
53
39
|
}
|
|
54
40
|
}
|
|
55
41
|
|
|
56
|
-
/**
|
|
57
|
-
* Save the MORPH version to the project
|
|
58
|
-
* @param {string} projectPath - Path to the project
|
|
59
|
-
* @param {string} version - Version to save
|
|
60
|
-
* @returns {Promise<void>}
|
|
61
|
-
*/
|
|
62
42
|
export async function saveProjectMorphVersion(projectPath, version, fx = realEffects()) {
|
|
63
43
|
const versionPath = join(projectPath, '.morph', '.morphversion');
|
|
64
44
|
const versionInfo = {
|
|
@@ -69,12 +49,7 @@ export async function saveProjectMorphVersion(projectPath, version, fx = realEff
|
|
|
69
49
|
fx.writeJson(versionPath, versionInfo);
|
|
70
50
|
}
|
|
71
51
|
|
|
72
|
-
/**
|
|
73
|
-
* Compare two semver versions
|
|
74
|
-
* @param {string} v1 - First version
|
|
75
|
-
* @param {string} v2 - Second version
|
|
76
|
-
* @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
77
|
-
*/
|
|
52
|
+
/** @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2 */
|
|
78
53
|
export function compareVersions(v1, v2) {
|
|
79
54
|
if (!v1 || !v2) return 0;
|
|
80
55
|
|
|
@@ -92,49 +67,17 @@ export function compareVersions(v1, v2) {
|
|
|
92
67
|
return 0;
|
|
93
68
|
}
|
|
94
69
|
|
|
95
|
-
/**
|
|
96
|
-
* Major de uma versão, ou `null` quando a string não permite decidir.
|
|
97
|
-
*
|
|
98
|
-
* Vive aqui, ao lado de `compareVersions`, de propósito: uma segunda
|
|
99
|
-
* aritmética de versão noutro arquivo é exatamente como duas verdades sobre a
|
|
100
|
-
* mesma versão nascem.
|
|
101
|
-
*
|
|
102
|
-
* @param {string|null|undefined} version
|
|
103
|
-
* @returns {number|null}
|
|
104
|
-
*/
|
|
70
|
+
/** @returns {number|null} `null` when the string does not determine a major */
|
|
105
71
|
export function majorOf(version) {
|
|
106
72
|
if (typeof version !== 'string') return null;
|
|
107
73
|
const m = /^\s*v?(\d+)\./.exec(version) ?? /^\s*v?(\d+)\s*$/.exec(version);
|
|
108
74
|
return m ? Number(m[1]) : null;
|
|
109
75
|
}
|
|
110
76
|
|
|
111
|
-
/**
|
|
112
|
-
* Limiar do freio: um salto de DOIS majors ou mais recusa sem `--force`.
|
|
113
|
-
*
|
|
114
|
-
* O limiar não é "quantos majors dá para pular com segurança" — é "a partir de
|
|
115
|
-
* onde o usuário merece ler o relatório antes". Medido: um projeto em 7.x indo
|
|
116
|
-
* para 9.x recusa; um em 8.x atualiza sem atrito.
|
|
117
|
-
*/
|
|
118
77
|
export const MAJOR_JUMP_LIMIT = 2;
|
|
119
78
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
*
|
|
123
|
-
* Três resultados, e a diferença entre eles é o que a mensagem tem de dizer:
|
|
124
|
-
*
|
|
125
|
-
* - `reason: 'unknown-installed'` — a versão instalada é indeterminável.
|
|
126
|
-
* Tratada como salto grande, com motivo PRÓPRIO: "não sei quão velho isto é"
|
|
127
|
-
* é precisamente quando sobrescrever às cegas é mais perigoso, e tratá-la
|
|
128
|
-
* como zero seria a única leitura que libera o pior caso.
|
|
129
|
-
* - `reason: 'major-jump'` — salto medido de `jump` majors, >= ao limiar.
|
|
130
|
-
* - `reason: null` — sem freio (mesmo major, um só major de diferença, ou
|
|
131
|
-
* destino não superior à instalada).
|
|
132
|
-
*
|
|
133
|
-
* @param {string|null} installedVersion
|
|
134
|
-
* @param {string|null} targetVersion
|
|
135
|
-
* @param {{ limit?: number }} [options]
|
|
136
|
-
* @returns {{ refuse: boolean, reason: 'unknown-installed'|'major-jump'|null, jump: number|null }}
|
|
137
|
-
*/
|
|
79
|
+
// An unreadable installed version refuses with its own reason: not knowing how old
|
|
80
|
+
// the project is is when a blind overwrite is most dangerous.
|
|
138
81
|
export function majorJump(installedVersion, targetVersion, { limit = MAJOR_JUMP_LIMIT } = {}) {
|
|
139
82
|
const installedMajor = majorOf(installedVersion);
|
|
140
83
|
const targetMajor = majorOf(targetVersion);
|
|
@@ -143,8 +86,6 @@ export function majorJump(installedVersion, targetVersion, { limit = MAJOR_JUMP_
|
|
|
143
86
|
return { refuse: true, reason: 'unknown-installed', jump: null };
|
|
144
87
|
}
|
|
145
88
|
if (targetMajor === null) {
|
|
146
|
-
// Sem destino legível não há salto a medir — e inventar um seria decidir
|
|
147
|
-
// por um número que ninguém mantém.
|
|
148
89
|
return { refuse: false, reason: null, jump: null };
|
|
149
90
|
}
|
|
150
91
|
if (compareVersions(installedVersion, targetVersion) >= 0) {
|
|
@@ -156,17 +97,11 @@ export function majorJump(installedVersion, targetVersion, { limit = MAJOR_JUMP_
|
|
|
156
97
|
return { refuse: false, reason: null, jump };
|
|
157
98
|
}
|
|
158
99
|
|
|
159
|
-
/**
|
|
160
|
-
* Detect how the CLI was installed
|
|
161
|
-
* @returns {'global'|'npx'|'unknown'}
|
|
162
|
-
*/
|
|
163
100
|
export function detectInstallMethod() {
|
|
164
|
-
// Check if running via npx
|
|
165
101
|
if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) {
|
|
166
102
|
return 'npx';
|
|
167
103
|
}
|
|
168
104
|
|
|
169
|
-
// Check if installed globally
|
|
170
105
|
const isGlobal = process.env.npm_config_global === 'true' ||
|
|
171
106
|
process.execPath.includes('node_modules');
|
|
172
107
|
|
|
@@ -177,11 +112,6 @@ export function detectInstallMethod() {
|
|
|
177
112
|
return 'unknown';
|
|
178
113
|
}
|
|
179
114
|
|
|
180
|
-
/**
|
|
181
|
-
* Get update instructions based on install method
|
|
182
|
-
* @param {string} method - Install method ('global', 'npx', 'unknown')
|
|
183
|
-
* @returns {string[]} Array of instruction strings
|
|
184
|
-
*/
|
|
185
115
|
export function getUpdateInstructions(method) {
|
|
186
116
|
switch (method) {
|
|
187
117
|
case 'global':
|
|
@@ -198,10 +128,6 @@ export function getUpdateInstructions(method) {
|
|
|
198
128
|
}
|
|
199
129
|
}
|
|
200
130
|
|
|
201
|
-
/**
|
|
202
|
-
* Check if CLI version is outdated
|
|
203
|
-
* @returns {Promise<{isOutdated: boolean, current: string, latest: string|null}>}
|
|
204
|
-
*/
|
|
205
131
|
export async function checkCLIOutdated() {
|
|
206
132
|
const current = getInstalledCLIVersion();
|
|
207
133
|
const latest = await getLatestNPMVersion();
|
|
@@ -215,11 +141,6 @@ export async function checkCLIOutdated() {
|
|
|
215
141
|
return { isOutdated, current, latest };
|
|
216
142
|
}
|
|
217
143
|
|
|
218
|
-
/**
|
|
219
|
-
* Check if project MORPH version is outdated
|
|
220
|
-
* @param {string} projectPath - Path to the project
|
|
221
|
-
* @returns {Promise<{isOutdated: boolean, current: string|null, cliVersion: string}>}
|
|
222
|
-
*/
|
|
223
144
|
export async function checkProjectOutdated(projectPath) {
|
|
224
145
|
const cliVersion = getInstalledCLIVersion();
|
|
225
146
|
const projectVersion = await getProjectMorphVersion(projectPath);
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Inicia ou retoma uma feature MORPH-SPEC — entrevista, plan.md, plan-check e gate do plano.
|
|
3
|
-
argument-hint: <feature-name>
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Agent, AskUserQuestion, Skill
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
Invoque `Skill(morph-plan)` com o argumento abaixo e siga a skill.
|
|
8
|
-
|
|
9
|
-
**Feature:** $ARGUMENTS
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Design System Detector
|
|
3
|
-
*
|
|
4
|
-
* Detects if a design system exists for the project or feature.
|
|
5
|
-
* Checks multiple locations in priority order.
|
|
6
|
-
*
|
|
7
|
-
* @module design-system-detector
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
11
|
-
import { join } from 'path';
|
|
12
|
-
|
|
13
|
-
const SKIP_DIRS = new Set(['node_modules', '.git', '.morph', 'dist', 'build', '.next', 'obj', 'bin', '.vs']);
|
|
14
|
-
const CSS_SCAN_MAX_DEPTH = 6;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Recursively walk a directory collecting files with given extensions.
|
|
18
|
-
* @param {string} dir - Directory to walk
|
|
19
|
-
* @param {string[]} extensions - File extensions to collect (e.g. ['.css', '.scss'])
|
|
20
|
-
* @param {string[]} files - Accumulator array (mutated in-place)
|
|
21
|
-
* @param {number} depth - Current recursion depth
|
|
22
|
-
* @param {number} maxDepth - Maximum recursion depth
|
|
23
|
-
*/
|
|
24
|
-
function walkDirForCss(dir, extensions, files, depth, maxDepth) {
|
|
25
|
-
if (depth > maxDepth) return;
|
|
26
|
-
let entries;
|
|
27
|
-
try {
|
|
28
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
29
|
-
} catch {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
for (const entry of entries) {
|
|
33
|
-
if (entry.isDirectory()) {
|
|
34
|
-
if (!SKIP_DIRS.has(entry.name)) {
|
|
35
|
-
walkDirForCss(join(dir, entry.name), extensions, files, depth + 1, maxDepth);
|
|
36
|
-
}
|
|
37
|
-
} else if (entry.isFile()) {
|
|
38
|
-
const lower = entry.name.toLowerCase();
|
|
39
|
-
if (extensions.some(ext => lower.endsWith(ext))) {
|
|
40
|
-
files.push(join(dir, entry.name));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Detect design system for project or feature
|
|
48
|
-
* @param {string} projectPath - Root path of the project
|
|
49
|
-
* @param {string|null} featureName - Feature name (optional, checks feature-level if provided)
|
|
50
|
-
* @returns {Object} { hasDesignSystem, path, isProjectLevel, isFeatureLevel, source }
|
|
51
|
-
*/
|
|
52
|
-
export function detectDesignSystem(projectPath, featureName = null) {
|
|
53
|
-
const result = {
|
|
54
|
-
hasDesignSystem: false,
|
|
55
|
-
path: null,
|
|
56
|
-
isProjectLevel: false,
|
|
57
|
-
isFeatureLevel: false,
|
|
58
|
-
source: null
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Priority 1: Feature-level design system (if feature specified)
|
|
62
|
-
if (featureName) {
|
|
63
|
-
const featureDesignSystemPath = join(
|
|
64
|
-
projectPath,
|
|
65
|
-
'.morph/features',
|
|
66
|
-
featureName,
|
|
67
|
-
'1-design',
|
|
68
|
-
'design-system.md'
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
if (existsSync(featureDesignSystemPath)) {
|
|
72
|
-
return {
|
|
73
|
-
hasDesignSystem: true,
|
|
74
|
-
path: featureDesignSystemPath,
|
|
75
|
-
isProjectLevel: false,
|
|
76
|
-
isFeatureLevel: true,
|
|
77
|
-
source: 'feature-output'
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Priority 2: Project-level design system (markdown)
|
|
83
|
-
const projectDesignSystemPath = join(projectPath, '.morph/context/design-system.md');
|
|
84
|
-
if (existsSync(projectDesignSystemPath)) {
|
|
85
|
-
return {
|
|
86
|
-
hasDesignSystem: true,
|
|
87
|
-
path: projectDesignSystemPath,
|
|
88
|
-
isProjectLevel: true,
|
|
89
|
-
isFeatureLevel: false,
|
|
90
|
-
source: 'project-markdown'
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Priority 3: Generated CSS design system
|
|
95
|
-
const cssDesignSystemPath = join(projectPath, 'wwwroot/css/design-system.css');
|
|
96
|
-
if (existsSync(cssDesignSystemPath)) {
|
|
97
|
-
return {
|
|
98
|
-
hasDesignSystem: true,
|
|
99
|
-
path: cssDesignSystemPath,
|
|
100
|
-
isProjectLevel: true,
|
|
101
|
-
isFeatureLevel: false,
|
|
102
|
-
source: 'generated-css'
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Priority 4: Recursively scan frontend root directories for CSS/SCSS with design tokens
|
|
107
|
-
const frontendRoots = ['wwwroot', 'styles', 'src/styles', 'src', 'public'];
|
|
108
|
-
const extensions = ['.css', '.scss'];
|
|
109
|
-
const collected = [];
|
|
110
|
-
|
|
111
|
-
for (const root of frontendRoots) {
|
|
112
|
-
const rootPath = join(projectPath, root);
|
|
113
|
-
if (existsSync(rootPath)) {
|
|
114
|
-
walkDirForCss(rootPath, extensions, collected, 0, CSS_SCAN_MAX_DEPTH);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Deduplicate by resolved path
|
|
119
|
-
const seen = new Set();
|
|
120
|
-
const cssFiles = [];
|
|
121
|
-
for (const file of collected) {
|
|
122
|
-
if (!seen.has(file)) {
|
|
123
|
-
seen.add(file);
|
|
124
|
-
cssFiles.push(file);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Sort for deterministic first-match across platforms
|
|
129
|
-
cssFiles.sort();
|
|
130
|
-
|
|
131
|
-
for (const cssFile of cssFiles) {
|
|
132
|
-
try {
|
|
133
|
-
const content = readFileSync(cssFile, 'utf8');
|
|
134
|
-
const hasDesignTokens = /:root\s*\{[\s\S]*?--/.test(content);
|
|
135
|
-
if (hasDesignTokens) {
|
|
136
|
-
return {
|
|
137
|
-
hasDesignSystem: true,
|
|
138
|
-
path: cssFile,
|
|
139
|
-
isProjectLevel: true,
|
|
140
|
-
isFeatureLevel: false,
|
|
141
|
-
source: 'existing-css-variables'
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
} catch {
|
|
145
|
-
// Continue checking other files
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// No design system found
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Get recommended design system locations based on project type
|
|
155
|
-
* @param {string} projectPath - Root path of the project
|
|
156
|
-
* @returns {Object} { markdown, css, description }
|
|
157
|
-
*/
|
|
158
|
-
export function getRecommendedLocations(projectPath) {
|
|
159
|
-
try {
|
|
160
|
-
const configPath = join(projectPath, '.morph/config/config.json');
|
|
161
|
-
|
|
162
|
-
let projectType = 'unknown';
|
|
163
|
-
if (existsSync(configPath)) {
|
|
164
|
-
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
165
|
-
projectType = config.project?.type || 'unknown';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Default locations
|
|
169
|
-
const locations = {
|
|
170
|
-
markdown: '.morph/context/design-system.md',
|
|
171
|
-
css: 'public/design-system.css',
|
|
172
|
-
description: 'Project-level design system (shared across features)'
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Adjust based on project type
|
|
176
|
-
if (projectType === 'nextjs' || projectType.includes('nextjs')) {
|
|
177
|
-
locations.css = 'styles/design-system.css';
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return locations;
|
|
181
|
-
} catch {
|
|
182
|
-
return {
|
|
183
|
-
markdown: '.morph/context/design-system.md',
|
|
184
|
-
css: 'wwwroot/css/design-system.css',
|
|
185
|
-
description: 'Project-level design system (shared across features)'
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|