@polymorphism-tech/morph-spec 4.3.5 → 4.3.7
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/package.json +1 -2
- package/src/commands/project/update.js +85 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polymorphism-tech/morph-spec",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.7",
|
|
4
4
|
"description": "MORPH-SPEC: AI-First development framework with validation pipeline and multi-stack support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -48,7 +48,6 @@
|
|
|
48
48
|
"docs:serve": "npx http-server docs/api -p 8080 -o"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@polymorphism-tech/morph-spec": "^4.3.1",
|
|
52
51
|
"ajv": "^8.12.0",
|
|
53
52
|
"ajv-formats": "^3.0.1",
|
|
54
53
|
"chalk": "^5.3.0",
|
|
@@ -9,9 +9,11 @@ import { logger } from '../../utils/logger.js';
|
|
|
9
9
|
import {
|
|
10
10
|
getContentDir,
|
|
11
11
|
copyDirectory,
|
|
12
|
+
copyFile,
|
|
12
13
|
pathExists,
|
|
13
14
|
ensureDir,
|
|
14
15
|
createDirectoryLink,
|
|
16
|
+
createSymlink,
|
|
15
17
|
readJson,
|
|
16
18
|
writeJson
|
|
17
19
|
} from '../../utils/file-copier.js';
|
|
@@ -204,54 +206,94 @@ export async function updateCommand(options) {
|
|
|
204
206
|
await copyDirectory(standardsSrc, standardsDest);
|
|
205
207
|
}
|
|
206
208
|
|
|
207
|
-
// Update agents.json (sourced from framework
|
|
209
|
+
// Update agents.json (sourced from framework/ — canonical single source of truth)
|
|
208
210
|
updateSpinner.text = 'Updating agents configuration...';
|
|
209
|
-
const agentsSrc = join(__dirname, '..', '..', '..', '
|
|
211
|
+
const agentsSrc = join(__dirname, '..', '..', '..', 'framework', 'agents.json');
|
|
210
212
|
const agentsDest = join(morphPath, 'config', 'agents.json');
|
|
211
213
|
if (await pathExists(agentsSrc)) {
|
|
212
|
-
await
|
|
214
|
+
await copyFile(agentsSrc, agentsDest);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Update azure-pricing files
|
|
218
|
+
updateSpinner.text = 'Updating Azure pricing data...';
|
|
219
|
+
const configDir = join(morphPath, 'config');
|
|
220
|
+
const pricingSrc = join(contentDir, '.morph', 'config', 'azure-pricing.json');
|
|
221
|
+
const pricingDest = join(configDir, 'azure-pricing.json');
|
|
222
|
+
if (await pathExists(pricingSrc)) {
|
|
223
|
+
await copyFile(pricingSrc, pricingDest);
|
|
224
|
+
}
|
|
225
|
+
const pricingSchemaSrc = join(contentDir, '.morph', 'config', 'azure-pricing.schema.json');
|
|
226
|
+
const pricingSchemaDest = join(configDir, 'azure-pricing.schema.json');
|
|
227
|
+
if (await pathExists(pricingSchemaSrc)) {
|
|
228
|
+
await copyFile(pricingSchemaSrc, pricingSchemaDest);
|
|
213
229
|
}
|
|
214
230
|
|
|
215
231
|
// Update .claude commands and skills
|
|
216
|
-
// Source: framework
|
|
217
|
-
updateSpinner.text = '
|
|
218
|
-
const
|
|
232
|
+
// Source: framework/ (canonical for all stacks)
|
|
233
|
+
updateSpinner.text = 'Setting up Claude Code integration...';
|
|
234
|
+
const frameworkDir = join(__dirname, '..', '..', '..', 'framework');
|
|
219
235
|
const claudeDest = join(targetPath, '.claude');
|
|
220
|
-
let claudeUpdated = false;
|
|
221
|
-
if (await pathExists(claudeSrc)) {
|
|
222
|
-
// Copy commands (always copy, these are small)
|
|
223
|
-
const commandsSrc = join(claudeSrc, 'commands');
|
|
224
|
-
const commandsDest = join(claudeDest, 'commands');
|
|
225
|
-
if (await pathExists(commandsSrc)) {
|
|
226
|
-
await copyDirectory(commandsSrc, commandsDest);
|
|
227
|
-
claudeUpdated = true;
|
|
228
|
-
|
|
229
|
-
// Clean up stale command files moved to skills/workflows/ in v2.4+
|
|
230
|
-
const staleCommands = [
|
|
231
|
-
'morph-setup.md', 'morph-uiux.md', 'morph-design.md',
|
|
232
|
-
'morph-clarify.md', 'morph-tasks.md'
|
|
233
|
-
];
|
|
234
|
-
for (const file of staleCommands) {
|
|
235
|
-
const stalePath = join(commandsDest, file);
|
|
236
|
-
if (await pathExists(stalePath)) {
|
|
237
|
-
await fs.remove(stalePath);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
} else {
|
|
241
|
-
logger.warn(' ⚠ .claude/commands/ source missing — commands not updated');
|
|
242
|
-
}
|
|
243
236
|
|
|
244
|
-
|
|
245
|
-
|
|
237
|
+
let symlinkCount = 0;
|
|
238
|
+
let copyCount = 0;
|
|
239
|
+
let commandsCopied = false;
|
|
240
|
+
|
|
241
|
+
// Copy commands directory (slash commands): framework/commands/ → .claude/commands/
|
|
242
|
+
const commandsSrc = join(frameworkDir, 'commands');
|
|
243
|
+
const commandsDest = join(claudeDest, 'commands');
|
|
244
|
+
if (await pathExists(commandsSrc)) {
|
|
245
|
+
await copyDirectory(commandsSrc, commandsDest);
|
|
246
|
+
commandsCopied = true;
|
|
247
|
+
} else {
|
|
248
|
+
logger.warn(' ⚠ framework/commands/ source missing — commands not updated');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Create directory links for skill categories (or copy if linking fails)
|
|
252
|
+
// Source: framework/skills/ → .claude/skills/
|
|
253
|
+
{
|
|
254
|
+
const skillsSrc = join(frameworkDir, 'skills');
|
|
246
255
|
const skillsDest = join(claudeDest, 'skills');
|
|
256
|
+
|
|
247
257
|
if (await pathExists(skillsSrc)) {
|
|
248
258
|
await ensureDir(skillsDest);
|
|
259
|
+
|
|
249
260
|
const entries = await fs.readdir(skillsSrc, { withFileTypes: true });
|
|
261
|
+
|
|
262
|
+
let linkedCategories = 0;
|
|
263
|
+
let copiedCategories = 0;
|
|
264
|
+
let totalSkillFiles = 0;
|
|
265
|
+
|
|
266
|
+
// Link category directories (specialists/, infra/, checklists/, etc.)
|
|
250
267
|
for (const entry of entries) {
|
|
251
268
|
if (entry.isDirectory()) {
|
|
252
|
-
|
|
269
|
+
const categorySrc = join(skillsSrc, entry.name);
|
|
270
|
+
const categoryDest = join(skillsDest, entry.name);
|
|
271
|
+
|
|
272
|
+
// Count .md files for reporting
|
|
273
|
+
const categoryEntries = await fs.readdir(categorySrc, { withFileTypes: true });
|
|
274
|
+
const mdFiles = categoryEntries.filter(e => e.isFile() && e.name.endsWith('.md'));
|
|
275
|
+
totalSkillFiles += mdFiles.length;
|
|
276
|
+
|
|
277
|
+
const result = await createDirectoryLink(categorySrc, categoryDest);
|
|
278
|
+
if (result === 'copy') {
|
|
279
|
+
copiedCategories++;
|
|
280
|
+
} else {
|
|
281
|
+
linkedCategories++;
|
|
282
|
+
}
|
|
283
|
+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
284
|
+
// Handle .md files in skills root
|
|
285
|
+
totalSkillFiles++;
|
|
286
|
+
const result = await createSymlink(join(skillsSrc, entry.name), join(skillsDest, entry.name), 'file');
|
|
287
|
+
if (result === 'symlink') {
|
|
288
|
+
linkedCategories++;
|
|
289
|
+
} else {
|
|
290
|
+
copiedCategories++;
|
|
291
|
+
}
|
|
253
292
|
}
|
|
254
293
|
}
|
|
294
|
+
|
|
295
|
+
symlinkCount = linkedCategories > 0 ? totalSkillFiles : 0;
|
|
296
|
+
copyCount = linkedCategories === 0 ? totalSkillFiles : 0;
|
|
255
297
|
}
|
|
256
298
|
}
|
|
257
299
|
|
|
@@ -291,7 +333,17 @@ export async function updateCommand(options) {
|
|
|
291
333
|
if (updateTemplates) logger.dim(' ✓ .morph/templates/');
|
|
292
334
|
if (updateStandards) logger.dim(' ✓ .morph/standards/');
|
|
293
335
|
logger.dim(' ✓ .morph/config/agents.json');
|
|
294
|
-
|
|
336
|
+
logger.dim(' ✓ .morph/config/azure-pricing.json');
|
|
337
|
+
if (commandsCopied) logger.dim(' ✓ .claude/commands/');
|
|
338
|
+
|
|
339
|
+
if (symlinkCount > 0) {
|
|
340
|
+
const linkType = process.platform === 'win32' ? 'junction-linked' : 'symlinked';
|
|
341
|
+
logger.dim(` ✓ .claude/skills/ (${symlinkCount} ${linkType})`);
|
|
342
|
+
} else if (copyCount > 0) {
|
|
343
|
+
logger.dim(` ✓ .claude/skills/ (${copyCount} copied)`);
|
|
344
|
+
logger.warn(` ⚠ Directory links not supported (copied instead). Skills won't auto-update.`);
|
|
345
|
+
}
|
|
346
|
+
|
|
295
347
|
logger.dim(' ✓ .claude/settings.local.json (agent-teams hooks)');
|
|
296
348
|
logger.dim(' ✓ CLAUDE.md');
|
|
297
349
|
logger.blank();
|