@polymorphism-tech/morph-spec 4.3.4 → 4.3.6
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/.morph/.morphversion +5 -0
- package/.morph/config/agents.json +948 -0
- package/.morph/config/config.json +9 -9
- package/.morph/project/context/README.md +17 -0
- package/.morph/project/context/detection-log.md +16 -0
- package/.morph/project/standards/inferred.md +59 -0
- package/.morph/standards/ai-agents/blazor-ui.md +364 -0
- package/.morph/standards/ai-agents/production.md +415 -0
- package/.morph/standards/ai-agents/setup.md +418 -0
- package/.morph/standards/ai-agents/team-orchestration.md +479 -0
- package/.morph/standards/ai-agents/workflows.md +354 -0
- package/.morph/standards/architecture/ddd/aggregates.md +120 -0
- package/.morph/standards/architecture/ddd/entities.md +99 -0
- package/.morph/standards/architecture/ddd/value-objects.md +124 -0
- package/.morph/standards/backend/api/minimal-api.md +494 -0
- package/.morph/standards/backend/api/rest.md +492 -0
- package/.morph/standards/backend/api/validation.md +88 -0
- package/.morph/standards/backend/authentication/passkeys.md +428 -0
- package/.morph/standards/backend/database/ef-core.md +199 -0
- package/.morph/standards/backend/database/migrations.md +393 -0
- package/.morph/standards/backend/database/postgresql/database.md +352 -0
- package/.morph/standards/backend/database/repository-patterns.md +528 -0
- package/.morph/standards/backend/database/vector-search-rag.md +541 -0
- package/.morph/standards/backend/dotnet/async.md +366 -0
- package/.morph/standards/backend/dotnet/core.md +117 -0
- package/.morph/standards/backend/dotnet/di.md +439 -0
- package/.morph/standards/backend/dotnet/program-cs-checklist.md +92 -0
- package/.morph/standards/backend/integrations/asaas/asaas-api.md +216 -0
- package/.morph/standards/backend/integrations/clerk/clerk-auth.md +290 -0
- package/.morph/standards/backend/integrations/hangfire/hangfire-jobs.md +350 -0
- package/.morph/standards/backend/integrations/resend/resend-email.md +385 -0
- package/.morph/standards/context/analytics.md +96 -0
- package/.morph/standards/context/bundles.md +110 -0
- package/.morph/standards/context/priming.md +78 -0
- package/.morph/standards/core/architecture.md +185 -0
- package/.morph/standards/core/coding.md +214 -0
- package/.morph/standards/core/git-branching-strategy.md +403 -0
- package/.morph/standards/core/git.md +185 -0
- package/.morph/standards/core/testing.md +295 -0
- package/.morph/standards/data/nosql/blob-storage.md +102 -0
- package/.morph/standards/data/nosql/cache/redis.md +97 -0
- package/.morph/standards/data/nosql/cosmos-db.md +118 -0
- package/.morph/standards/data/vector-search/azure-ai-search.md +121 -0
- package/.morph/standards/data/vector-search/rag-chunking.md +104 -0
- package/.morph/standards/frontend/blazor/design-checklist.md +222 -0
- package/.morph/standards/frontend/blazor/fluent-ui-setup.md +595 -0
- package/.morph/standards/frontend/blazor/fluent-ui.md +137 -0
- package/.morph/standards/frontend/blazor/html-conversion.md +184 -0
- package/.morph/standards/frontend/blazor/lifecycle.md +195 -0
- package/.morph/standards/frontend/blazor/pitfalls.md +198 -0
- package/.morph/standards/frontend/blazor/state.md +191 -0
- package/.morph/standards/frontend/design-system/animations.md +151 -0
- package/.morph/standards/frontend/design-system/naming.md +64 -0
- package/.morph/standards/frontend/nextjs/nextjs-patterns.md +198 -0
- package/.morph/standards/infrastructure/azure/azure.md +624 -0
- package/.morph/standards/infrastructure/azure/bicep/bicep-patterns.md +422 -0
- package/.morph/standards/infrastructure/azure/devops/azure-devops-setup.md +516 -0
- package/.morph/standards/infrastructure/azure/devops/local-development.md +520 -0
- package/.morph/standards/infrastructure/azure/services/functions.md +486 -0
- package/.morph/standards/infrastructure/azure/services/service-bus.md +459 -0
- package/.morph/standards/infrastructure/azure/services/storage.md +407 -0
- package/.morph/standards/infrastructure/docker/easypanel-deploy.md +196 -0
- package/.morph/standards/infrastructure/supabase/mcp-setup.md +252 -0
- package/.morph/standards/infrastructure/supabase/supabase-auth.md +176 -0
- package/.morph/standards/infrastructure/supabase/supabase-pgvector.md +169 -0
- package/.morph/standards/infrastructure/supabase/supabase-rls.md +184 -0
- package/.morph/standards/infrastructure/supabase/supabase-storage.md +153 -0
- package/.morph/standards/integration/api/graphql.md +91 -0
- package/.morph/standards/integration/api/grpc.md +114 -0
- package/.morph/standards/integration/api/rest-design.md +95 -0
- package/.morph/standards/integration/event-driven/cqrs.md +101 -0
- package/.morph/standards/integration/event-driven/event-sourcing.md +124 -0
- package/.morph/standards/integration/event-driven/service-bus.md +95 -0
- package/.morph/standards/observability/logging.md +131 -0
- package/.morph/standards/observability/metrics.md +121 -0
- package/.morph/standards/observability/monitoring.md +114 -0
- package/.morph/standards/observability/tracing.md +132 -0
- package/.morph/standards/workflows/parallel-execution.md +112 -0
- package/.morph/standards/workflows/thread-management.md +113 -0
- package/.morph/templates/.idea/morph-templates.xml +92 -0
- package/.morph/templates/.vscode/morph-templates.code-snippets +186 -0
- package/.morph/templates/IDE-SNIPPETS.md +266 -0
- package/.morph/templates/README.md +814 -0
- package/.morph/templates/REGISTRY.json +1677 -0
- package/.morph/templates/code/dotnet/backend/repository.cs +141 -0
- package/.morph/templates/code/dotnet/backend/service.cs +139 -0
- package/.morph/templates/code/dotnet/contracts/Commands.cs +74 -0
- package/.morph/templates/code/dotnet/contracts/Entities.cs +25 -0
- package/.morph/templates/code/dotnet/contracts/Queries.cs +74 -0
- package/.morph/templates/code/dotnet/contracts/README.md +74 -0
- package/.morph/templates/code/dotnet/contracts/api-contracts.cs +173 -0
- package/.morph/templates/code/dotnet/contracts/contracts.cs +217 -0
- package/.morph/templates/code/dotnet/database/migration.cs +83 -0
- package/.morph/templates/code/dotnet/frontend/component.razor +239 -0
- package/.morph/templates/code/dotnet/jobs/agent.cs +163 -0
- package/.morph/templates/code/dotnet/jobs/job.cs +171 -0
- package/.morph/templates/code/dotnet/test.cs +239 -0
- package/.morph/templates/code/sql/rls-policy.sql +57 -0
- package/.morph/templates/code/sql/supabase-migration.sql +100 -0
- package/.morph/templates/code/sql/supabase-migration.template.sql +113 -0
- package/.morph/templates/code/typescript/contracts.ts +168 -0
- package/.morph/templates/context/CONTEXT-FEATURE.md +276 -0
- package/.morph/templates/context/CONTEXT.md +181 -0
- package/.morph/templates/docs/proposal.md +182 -0
- package/.morph/templates/docs/spec.md +149 -0
- package/.morph/templates/examples/design-system-examples.md +357 -0
- package/.morph/templates/examples/spec-examples.md +90 -0
- package/.morph/templates/feature/decisions.md +187 -0
- package/.morph/templates/feature/recap.md +146 -0
- package/.morph/templates/feature/tasks.md +199 -0
- package/.morph/templates/infrastructure/azure/Dockerfile.example +82 -0
- package/.morph/templates/infrastructure/azure/README.md +286 -0
- package/.morph/templates/infrastructure/azure/app-insights.bicep +63 -0
- package/.morph/templates/infrastructure/azure/app-service.bicep +164 -0
- package/.morph/templates/infrastructure/azure/container-app-env.bicep +49 -0
- package/.morph/templates/infrastructure/azure/container-app.bicep +156 -0
- package/.morph/templates/infrastructure/azure/deploy-checklist.md +426 -0
- package/.morph/templates/infrastructure/azure/deploy.ps1 +229 -0
- package/.morph/templates/infrastructure/azure/deploy.sh +208 -0
- package/.morph/templates/infrastructure/azure/key-vault.bicep +91 -0
- package/.morph/templates/infrastructure/azure/main.bicep +189 -0
- package/.morph/templates/infrastructure/azure/parameters.dev.json +29 -0
- package/.morph/templates/infrastructure/azure/parameters.prod.json +29 -0
- package/.morph/templates/infrastructure/azure/parameters.staging.json +29 -0
- package/.morph/templates/infrastructure/azure/sql-database.bicep +103 -0
- package/.morph/templates/infrastructure/azure/storage.bicep +106 -0
- package/.morph/templates/infrastructure/docker/Dockerfile.template +58 -0
- package/.morph/templates/infrastructure/docker/docker-compose.template.yml +67 -0
- package/.morph/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -0
- package/.morph/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -0
- package/.morph/templates/infrastructure/docker/easypanel.template.json +54 -0
- package/.morph/templates/infrastructure/github/README.md +593 -0
- package/.morph/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +22 -0
- package/.morph/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -0
- package/.morph/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -0
- package/.morph/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +61 -0
- package/.morph/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +31 -0
- package/.morph/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -0
- package/.morph/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -0
- package/.morph/templates/integrations/asaas-client.cs +387 -0
- package/.morph/templates/integrations/asaas-webhook.cs +351 -0
- package/.morph/templates/integrations/azure-identity-config.cs +288 -0
- package/.morph/templates/integrations/clerk-config.cs +258 -0
- package/.morph/templates/meta-prompts/fusion/fusion-agent.md +76 -0
- package/.morph/templates/meta-prompts/fusion/fusion-aggregator.md +100 -0
- package/.morph/templates/meta-prompts/hops/hop-retry.md +78 -0
- package/.morph/templates/meta-prompts/hops/hop-validation.md +97 -0
- package/.morph/templates/meta-prompts/hops/hop-wrapper.md +36 -0
- package/.morph/templates/meta-prompts/parallel-workers/parallel-coordinator.md +113 -0
- package/.morph/templates/meta-prompts/parallel-workers/parallel-worker.md +80 -0
- package/.morph/templates/meta-prompts/squad-leaders/backend-squad.md +90 -0
- package/.morph/templates/meta-prompts/squad-leaders/frontend-squad.md +126 -0
- package/.morph/templates/meta-prompts/squad-leaders/squad-leader.md +43 -0
- package/.morph/templates/meta-prompts/validators/checkpoint-validator.md +107 -0
- package/.morph/templates/meta-prompts/validators/pre-commit-validator.md +95 -0
- package/.morph/templates/saas/subscription.cs +347 -0
- package/.morph/templates/saas/tenant.cs +338 -0
- package/.morph/templates/state.template.json +17 -0
- package/.morph/templates/ui/FluentDesignTheme.cs +149 -0
- package/.morph/templates/ui/MudTheme.cs +281 -0
- package/.morph/templates/ui/design-system.css +226 -0
- package/bin/morph-spec.js +1 -1
- package/package.json +1 -1
- package/src/commands/project/update.js +185 -46
|
@@ -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';
|
|
@@ -26,6 +28,79 @@ import { installClaudeHooks } from '../../utils/hooks-installer.js';
|
|
|
26
28
|
import { AutoContextOrchestrator } from '../../core/orchestrator.js';
|
|
27
29
|
import { detectClaudeCode } from '../../llm/environment-detector.js';
|
|
28
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Backup user's config.json before cleaning
|
|
33
|
+
* @param {string} morphPath - Path to .morph directory
|
|
34
|
+
* @returns {Promise<object|null>} Backup of config.json or null
|
|
35
|
+
*/
|
|
36
|
+
async function backupUserConfig(morphPath) {
|
|
37
|
+
try {
|
|
38
|
+
const configPath = join(morphPath, 'config', 'config.json');
|
|
39
|
+
if (await pathExists(configPath)) {
|
|
40
|
+
const config = await readJson(configPath);
|
|
41
|
+
return config;
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
// If backup fails, return null and continue
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Clean framework directories but preserve .morph/project/
|
|
51
|
+
* @param {string} morphPath - Path to .morph directory
|
|
52
|
+
* @param {string} targetPath - Project root path
|
|
53
|
+
*/
|
|
54
|
+
async function cleanFrameworkDirs(morphPath, targetPath) {
|
|
55
|
+
const dirsToClean = [
|
|
56
|
+
join(morphPath, 'templates'),
|
|
57
|
+
join(morphPath, 'standards'),
|
|
58
|
+
join(morphPath, 'config'),
|
|
59
|
+
join(targetPath, '.claude')
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
for (const dir of dirsToClean) {
|
|
63
|
+
try {
|
|
64
|
+
if (await pathExists(dir)) {
|
|
65
|
+
await fs.remove(dir);
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
// Continue even if cleanup fails for one directory
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Restore user's config.json after reinstallation
|
|
75
|
+
* @param {string} morphPath - Path to .morph directory
|
|
76
|
+
* @param {string} targetPath - Project root path
|
|
77
|
+
* @param {object|null} configBackup - Backup of config.json
|
|
78
|
+
* @param {string} currentVersion - Current CLI version
|
|
79
|
+
*/
|
|
80
|
+
async function restoreUserConfig(morphPath, targetPath, configBackup, currentVersion) {
|
|
81
|
+
const configDir = join(morphPath, 'config');
|
|
82
|
+
await ensureDir(configDir);
|
|
83
|
+
const configPath = join(configDir, 'config.json');
|
|
84
|
+
|
|
85
|
+
if (configBackup) {
|
|
86
|
+
// Preserve user data but update framework version
|
|
87
|
+
configBackup.frameworkVersion = currentVersion;
|
|
88
|
+
await writeJson(configPath, configBackup);
|
|
89
|
+
} else {
|
|
90
|
+
// Create basic config if none existed
|
|
91
|
+
const dirName = targetPath.split(/[\\/]/).pop();
|
|
92
|
+
const defaultConfig = {
|
|
93
|
+
framework: 'global',
|
|
94
|
+
frameworkVersion: currentVersion,
|
|
95
|
+
project: {
|
|
96
|
+
name: dirName,
|
|
97
|
+
architecture: 'unknown'
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
await writeJson(configPath, defaultConfig);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
29
104
|
export async function updateCommand(options) {
|
|
30
105
|
const targetPath = process.cwd();
|
|
31
106
|
const contentDir = getContentDir();
|
|
@@ -34,10 +109,17 @@ export async function updateCommand(options) {
|
|
|
34
109
|
logger.header('MORPH-SPEC Update');
|
|
35
110
|
|
|
36
111
|
// Check if MORPH is initialized
|
|
112
|
+
let needsInitialization = false;
|
|
37
113
|
if (!(await pathExists(morphPath))) {
|
|
38
|
-
logger.
|
|
39
|
-
|
|
40
|
-
|
|
114
|
+
logger.warn('MORPH not fully initialized. Creating basic structure...');
|
|
115
|
+
needsInitialization = true;
|
|
116
|
+
|
|
117
|
+
// Create basic .morph structure
|
|
118
|
+
await ensureDir(join(morphPath, 'project', 'context'));
|
|
119
|
+
await ensureDir(join(morphPath, 'project', 'standards'));
|
|
120
|
+
await ensureDir(join(morphPath, 'project', 'outputs'));
|
|
121
|
+
await ensureDir(join(morphPath, 'config'));
|
|
122
|
+
logger.dim(' ✓ Created .morph directory structure');
|
|
41
123
|
}
|
|
42
124
|
|
|
43
125
|
// Check CLI version
|
|
@@ -93,6 +175,18 @@ export async function updateCommand(options) {
|
|
|
93
175
|
const updateSpinner = ora('Updating MORPH-SPEC...').start();
|
|
94
176
|
|
|
95
177
|
try {
|
|
178
|
+
// Backup user config before cleaning
|
|
179
|
+
updateSpinner.text = 'Backing up user configuration...';
|
|
180
|
+
const configBackup = await backupUserConfig(morphPath);
|
|
181
|
+
if (configBackup) {
|
|
182
|
+
logger.dim(' ✓ User config backed up');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Clean framework directories (preserve .morph/project/)
|
|
186
|
+
updateSpinner.text = 'Cleaning old framework files...';
|
|
187
|
+
await cleanFrameworkDirs(morphPath, targetPath);
|
|
188
|
+
logger.dim(' ✓ Old framework files removed');
|
|
189
|
+
|
|
96
190
|
const updateTemplates = !options.standards || options.templates;
|
|
97
191
|
const updateStandards = !options.templates || options.standards;
|
|
98
192
|
|
|
@@ -112,54 +206,94 @@ export async function updateCommand(options) {
|
|
|
112
206
|
await copyDirectory(standardsSrc, standardsDest);
|
|
113
207
|
}
|
|
114
208
|
|
|
115
|
-
// Update agents.json (sourced from framework
|
|
209
|
+
// Update agents.json (sourced from framework/ — canonical single source of truth)
|
|
116
210
|
updateSpinner.text = 'Updating agents configuration...';
|
|
117
|
-
const agentsSrc = join(__dirname, '..', '..', '..', '
|
|
211
|
+
const agentsSrc = join(__dirname, '..', '..', '..', 'framework', 'agents.json');
|
|
118
212
|
const agentsDest = join(morphPath, 'config', 'agents.json');
|
|
119
213
|
if (await pathExists(agentsSrc)) {
|
|
120
|
-
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);
|
|
121
229
|
}
|
|
122
230
|
|
|
123
231
|
// Update .claude commands and skills
|
|
124
|
-
// Source: framework
|
|
125
|
-
updateSpinner.text = '
|
|
126
|
-
const
|
|
232
|
+
// Source: framework/ (canonical for all stacks)
|
|
233
|
+
updateSpinner.text = 'Setting up Claude Code integration...';
|
|
234
|
+
const frameworkDir = join(__dirname, '..', '..', '..', 'framework');
|
|
127
235
|
const claudeDest = join(targetPath, '.claude');
|
|
128
|
-
let claudeUpdated = false;
|
|
129
|
-
if (await pathExists(claudeSrc)) {
|
|
130
|
-
// Copy commands (always copy, these are small)
|
|
131
|
-
const commandsSrc = join(claudeSrc, 'commands');
|
|
132
|
-
const commandsDest = join(claudeDest, 'commands');
|
|
133
|
-
if (await pathExists(commandsSrc)) {
|
|
134
|
-
await copyDirectory(commandsSrc, commandsDest);
|
|
135
|
-
claudeUpdated = true;
|
|
136
|
-
|
|
137
|
-
// Clean up stale command files moved to skills/workflows/ in v2.4+
|
|
138
|
-
const staleCommands = [
|
|
139
|
-
'morph-setup.md', 'morph-uiux.md', 'morph-design.md',
|
|
140
|
-
'morph-clarify.md', 'morph-tasks.md'
|
|
141
|
-
];
|
|
142
|
-
for (const file of staleCommands) {
|
|
143
|
-
const stalePath = join(commandsDest, file);
|
|
144
|
-
if (await pathExists(stalePath)) {
|
|
145
|
-
await fs.remove(stalePath);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
logger.warn(' ⚠ .claude/commands/ source missing — commands not updated');
|
|
150
|
-
}
|
|
151
236
|
|
|
152
|
-
|
|
153
|
-
|
|
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');
|
|
154
255
|
const skillsDest = join(claudeDest, 'skills');
|
|
256
|
+
|
|
155
257
|
if (await pathExists(skillsSrc)) {
|
|
156
258
|
await ensureDir(skillsDest);
|
|
259
|
+
|
|
157
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.)
|
|
158
267
|
for (const entry of entries) {
|
|
159
268
|
if (entry.isDirectory()) {
|
|
160
|
-
|
|
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
|
+
}
|
|
161
292
|
}
|
|
162
293
|
}
|
|
294
|
+
|
|
295
|
+
symlinkCount = linkedCategories > 0 ? totalSkillFiles : 0;
|
|
296
|
+
copyCount = linkedCategories === 0 ? totalSkillFiles : 0;
|
|
163
297
|
}
|
|
164
298
|
}
|
|
165
299
|
|
|
@@ -173,20 +307,15 @@ export async function updateCommand(options) {
|
|
|
173
307
|
const claudeMdDest = join(targetPath, 'CLAUDE.md');
|
|
174
308
|
await copyDirectory(claudeMdSrc, claudeMdDest);
|
|
175
309
|
|
|
310
|
+
// Restore user config after framework reinstallation
|
|
311
|
+
updateSpinner.text = 'Restoring user configuration...';
|
|
312
|
+
await restoreUserConfig(morphPath, targetPath, configBackup, cliCheck.current);
|
|
313
|
+
logger.dim(' ✓ User config restored with updated framework version');
|
|
314
|
+
|
|
176
315
|
// Update .morphversion
|
|
177
316
|
updateSpinner.text = 'Saving version info...';
|
|
178
317
|
await saveProjectMorphVersion(targetPath, cliCheck.current);
|
|
179
318
|
|
|
180
|
-
// Update config.json frameworkVersion (BUG #9 fix)
|
|
181
|
-
const configPath = join(morphPath, 'config', 'config.json');
|
|
182
|
-
if (await pathExists(configPath)) {
|
|
183
|
-
try {
|
|
184
|
-
const config = await readJson(configPath);
|
|
185
|
-
config.frameworkVersion = cliCheck.current;
|
|
186
|
-
await writeJson(configPath, config);
|
|
187
|
-
} catch { /* preserve existing config if read/write fails */ }
|
|
188
|
-
}
|
|
189
|
-
|
|
190
319
|
updateSpinner.succeed('MORPH-SPEC updated successfully!');
|
|
191
320
|
logger.blank();
|
|
192
321
|
|
|
@@ -204,7 +333,17 @@ export async function updateCommand(options) {
|
|
|
204
333
|
if (updateTemplates) logger.dim(' ✓ .morph/templates/');
|
|
205
334
|
if (updateStandards) logger.dim(' ✓ .morph/standards/');
|
|
206
335
|
logger.dim(' ✓ .morph/config/agents.json');
|
|
207
|
-
|
|
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
|
+
|
|
208
347
|
logger.dim(' ✓ .claude/settings.local.json (agent-teams hooks)');
|
|
209
348
|
logger.dim(' ✓ CLAUDE.md');
|
|
210
349
|
logger.blank();
|