@polymorphism-tech/morph-spec 8.16.0 → 8.18.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/README.md +2 -2
- package/bin/morph-spec.js +11 -0
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +45 -38
- package/framework/MORPH.md +119 -167
- package/framework/docs/CLI.md +40 -0
- package/framework/evals/rubrics/condensed.md +49 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +9 -0
- package/framework/rules/morph-task-tracking.md +4 -2
- package/framework/schemas/tasks.schema.json +10 -0
- package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -1
- package/framework/skills/core/morph-apply/SKILL.md +42 -34
- package/framework/skills/core/morph-brainstorming/SKILL.md +2 -12
- package/framework/skills/core/morph-eval/SKILL.md +16 -13
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +26 -25
- package/framework/skills/core/morph-plan/SKILL.md +15 -10
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +2 -14
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/package.json +2 -2
- package/src/commands/project/doctor.js +11 -4
- package/src/commands/project/init.js +1 -1
- package/src/commands/project/standards.js +220 -0
- package/src/commands/project/update.js +91 -7
- package/src/lib/stack-filter.js +7 -2
- package/src/lib/tasks/tasks-json-parser.js +26 -0
- package/src/scripts/setup-infra.js +27 -8
- package/src/utils/skills-installer.js +28 -15
|
@@ -75,8 +75,14 @@ function copyDirectory(src, dest) {
|
|
|
75
75
|
*
|
|
76
76
|
* @param {string} srcDir - Source directory to walk
|
|
77
77
|
* @param {string} destDir - Destination base directory (.claude/skills/)
|
|
78
|
+
* @param {string[]} projectTags - Stack tags from config
|
|
79
|
+
* @param {Set<string>} installedNow - Accumulator: skill names this call
|
|
80
|
+
* actually (re)installed. NOT derived by re-scanning destDir afterwards —
|
|
81
|
+
* a disk scan can't distinguish "installed this run" from "leftover from a
|
|
82
|
+
* previous run with different projectTags", which is exactly the stale
|
|
83
|
+
* entry a stack change needs cleaned up.
|
|
78
84
|
*/
|
|
79
|
-
function installSkillsFromDir(srcDir, destDir, projectTags) {
|
|
85
|
+
function installSkillsFromDir(srcDir, destDir, projectTags, installedNow) {
|
|
80
86
|
const entries = readdirSync(srcDir);
|
|
81
87
|
for (const entry of entries) {
|
|
82
88
|
const srcPath = join(srcDir, entry);
|
|
@@ -90,9 +96,10 @@ function installSkillsFromDir(srcDir, destDir, projectTags) {
|
|
|
90
96
|
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
91
97
|
const skillDestDir = join(destDir, entry);
|
|
92
98
|
copyDirectory(srcPath, skillDestDir);
|
|
99
|
+
installedNow.add(entry);
|
|
93
100
|
} else {
|
|
94
101
|
// Level/category directory — recurse
|
|
95
|
-
installSkillsFromDir(srcPath, destDir, projectTags);
|
|
102
|
+
installSkillsFromDir(srcPath, destDir, projectTags, installedNow);
|
|
96
103
|
}
|
|
97
104
|
} else if (entry.endsWith('.md') && entry !== 'README.md') {
|
|
98
105
|
// Legacy flat .md skill — check stack filter before copying
|
|
@@ -102,6 +109,7 @@ function installSkillsFromDir(srcDir, destDir, projectTags) {
|
|
|
102
109
|
const skillDir = join(destDir, skillName);
|
|
103
110
|
mkdirSync(skillDir, { recursive: true });
|
|
104
111
|
copyFileSync(srcPath, join(skillDir, 'SKILL.md'));
|
|
112
|
+
installedNow.add(skillName);
|
|
105
113
|
}
|
|
106
114
|
}
|
|
107
115
|
}
|
|
@@ -147,28 +155,33 @@ export async function installSkills(projectDir, { projectTags = [] } = {}) {
|
|
|
147
155
|
const claudeSkillsDir = join(projectDir, '.claude', 'skills');
|
|
148
156
|
mkdirSync(claudeSkillsDir, { recursive: true });
|
|
149
157
|
|
|
158
|
+
// Skill names this call actually (re)installs, given projectTags. Tracked
|
|
159
|
+
// as we go (NOT by re-scanning claudeSkillsDir afterwards) — a disk scan
|
|
160
|
+
// can't tell "installed this run" apart from "leftover from a previous
|
|
161
|
+
// run with different projectTags", which is exactly the stale entry a
|
|
162
|
+
// stack change (or a re-run that narrows the stack) needs cleaned up.
|
|
163
|
+
const installedNow = new Set();
|
|
150
164
|
for (const level of SKILL_LEVELS_TO_INSTALL) {
|
|
151
165
|
const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
|
|
152
166
|
if (!existsSync(levelDir)) continue;
|
|
153
|
-
installSkillsFromDir(levelDir, claudeSkillsDir, projectTags);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Collect installed morph-* skill names
|
|
157
|
-
const installed = new Set();
|
|
158
|
-
for (const entry of readdirSync(claudeSkillsDir)) {
|
|
159
|
-
if (existsSync(join(claudeSkillsDir, entry, 'SKILL.md'))) {
|
|
160
|
-
installed.add(entry);
|
|
161
|
-
}
|
|
167
|
+
installSkillsFromDir(levelDir, claudeSkillsDir, projectTags, installedNow);
|
|
162
168
|
}
|
|
163
169
|
|
|
164
|
-
//
|
|
170
|
+
// Clean-slate: remove any skill dir that is a KNOWN framework skill (still
|
|
171
|
+
// present in framework/skills/core/) but did NOT get (re)installed this
|
|
172
|
+
// run — i.e. it was filtered out by a `stacks:` mismatch (project.stack
|
|
173
|
+
// changed, or a re-run narrowed the tags). Previously scoped to `morph-`
|
|
174
|
+
// prefixed names only, which silently exempted every non-`morph-` skill
|
|
175
|
+
// (the Umbraco pair) from ever being cleaned up: install once with
|
|
176
|
+
// `umbraco` in the stack, drop the stack later, and they'd survive every
|
|
177
|
+
// reinstall/update forever. Names outside allSourceSkills (user-added,
|
|
178
|
+
// non-framework skills) are never touched.
|
|
165
179
|
const allSourceSkills = collectSourceSkillNames();
|
|
166
180
|
for (const entry of readdirSync(claudeSkillsDir)) {
|
|
167
|
-
if (!entry.
|
|
168
|
-
if (!installed.has(entry) && allSourceSkills.has(entry)) {
|
|
181
|
+
if (!installedNow.has(entry) && allSourceSkills.has(entry)) {
|
|
169
182
|
rmSync(join(claudeSkillsDir, entry), { recursive: true, force: true });
|
|
170
183
|
}
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
return { installed:
|
|
186
|
+
return { installed: installedNow.size };
|
|
174
187
|
}
|