@polymorphism-tech/morph-spec 2.1.0 → 2.1.2

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 CHANGED
@@ -67,24 +67,48 @@ morph-spec init
67
67
  ```
68
68
 
69
69
  <details>
70
- <summary><strong>Windows: Comando não reconhecido?</strong></summary>
70
+ <summary><strong>⚠️ Windows + nvm-windows: Comando não reconhecido?</strong></summary>
71
71
 
72
- Se após instalar globalmente o comando `morph-spec` não for reconhecido, adicione o npm ao PATH:
72
+ Se você usa **nvm-windows** para gerenciar versões do Node.js, o npm **não** adiciona automaticamente os pacotes globais ao PATH. Este é um [problema conhecido do nvm-windows](https://github.com/coreybutler/nvm-windows/issues/391).
73
+
74
+ ### Solução 1: Adicionar ao PATH manualmente
73
75
 
74
76
  **PowerShell (executar como Administrador):**
75
77
  ```powershell
76
- # Adicionar npm global ao PATH permanentemente
78
+ # Verificar o caminho do npm global
79
+ npm config get prefix
80
+
81
+ # Adicionar ao PATH (substitua pelo caminho acima)
77
82
  $npmPath = "$env:APPDATA\npm"
78
- [Environment]::SetEnvironmentVariable("PATH", $env:PATH + ";$npmPath", "User")
83
+ [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";$npmPath", "User")
79
84
  ```
80
85
 
81
86
  Depois, **feche e reabra o terminal**.
82
87
 
83
- **Alternativa:** Use `npx` que funciona sem configurar PATH:
88
+ ### Solução 2: Use npx (Recomendado)
89
+
90
+ O `npx` funciona sem configurar PATH:
84
91
  ```bash
85
92
  npx @polymorphism-tech/morph-spec init
93
+ npx @polymorphism-tech/morph-spec doctor
94
+ ```
95
+
96
+ ### Verificar se funcionou
97
+
98
+ ```bash
99
+ morph-spec --version
100
+ # Ou
101
+ npx @polymorphism-tech/morph-spec --version
102
+ ```
103
+
104
+ ### Mais Ajuda
105
+
106
+ ```bash
107
+ morph-spec doctor # Detecta problemas de PATH automaticamente
86
108
  ```
87
109
 
110
+ Veja também: [Troubleshooting](#troubleshooting)
111
+
88
112
  </details>
89
113
 
90
114
  ### Comandos CLI
@@ -342,6 +366,111 @@ O MORPH segue a filosofia **"Free tier first"**:
342
366
 
343
367
  ---
344
368
 
369
+ ## 🔧 Troubleshooting
370
+
371
+ ### Comando `morph-spec` não encontrado
372
+
373
+ **Sintoma:** Após `npm install -g`, o comando não é reconhecido.
374
+
375
+ **Diagnóstico:**
376
+ ```bash
377
+ npm config get prefix # Ver onde npm instala globalmente
378
+ echo $PATH # Linux/Mac: Verificar se está no PATH
379
+ $env:Path # Windows: Verificar se está no PATH
380
+ ```
381
+
382
+ **Causa comum:** Você está usando **nvm** ou **nvm-windows**, que não adiciona automaticamente npm global ao PATH.
383
+
384
+ **Solução 1 - Usar npx (Recomendado):**
385
+ ```bash
386
+ npx @polymorphism-tech/morph-spec init
387
+ ```
388
+
389
+ **Solução 2 - Adicionar ao PATH:**
390
+
391
+ **Linux/Mac:**
392
+ ```bash
393
+ # Adicionar ao ~/.bashrc ou ~/.zshrc
394
+ export PATH="$HOME/.npm-global/bin:$PATH"
395
+ ```
396
+
397
+ **Windows (PowerShell como Admin):**
398
+ ```powershell
399
+ $npmPath = "$env:APPDATA\npm"
400
+ [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";$npmPath", "User")
401
+ ```
402
+
403
+ **Verificar correção:**
404
+ ```bash
405
+ morph-spec --version
406
+ ```
407
+
408
+ ---
409
+
410
+ ### `morph-spec doctor` reporta problemas
411
+
412
+ **O que o doctor verifica:**
413
+ - ✓ Versão do CLI (atualizada?)
414
+ - ✓ Versão do MORPH no projeto
415
+ - ✓ Comando morph-spec no PATH
416
+ - ✓ Estrutura de arquivos (.morph/, CLAUDE.md, etc.)
417
+
418
+ **Se reportar "morph-spec not in PATH":**
419
+ 1. Use `npx` como alternativa (sempre funciona)
420
+ 2. Ou siga as instruções acima para adicionar ao PATH
421
+
422
+ **Se reportar arquivos faltando:**
423
+ ```bash
424
+ morph-spec init --force # Reinstala estrutura completa
425
+ ```
426
+
427
+ ---
428
+
429
+ ### Erro ao instalar globalmente no Windows
430
+
431
+ **Erro:** `EPERM: operation not permitted`
432
+
433
+ **Causa:** Falta de permissões de administrador.
434
+
435
+ **Solução:**
436
+ 1. Abra PowerShell ou CMD **como Administrador**
437
+ 2. Execute: `npm install -g @polymorphism-tech/morph-spec`
438
+
439
+ **Alternativa:** Use `npx` (não requer admin):
440
+ ```bash
441
+ npx @polymorphism-tech/morph-spec init
442
+ ```
443
+
444
+ ---
445
+
446
+ ### nvm-windows específico
447
+
448
+ Se você usa **nvm-windows** para gerenciar versões do Node.js:
449
+
450
+ **Problema conhecido:** npm global não vai para PATH automaticamente
451
+ **Issue oficial:** https://github.com/coreybutler/nvm-windows/issues/391
452
+
453
+ **Recomendações:**
454
+ 1. **Melhor opção:** Use `npx` para todos os comandos
455
+ 2. **Alternativa:** Configure PATH manualmente (ver seção de instalação)
456
+ 3. **Verificar:** Execute `morph-spec doctor` para diagnosticar
457
+
458
+ ---
459
+
460
+ ### Precisa de mais ajuda?
461
+
462
+ ```bash
463
+ morph-spec doctor # Diagnóstico automático
464
+ morph-spec --help # Ver todos os comandos
465
+ morph-spec <comando> --help # Ajuda de comando específico
466
+ ```
467
+
468
+ **Suporte:**
469
+ - 📧 Email: support@polymorphism.com.br
470
+ - 🐛 Issues: [GitHub](https://github.com/lucasPolymorphism/morph-spec-framework/issues)
471
+
472
+ ---
473
+
345
474
  ## 📄 Licença e Distribuição
346
475
 
347
476
  **Repositório:** Privado (código fonte protegido)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polymorphism-tech/morph-spec",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "MORPH-SPEC v2.0: AI-First development framework with .NET 10, Microsoft Agent Framework, and Fluent UI Blazor",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -26,6 +26,7 @@
26
26
  "files": [
27
27
  "bin/",
28
28
  "src/",
29
+ "scripts/",
29
30
  "detectors/",
30
31
  "content/",
31
32
  "docs/",
@@ -38,6 +39,7 @@
38
39
  "node": ">=18.0.0"
39
40
  },
40
41
  "scripts": {
42
+ "postinstall": "node scripts/postinstall.js",
41
43
  "start": "node bin/morph-spec.js",
42
44
  "test": "node --test",
43
45
  "test:coverage": "c8 --reporter=text --reporter=html --reporter=lcov node --test",
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Postinstall script for @polymorphism-tech/morph-spec
5
+ *
6
+ * Checks if morph-spec is accessible in PATH and warns users
7
+ * if it's not (common issue with nvm-windows).
8
+ */
9
+
10
+ import { execSync } from 'child_process';
11
+ import { platform } from 'os';
12
+ import chalk from 'chalk';
13
+
14
+ const isWindows = platform() === 'win32';
15
+
16
+ /**
17
+ * Check if morph-spec command is in PATH
18
+ */
19
+ function isMorphSpecInPath() {
20
+ try {
21
+ const command = isWindows ? 'where morph-spec' : 'which morph-spec';
22
+ execSync(command, { stdio: 'ignore' });
23
+ return true;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Detect if nvm-windows is installed
31
+ */
32
+ function isUsingNvmWindows() {
33
+ if (!isWindows) return false;
34
+
35
+ try {
36
+ const path = process.env.PATH || '';
37
+ return path.includes('nvm4w') || path.includes('\\nvm\\');
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Get npm global prefix
45
+ */
46
+ function getNpmGlobalPrefix() {
47
+ try {
48
+ const prefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
49
+ return prefix;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Show installation success message
57
+ */
58
+ function showSuccessMessage() {
59
+ console.log('');
60
+ console.log(chalk.green('✓ @polymorphism-tech/morph-spec installed successfully!'));
61
+ console.log('');
62
+ console.log(chalk.cyan('Quick start:'));
63
+ console.log(chalk.white(' morph-spec init'));
64
+ console.log(chalk.white(' morph-spec --help'));
65
+ console.log('');
66
+ }
67
+
68
+ /**
69
+ * Show PATH warning for nvm-windows users
70
+ */
71
+ function showPathWarning() {
72
+ console.log('');
73
+ console.log(chalk.yellow('⚠ Warning: morph-spec command not found in PATH'));
74
+ console.log('');
75
+
76
+ if (isUsingNvmWindows()) {
77
+ console.log(chalk.white('You are using nvm-windows, which does not automatically add'));
78
+ console.log(chalk.white('npm global packages to your PATH.'));
79
+ console.log('');
80
+ console.log(chalk.cyan('Solution 1: Add npm global directory to PATH'));
81
+
82
+ const prefix = getNpmGlobalPrefix();
83
+ if (prefix) {
84
+ console.log(chalk.white(` Add this to your PATH: ${chalk.yellow(prefix)}`));
85
+ console.log('');
86
+ console.log(chalk.gray(' PowerShell (as Administrator):'));
87
+ console.log(chalk.white(` [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";${prefix}", "User")`));
88
+ console.log('');
89
+ console.log(chalk.gray(' Or manually via System Properties > Environment Variables'));
90
+ }
91
+
92
+ console.log('');
93
+ console.log(chalk.cyan('Solution 2: Use npx (recommended)'));
94
+ console.log(chalk.white(' npx @polymorphism-tech/morph-spec init'));
95
+ console.log(chalk.white(' npx @polymorphism-tech/morph-spec --help'));
96
+ } else {
97
+ console.log(chalk.white('The morph-spec command was not found in your PATH.'));
98
+ console.log('');
99
+ console.log(chalk.cyan('Solution: Use npx instead'));
100
+ console.log(chalk.white(' npx @polymorphism-tech/morph-spec init'));
101
+ console.log(chalk.white(' npx @polymorphism-tech/morph-spec --help'));
102
+ }
103
+
104
+ console.log('');
105
+ console.log(chalk.gray('For more help, see: https://github.com/lucasPolymorphism/morph-spec-framework#troubleshooting'));
106
+ console.log('');
107
+ }
108
+
109
+ /**
110
+ * Main postinstall check
111
+ */
112
+ function main() {
113
+ // Only show messages for global installs
114
+ // Skip for local installs (when used as dependency)
115
+ const isGlobalInstall = process.env.npm_config_global === 'true';
116
+
117
+ if (!isGlobalInstall) {
118
+ // Silent exit for local installs
119
+ return;
120
+ }
121
+
122
+ // Give npm a moment to set up the symlinks
123
+ setTimeout(() => {
124
+ if (isMorphSpecInPath()) {
125
+ showSuccessMessage();
126
+ } else {
127
+ showPathWarning();
128
+ }
129
+ }, 100);
130
+ }
131
+
132
+ main();
@@ -1,4 +1,6 @@
1
1
  import { join } from 'path';
2
+ import { execSync } from 'child_process';
3
+ import { platform } from 'os';
2
4
  import chalk from 'chalk';
3
5
  import { logger } from '../utils/logger.js';
4
6
  import { pathExists, readJson } from '../utils/file-copier.js';
@@ -8,6 +10,47 @@ import {
8
10
  getInstalledCLIVersion
9
11
  } from '../utils/version-checker.js';
10
12
 
13
+ const isWindows = platform() === 'win32';
14
+
15
+ /**
16
+ * Check if morph-spec command is in PATH
17
+ */
18
+ function isMorphSpecInPath() {
19
+ try {
20
+ const command = isWindows ? 'where morph-spec' : 'which morph-spec';
21
+ execSync(command, { stdio: 'ignore' });
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Detect if nvm-windows is installed
30
+ */
31
+ function isUsingNvmWindows() {
32
+ if (!isWindows) return false;
33
+
34
+ try {
35
+ const path = process.env.PATH || '';
36
+ return path.includes('nvm4w') || path.includes('\\nvm\\');
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Get npm global prefix
44
+ */
45
+ function getNpmGlobalPrefix() {
46
+ try {
47
+ const prefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
48
+ return prefix;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+
11
54
  export async function doctorCommand() {
12
55
  const targetPath = process.cwd();
13
56
 
@@ -68,6 +111,22 @@ export async function doctorCommand() {
68
111
  hasWarnings = true;
69
112
  }
70
113
 
114
+ // Check if morph-spec is in PATH
115
+ const inPath = isMorphSpecInPath();
116
+ if (inPath) {
117
+ checks.push({
118
+ name: 'morph-spec in PATH',
119
+ status: 'ok'
120
+ });
121
+ } else {
122
+ checks.push({
123
+ name: 'morph-spec in PATH',
124
+ status: 'warn',
125
+ msg: 'command not found'
126
+ });
127
+ hasWarnings = true;
128
+ }
129
+
71
130
  // Check CLAUDE.md
72
131
  const claudeMd = join(targetPath, 'CLAUDE.md');
73
132
  if (await pathExists(claudeMd)) {
@@ -187,6 +246,28 @@ export async function doctorCommand() {
187
246
  logger.warn('Some checks need attention.');
188
247
  logger.blank();
189
248
 
249
+ if (!inPath) {
250
+ const usingNvm = isUsingNvmWindows();
251
+ const prefix = getNpmGlobalPrefix();
252
+
253
+ if (usingNvm && prefix) {
254
+ logger.info('PATH Issue Detected (nvm-windows):');
255
+ logger.dim(` Add to PATH: ${prefix}`);
256
+ logger.blank();
257
+ logger.dim(' PowerShell (as Administrator):');
258
+ console.log(chalk.gray(` [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";${prefix}", "User")`));
259
+ logger.blank();
260
+ logger.dim(' Or use npx instead:');
261
+ logger.dim(' npx @polymorphism-tech/morph-spec init');
262
+ logger.blank();
263
+ } else if (!inPath) {
264
+ logger.info('morph-spec not in PATH:');
265
+ logger.dim(' Use npx instead:');
266
+ logger.dim(' npx @polymorphism-tech/morph-spec init');
267
+ logger.blank();
268
+ }
269
+ }
270
+
190
271
  if (cliCheck.isOutdated || projectCheck.isOutdated) {
191
272
  logger.info('To update:');
192
273
  logger.dim(' 1. Update the CLI: npm install -g @polymorphism-tech/morph-spec@latest');
@@ -52,16 +52,18 @@ export async function initCommand(options) {
52
52
  }
53
53
  await copyFile(claudeMdPath, claudeMdDest);
54
54
 
55
- // 2. Create .morph/project structure
56
- spinner.text = 'Creating .morph/project structure...';
55
+ // 2. Create .morph directory structure
56
+ spinner.text = 'Creating .morph structure...';
57
57
  const projectPath = join(morphPath, 'project');
58
+ const configDir = join(morphPath, 'config');
58
59
  await ensureDir(join(projectPath, 'context'));
59
60
  await ensureDir(join(projectPath, 'standards'));
60
61
  await ensureDir(join(projectPath, 'outputs'));
62
+ await ensureDir(configDir);
61
63
 
62
64
  // 3. Create config.json
63
65
  spinner.text = 'Generating config.json...';
64
- const configPath = join(morphPath, 'config.json');
66
+ const configPath = join(configDir, 'config.json');
65
67
  const dirName = targetPath.split(/[\\/]/).pop();
66
68
 
67
69
  const config = {
@@ -99,12 +101,58 @@ Run \`morph-spec detect\` to analyze your project.
99
101
  `;
100
102
  await writeFile(contextReadme, readmeContent);
101
103
 
102
- // 5. Save version info
104
+ // 5. Copy templates from content
105
+ spinner.text = 'Copying templates...';
106
+ const contentDir = getContentDir();
107
+ const templatesSrc = join(contentDir, '.morph', 'templates');
108
+ const templatesDest = join(morphPath, 'templates');
109
+ if (await pathExists(templatesSrc)) {
110
+ await copyDirectory(templatesSrc, templatesDest);
111
+ }
112
+
113
+ // 6. Copy standards from content
114
+ spinner.text = 'Copying standards...';
115
+ const standardsSrc = join(contentDir, '.morph', 'standards');
116
+ const standardsDest = join(morphPath, 'standards');
117
+ if (await pathExists(standardsSrc)) {
118
+ await copyDirectory(standardsSrc, standardsDest);
119
+ }
120
+
121
+ // 7. Copy agents.json
122
+ spinner.text = 'Copying agents configuration...';
123
+ const agentsSrc = join(contentDir, '.morph', 'config', 'agents.json');
124
+ const agentsDest = join(configDir, 'agents.json');
125
+ if (await pathExists(agentsSrc)) {
126
+ await copyFile(agentsSrc, agentsDest);
127
+ }
128
+
129
+ // 8. Copy azure-pricing files
130
+ spinner.text = 'Copying Azure pricing data...';
131
+ const pricingSrc = join(contentDir, '.morph', 'config', 'azure-pricing.json');
132
+ const pricingDest = join(configDir, 'azure-pricing.json');
133
+ if (await pathExists(pricingSrc)) {
134
+ await copyFile(pricingSrc, pricingDest);
135
+ }
136
+ const pricingSchemaSrc = join(contentDir, '.morph', 'config', 'azure-pricing.schema.json');
137
+ const pricingSchemaDest = join(configDir, 'azure-pricing.schema.json');
138
+ if (await pathExists(pricingSchemaSrc)) {
139
+ await copyFile(pricingSchemaSrc, pricingSchemaDest);
140
+ }
141
+
142
+ // 9. Copy .claude commands and skills
143
+ spinner.text = 'Copying Claude commands and skills...';
144
+ const claudeSrc = join(contentDir, '.claude');
145
+ const claudeDest = join(targetPath, '.claude');
146
+ if (await pathExists(claudeSrc)) {
147
+ await copyDirectory(claudeSrc, claudeDest);
148
+ }
149
+
150
+ // 10. Save version info
103
151
  spinner.text = 'Saving version info...';
104
152
  const cliVersion = getInstalledCLIVersion();
105
153
  await saveProjectMorphVersion(targetPath, cliVersion);
106
154
 
107
- // 6. Update .gitignore
155
+ // 11. Update .gitignore
108
156
  spinner.text = 'Updating .gitignore...';
109
157
  await updateGitignore(targetPath);
110
158
 
@@ -126,16 +174,13 @@ Run \`morph-spec detect\` to analyze your project.
126
174
  ]);
127
175
  logger.blank();
128
176
 
129
- logger.info('Structure created:');
130
- logger.dim(`CLAUDE.md → ${join(targetPath, 'CLAUDE.md')}`);
131
- logger.dim(`.morph/config.json Project config`);
132
- logger.dim(`.morph/project/context/ → Project context`);
133
- logger.dim(`.morph/project/standards/→ Project standards`);
134
- logger.dim(`.morph/project/outputs/ → Feature outputs`);
135
- logger.blank();
136
-
137
- logger.info('Content (via npm package):');
138
- logger.dim(`Templates and standards available via morph-spec update`);
177
+ logger.info('Files installed:');
178
+ logger.dim(` CLAUDE.md`);
179
+ logger.dim(` ✓ .morph/config/ (config.json, agents.json, azure-pricing.json)`);
180
+ logger.dim(` ✓ .morph/standards/ (coding.md, architecture.md, azure.md, ...)`);
181
+ logger.dim(` ✓ .morph/templates/ (Bicep, integrations, saas, ...)`);
182
+ logger.dim(` ✓ .morph/project/ (context, standards, outputs)`);
183
+ logger.dim(` ✓ .claude/ (commands, skills)`);
139
184
  logger.blank();
140
185
 
141
186
  } catch (error) {