@polymorphism-tech/morph-spec 4.10.6 → 4.10.8
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 +3 -0
- package/LICENSE +71 -71
- package/README.md +379 -379
- package/bin/morph-spec.js +89 -0
- package/bin/task-manager.js +59 -6
- package/claude-plugin.json +14 -14
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +206 -206
- package/framework/agents/README.md +14 -14
- package/framework/agents/architecture/standards-architect.md +159 -159
- package/framework/agents/frontend/blazor-builder.md +13 -0
- package/framework/agents/frontend/css-specialist.md +87 -0
- package/framework/agents/frontend/nextjs-expert.md +13 -0
- package/framework/agents/frontend/nextjs-ui-specialist.md +218 -0
- package/framework/agents/frontend/ui-ux-designer.md +80 -10
- package/framework/agents/infrastructure/azure-architect.md +149 -149
- package/framework/agents/infrastructure/infra-architect.md +48 -48
- package/framework/agents.json +2268 -2567
- package/framework/hooks/README.md +204 -204
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +10 -5
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +10 -24
- package/framework/hooks/shared/phase-utils.js +139 -139
- package/framework/hooks/shared/worktree-helpers.js +30 -8
- package/framework/rules/csharp-standards.md +13 -13
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/infrastructure-standards.md +16 -16
- package/framework/rules/testing-standards.md +14 -14
- package/framework/skills/README.md +0 -1
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +12 -0
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
- package/framework/skills/level-0-meta/morph-init/SKILL.md +88 -3
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +247 -247
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +270 -270
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +515 -499
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +508 -471
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +12 -1
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +272 -258
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +317 -311
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +424 -323
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +2040 -2040
- package/framework/standards/frontend/nextjs/motion-patterns.md +321 -0
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
- package/framework/templates/README.md +814 -814
- package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
- package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
- package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
- package/framework/templates/code/dotnet/contracts/README.md +74 -74
- package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
- package/framework/templates/code/dotnet/frontend/component.razor +239 -239
- package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
- package/framework/templates/code/dotnet/jobs/job.cs +171 -171
- package/framework/templates/code/dotnet/test.cs +239 -239
- package/framework/templates/code/sql/rls-policy.sql +57 -57
- package/framework/templates/code/sql/supabase-migration.sql +100 -100
- package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
- package/framework/templates/code/typescript/contracts.ts +168 -168
- package/framework/templates/context/CONTEXT.md +181 -181
- package/framework/templates/context/design-system.md +211 -0
- package/framework/templates/docs/proposal.md +182 -182
- package/framework/templates/docs/user-stories.md +34 -34
- package/framework/templates/examples/design-system-examples.md +357 -357
- package/framework/templates/examples/spec-examples.md +90 -90
- package/framework/templates/feature/decisions.md +187 -187
- package/framework/templates/feature/recap.md +146 -146
- package/framework/templates/feature/tasks.md +199 -199
- package/framework/templates/infrastructure/azure/README.md +286 -286
- package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
- package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
- package/package.json +82 -82
- package/src/commands/agents/agent-metrics.js +174 -0
- package/src/commands/agents/audit-dispatch.js +82 -0
- package/src/commands/agents/detect-agents.js +275 -0
- package/src/commands/agents/dispatch-agents.js +41 -135
- package/src/commands/agents/index.js +13 -4
- package/src/commands/agents/manage-agents.js +352 -0
- package/src/commands/project/doctor.js +59 -0
- package/src/commands/project/install-plugin-cmd.js +28 -28
- package/src/commands/project/setup-infra-cmd.js +12 -12
- package/src/commands/project/update.js +1 -17
- package/src/commands/project/worktree.js +58 -2
- package/src/commands/state/advance-phase.js +11 -3
- package/src/commands/tasks/index.js +4 -4
- package/src/commands/templates/index.js +5 -5
- package/src/commands/utils/index.js +3 -3
- package/src/commands/validation/index.js +4 -4
- package/src/core/state/phase-state-machine.js +217 -217
- package/src/core/state/state-manager.js +81 -0
- package/src/lib/agents/agents-manager.js +349 -0
- package/src/lib/agents/metrics-calculator.js +125 -0
- package/src/lib/agents/output-collector.js +75 -0
- package/src/lib/generators/context-generator.js +8 -7
- package/src/lib/generators/design-system-generator.js +298 -298
- package/src/lib/generators/index.js +10 -10
- package/src/lib/generators/metadata-extractor.js +387 -387
- package/src/lib/stack-filter.js +65 -65
- package/src/lib/standards/index.js +7 -7
- package/src/lib/standards/standards-context-injector.js +302 -302
- package/src/lib/tasks/task-parser.js +94 -3
- package/src/lib/validators/architecture/architecture-validator.js +387 -387
- package/src/lib/validators/architecture/index.js +7 -7
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
- package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
- package/src/lib/validators/blazor/blazor-validator.js +369 -369
- package/src/lib/validators/blazor/index.js +9 -9
- package/src/lib/validators/checkpoint-persistence.js +39 -0
- package/src/lib/validators/content/content-validator.js +287 -279
- package/src/lib/validators/content/index.js +7 -7
- package/src/lib/validators/contracts/index.js +7 -7
- package/src/lib/validators/css/css-validator.js +352 -352
- package/src/lib/validators/css/index.js +7 -7
- package/src/lib/validators/design-system/design-system-validator.js +231 -231
- package/src/lib/validators/design-system/index.js +7 -7
- package/src/lib/validators/nextjs/index.js +6 -6
- package/src/lib/validators/nextjs/next-component-validator.js +183 -183
- package/src/lib/validators/packages/index.js +7 -7
- package/src/lib/validators/packages/package-validator.js +360 -360
- package/src/lib/validators/shared/index.js +12 -12
- package/src/lib/validators/shared/issue-counter.js +18 -18
- package/src/lib/validators/shared/result-formatter.js +124 -124
- package/src/lib/validators/spec-validator.js +2 -2
- package/src/lib/validators/ui/index.js +7 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
- package/src/lib/validators/validation-runner.js +20 -0
- package/src/scripts/global-install.js +58 -58
- package/src/scripts/install-plugin.js +126 -126
- package/src/scripts/setup-infra.js +1 -17
- package/src/utils/agent-id-mapper.js +11 -0
- package/src/utils/agents-installer.js +40 -8
- package/src/utils/claude-md-injector.js +90 -90
- package/src/utils/claude-settings-manager.js +1 -1
- package/src/utils/file-copier.js +200 -196
- package/src/utils/hooks-installer.js +556 -739
- package/src/utils/logger.js +32 -32
- package/src/utils/skills-installer.js +178 -178
- package/src/utils/tier-helpers.js +24 -0
- package/src/utils/version-checker.js +175 -175
- package/framework/agents/architecture/po-pm-advisor.md +0 -203
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +0 -68
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
|
@@ -1,175 +1,175 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { join, dirname } from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import { pathExists, readJson, writeJson } from './file-copier.js';
|
|
5
|
-
|
|
6
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get the current CLI version from package.json
|
|
10
|
-
* @returns {string} Version string (e.g., "1.0.4")
|
|
11
|
-
*/
|
|
12
|
-
export function getInstalledCLIVersion() {
|
|
13
|
-
try {
|
|
14
|
-
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
15
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
16
|
-
return pkg.version;
|
|
17
|
-
} catch (error) {
|
|
18
|
-
return 'unknown';
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Get the latest version from npm registry
|
|
24
|
-
* @param {string} packageName - The package name (default: @polymorphism-tech/morph-spec)
|
|
25
|
-
* @returns {Promise<string|null>} Latest version or null if failed
|
|
26
|
-
*/
|
|
27
|
-
export async function getLatestNPMVersion(packageName = '@polymorphism-tech/morph-spec') {
|
|
28
|
-
try {
|
|
29
|
-
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
|
|
30
|
-
if (!response.ok) return null;
|
|
31
|
-
const data = await response.json();
|
|
32
|
-
return data.version;
|
|
33
|
-
} catch (error) {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Get the MORPH version installed in the project
|
|
40
|
-
* @param {string} projectPath - Path to the project
|
|
41
|
-
* @returns {Promise<object|null>} Version info object or null if not found
|
|
42
|
-
*/
|
|
43
|
-
export async function getProjectMorphVersion(projectPath) {
|
|
44
|
-
try {
|
|
45
|
-
const versionPath = join(projectPath, '.morph', '.morphversion');
|
|
46
|
-
if (!(await pathExists(versionPath))) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
return await readJson(versionPath);
|
|
50
|
-
} catch (error) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Save the MORPH version to the project
|
|
57
|
-
* @param {string} projectPath - Path to the project
|
|
58
|
-
* @param {string} version - Version to save
|
|
59
|
-
* @returns {Promise<void>}
|
|
60
|
-
*/
|
|
61
|
-
export async function saveProjectMorphVersion(projectPath, version) {
|
|
62
|
-
const versionPath = join(projectPath, '.morph', '.morphversion');
|
|
63
|
-
const versionInfo = {
|
|
64
|
-
morphVersion: version,
|
|
65
|
-
installedAt: new Date().toISOString(),
|
|
66
|
-
cliVersion: version
|
|
67
|
-
};
|
|
68
|
-
await writeJson(versionPath, versionInfo);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Compare two semver versions
|
|
73
|
-
* @param {string} v1 - First version
|
|
74
|
-
* @param {string} v2 - Second version
|
|
75
|
-
* @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
76
|
-
*/
|
|
77
|
-
export function compareVersions(v1, v2) {
|
|
78
|
-
if (!v1 || !v2) return 0;
|
|
79
|
-
|
|
80
|
-
const parts1 = v1.split('.').map(Number);
|
|
81
|
-
const parts2 = v2.split('.').map(Number);
|
|
82
|
-
|
|
83
|
-
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
84
|
-
const num1 = parts1[i] || 0;
|
|
85
|
-
const num2 = parts2[i] || 0;
|
|
86
|
-
|
|
87
|
-
if (num1 < num2) return -1;
|
|
88
|
-
if (num1 > num2) return 1;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return 0;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Detect how the CLI was installed
|
|
96
|
-
* @returns {'global'|'npx'|'unknown'}
|
|
97
|
-
*/
|
|
98
|
-
export function detectInstallMethod() {
|
|
99
|
-
// Check if running via npx
|
|
100
|
-
if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) {
|
|
101
|
-
return 'npx';
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Check if installed globally
|
|
105
|
-
const isGlobal = process.env.npm_config_global === 'true' ||
|
|
106
|
-
process.execPath.includes('node_modules');
|
|
107
|
-
|
|
108
|
-
if (isGlobal) {
|
|
109
|
-
return 'global';
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return 'unknown';
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Get update instructions based on install method
|
|
117
|
-
* @param {string} method - Install method ('global', 'npx', 'unknown')
|
|
118
|
-
* @returns {string[]} Array of instruction strings
|
|
119
|
-
*/
|
|
120
|
-
export function getUpdateInstructions(method) {
|
|
121
|
-
switch (method) {
|
|
122
|
-
case 'global':
|
|
123
|
-
return [
|
|
124
|
-
'npm install -g @polymorphism-tech/morph-spec@latest',
|
|
125
|
-
'morph-spec update'
|
|
126
|
-
];
|
|
127
|
-
case 'npx':
|
|
128
|
-
return [
|
|
129
|
-
'npx @polymorphism-tech/morph-spec@latest update'
|
|
130
|
-
];
|
|
131
|
-
default:
|
|
132
|
-
return [
|
|
133
|
-
'npm install -g @polymorphism-tech/morph-spec@latest',
|
|
134
|
-
'morph-spec update',
|
|
135
|
-
'',
|
|
136
|
-
'Or use npx:',
|
|
137
|
-
'npx @polymorphism-tech/morph-spec@latest update'
|
|
138
|
-
];
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Check if CLI version is outdated
|
|
144
|
-
* @returns {Promise<{isOutdated: boolean, current: string, latest: string|null}>}
|
|
145
|
-
*/
|
|
146
|
-
export async function checkCLIOutdated() {
|
|
147
|
-
const current = getInstalledCLIVersion();
|
|
148
|
-
const latest = await getLatestNPMVersion();
|
|
149
|
-
|
|
150
|
-
if (!latest) {
|
|
151
|
-
return { isOutdated: false, current, latest: null };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const isOutdated = compareVersions(current, latest) < 0;
|
|
155
|
-
|
|
156
|
-
return { isOutdated, current, latest };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Check if project MORPH version is outdated
|
|
161
|
-
* @param {string} projectPath - Path to the project
|
|
162
|
-
* @returns {Promise<{isOutdated: boolean, current: string|null, cliVersion: string}>}
|
|
163
|
-
*/
|
|
164
|
-
export async function checkProjectOutdated(projectPath) {
|
|
165
|
-
const cliVersion = getInstalledCLIVersion();
|
|
166
|
-
const projectVersion = await getProjectMorphVersion(projectPath);
|
|
167
|
-
|
|
168
|
-
if (!projectVersion) {
|
|
169
|
-
return { isOutdated: false, current: null, cliVersion };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const isOutdated = compareVersions(projectVersion.morphVersion, cliVersion) < 0;
|
|
173
|
-
|
|
174
|
-
return { isOutdated, current: projectVersion.morphVersion, cliVersion };
|
|
175
|
-
}
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { pathExists, readJson, writeJson } from './file-copier.js';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the current CLI version from package.json
|
|
10
|
+
* @returns {string} Version string (e.g., "1.0.4")
|
|
11
|
+
*/
|
|
12
|
+
export function getInstalledCLIVersion() {
|
|
13
|
+
try {
|
|
14
|
+
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
15
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
16
|
+
return pkg.version;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
return 'unknown';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the latest version from npm registry
|
|
24
|
+
* @param {string} packageName - The package name (default: @polymorphism-tech/morph-spec)
|
|
25
|
+
* @returns {Promise<string|null>} Latest version or null if failed
|
|
26
|
+
*/
|
|
27
|
+
export async function getLatestNPMVersion(packageName = '@polymorphism-tech/morph-spec') {
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
|
|
30
|
+
if (!response.ok) return null;
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
return data.version;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the MORPH version installed in the project
|
|
40
|
+
* @param {string} projectPath - Path to the project
|
|
41
|
+
* @returns {Promise<object|null>} Version info object or null if not found
|
|
42
|
+
*/
|
|
43
|
+
export async function getProjectMorphVersion(projectPath) {
|
|
44
|
+
try {
|
|
45
|
+
const versionPath = join(projectPath, '.morph', '.morphversion');
|
|
46
|
+
if (!(await pathExists(versionPath))) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return await readJson(versionPath);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Save the MORPH version to the project
|
|
57
|
+
* @param {string} projectPath - Path to the project
|
|
58
|
+
* @param {string} version - Version to save
|
|
59
|
+
* @returns {Promise<void>}
|
|
60
|
+
*/
|
|
61
|
+
export async function saveProjectMorphVersion(projectPath, version) {
|
|
62
|
+
const versionPath = join(projectPath, '.morph', '.morphversion');
|
|
63
|
+
const versionInfo = {
|
|
64
|
+
morphVersion: version,
|
|
65
|
+
installedAt: new Date().toISOString(),
|
|
66
|
+
cliVersion: version
|
|
67
|
+
};
|
|
68
|
+
await writeJson(versionPath, versionInfo);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Compare two semver versions
|
|
73
|
+
* @param {string} v1 - First version
|
|
74
|
+
* @param {string} v2 - Second version
|
|
75
|
+
* @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
76
|
+
*/
|
|
77
|
+
export function compareVersions(v1, v2) {
|
|
78
|
+
if (!v1 || !v2) return 0;
|
|
79
|
+
|
|
80
|
+
const parts1 = v1.split('.').map(Number);
|
|
81
|
+
const parts2 = v2.split('.').map(Number);
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
84
|
+
const num1 = parts1[i] || 0;
|
|
85
|
+
const num2 = parts2[i] || 0;
|
|
86
|
+
|
|
87
|
+
if (num1 < num2) return -1;
|
|
88
|
+
if (num1 > num2) return 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Detect how the CLI was installed
|
|
96
|
+
* @returns {'global'|'npx'|'unknown'}
|
|
97
|
+
*/
|
|
98
|
+
export function detectInstallMethod() {
|
|
99
|
+
// Check if running via npx
|
|
100
|
+
if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) {
|
|
101
|
+
return 'npx';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check if installed globally
|
|
105
|
+
const isGlobal = process.env.npm_config_global === 'true' ||
|
|
106
|
+
process.execPath.includes('node_modules');
|
|
107
|
+
|
|
108
|
+
if (isGlobal) {
|
|
109
|
+
return 'global';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return 'unknown';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get update instructions based on install method
|
|
117
|
+
* @param {string} method - Install method ('global', 'npx', 'unknown')
|
|
118
|
+
* @returns {string[]} Array of instruction strings
|
|
119
|
+
*/
|
|
120
|
+
export function getUpdateInstructions(method) {
|
|
121
|
+
switch (method) {
|
|
122
|
+
case 'global':
|
|
123
|
+
return [
|
|
124
|
+
'npm install -g @polymorphism-tech/morph-spec@latest',
|
|
125
|
+
'morph-spec update'
|
|
126
|
+
];
|
|
127
|
+
case 'npx':
|
|
128
|
+
return [
|
|
129
|
+
'npx @polymorphism-tech/morph-spec@latest update'
|
|
130
|
+
];
|
|
131
|
+
default:
|
|
132
|
+
return [
|
|
133
|
+
'npm install -g @polymorphism-tech/morph-spec@latest',
|
|
134
|
+
'morph-spec update',
|
|
135
|
+
'',
|
|
136
|
+
'Or use npx:',
|
|
137
|
+
'npx @polymorphism-tech/morph-spec@latest update'
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if CLI version is outdated
|
|
144
|
+
* @returns {Promise<{isOutdated: boolean, current: string, latest: string|null}>}
|
|
145
|
+
*/
|
|
146
|
+
export async function checkCLIOutdated() {
|
|
147
|
+
const current = getInstalledCLIVersion();
|
|
148
|
+
const latest = await getLatestNPMVersion();
|
|
149
|
+
|
|
150
|
+
if (!latest) {
|
|
151
|
+
return { isOutdated: false, current, latest: null };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const isOutdated = compareVersions(current, latest) < 0;
|
|
155
|
+
|
|
156
|
+
return { isOutdated, current, latest };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check if project MORPH version is outdated
|
|
161
|
+
* @param {string} projectPath - Path to the project
|
|
162
|
+
* @returns {Promise<{isOutdated: boolean, current: string|null, cliVersion: string}>}
|
|
163
|
+
*/
|
|
164
|
+
export async function checkProjectOutdated(projectPath) {
|
|
165
|
+
const cliVersion = getInstalledCLIVersion();
|
|
166
|
+
const projectVersion = await getProjectMorphVersion(projectPath);
|
|
167
|
+
|
|
168
|
+
if (!projectVersion) {
|
|
169
|
+
return { isOutdated: false, current: null, cliVersion };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const isOutdated = compareVersions(projectVersion.morphVersion, cliVersion) < 0;
|
|
173
|
+
|
|
174
|
+
return { isOutdated, current: projectVersion.morphVersion, cliVersion };
|
|
175
|
+
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: po-pm-advisor
|
|
3
|
-
description: PO/PM Advisor specialist for clarifying product requirements, defining acceptance criteria, writing user stories, and evaluating feature ROI. Use when defining feature scope, writing user stories with acceptance criteria, prioritizing the backlog, or resolving requirements conflicts.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# PO/PM Advisor
|
|
8
|
-
|
|
9
|
-
Especialista em requisitos, priorização e gestão de produto para clarificar features e ROI.
|
|
10
|
-
|
|
11
|
-
## Responsabilidades
|
|
12
|
-
|
|
13
|
-
1. **Clarificar requisitos** ambíguos ou incompletos
|
|
14
|
-
2. **Priorizar features** por valor de negócio
|
|
15
|
-
3. **Calcular ROI** e esforço estimado
|
|
16
|
-
4. **Definir critérios** de aceitação
|
|
17
|
-
|
|
18
|
-
## Triggers
|
|
19
|
-
|
|
20
|
-
Keywords: `unclear`, `requirements`, `priority`, `ROI`, `mvp`, `scope`, `acceptance criteria`, `user story`
|
|
21
|
-
|
|
22
|
-
## Framework de Clarificação
|
|
23
|
-
|
|
24
|
-
### Perguntas Essenciais
|
|
25
|
-
|
|
26
|
-
Quando receber uma feature request ambígua, faça estas perguntas:
|
|
27
|
-
|
|
28
|
-
1. **Quem?** - Qual o usuário/persona principal?
|
|
29
|
-
2. **O quê?** - O que exatamente deve acontecer?
|
|
30
|
-
3. **Por quê?** - Qual problema resolve? Qual o valor?
|
|
31
|
-
4. **Quando?** - Urgência? Deadline externo?
|
|
32
|
-
5. **Como medir?** - Como saberemos que funcionou?
|
|
33
|
-
|
|
34
|
-
### Template de User Story
|
|
35
|
-
|
|
36
|
-
```markdown
|
|
37
|
-
## User Story: {Nome da Feature}
|
|
38
|
-
|
|
39
|
-
**Como** {persona/usuário},
|
|
40
|
-
**Eu quero** {ação/funcionalidade},
|
|
41
|
-
**Para que** {benefício/valor}.
|
|
42
|
-
|
|
43
|
-
### Critérios de Aceitação
|
|
44
|
-
|
|
45
|
-
**Dado** {contexto inicial},
|
|
46
|
-
**Quando** {ação do usuário},
|
|
47
|
-
**Então** {resultado esperado}.
|
|
48
|
-
|
|
49
|
-
### Fora do Escopo
|
|
50
|
-
|
|
51
|
-
- {Item explicitamente excluído}
|
|
52
|
-
|
|
53
|
-
### Perguntas Pendentes
|
|
54
|
-
|
|
55
|
-
- [ ] {Dúvida não resolvida}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Matriz de Priorização (RICE)
|
|
59
|
-
|
|
60
|
-
| Fator | Descrição | Escala |
|
|
61
|
-
|-------|-----------|--------|
|
|
62
|
-
| **R**each | Quantos usuários impacta | 1-10 |
|
|
63
|
-
| **I**mpact | Quanto impacta cada usuário | 0.25, 0.5, 1, 2, 3 |
|
|
64
|
-
| **C**onfidence | Confiança nas estimativas | 0-100% |
|
|
65
|
-
| **E**ffort | Pessoa-semanas de trabalho | 1+ |
|
|
66
|
-
|
|
67
|
-
```
|
|
68
|
-
RICE Score = (Reach × Impact × Confidence) / Effort
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Exemplo
|
|
72
|
-
|
|
73
|
-
| Feature | Reach | Impact | Confidence | Effort | Score |
|
|
74
|
-
|---------|-------|--------|------------|--------|-------|
|
|
75
|
-
| Login social | 8 | 1 | 80% | 1 | 6.4 |
|
|
76
|
-
| Dashboard | 5 | 2 | 90% | 3 | 3.0 |
|
|
77
|
-
| Export PDF | 3 | 0.5 | 100% | 0.5 | 3.0 |
|
|
78
|
-
| Dark mode | 4 | 0.25 | 100% | 1 | 1.0 |
|
|
79
|
-
|
|
80
|
-
## MVP vs Nice-to-Have
|
|
81
|
-
|
|
82
|
-
### Critérios para MVP
|
|
83
|
-
|
|
84
|
-
1. **Resolve o problema core** do usuário
|
|
85
|
-
2. **Diferencia** da concorrência
|
|
86
|
-
3. **Validável** em 2-4 semanas
|
|
87
|
-
4. **Mensurável** (métricas definidas)
|
|
88
|
-
|
|
89
|
-
### Framework MoSCoW
|
|
90
|
-
|
|
91
|
-
| Categoria | Significado | Exemplo |
|
|
92
|
-
|-----------|-------------|---------|
|
|
93
|
-
| **M**ust have | Essencial, sem isso não funciona | Login, CRUD básico |
|
|
94
|
-
| **S**hould have | Importante, mas não crítico | Filtros avançados |
|
|
95
|
-
| **C**ould have | Desejável se houver tempo | Exportar relatório |
|
|
96
|
-
| **W**on't have | Fora deste release | Integração X |
|
|
97
|
-
|
|
98
|
-
## Estimativa de Esforço
|
|
99
|
-
|
|
100
|
-
### T-Shirt Sizing
|
|
101
|
-
|
|
102
|
-
| Size | Dias | Complexidade |
|
|
103
|
-
|------|------|--------------|
|
|
104
|
-
| XS | 0.5 | Task trivial |
|
|
105
|
-
| S | 1-2 | Feature simples |
|
|
106
|
-
| M | 3-5 | Feature com complexidade |
|
|
107
|
-
| L | 5-10 | Feature complexa |
|
|
108
|
-
| XL | 10+ | Épico, precisa quebrar |
|
|
109
|
-
|
|
110
|
-
### Fatores de Risco
|
|
111
|
-
|
|
112
|
-
Multiplicar estimativa por:
|
|
113
|
-
|
|
114
|
-
| Fator | Multiplicador |
|
|
115
|
-
|-------|---------------|
|
|
116
|
-
| Tecnologia nova | 1.5x |
|
|
117
|
-
| Integração externa | 1.3x |
|
|
118
|
-
| Requisitos vagos | 1.5x |
|
|
119
|
-
| Primeiro da equipe | 1.3x |
|
|
120
|
-
|
|
121
|
-
## Template de ADR (Architecture Decision Record)
|
|
122
|
-
|
|
123
|
-
```markdown
|
|
124
|
-
# ADR-{XXX}: {Título da Decisão}
|
|
125
|
-
|
|
126
|
-
## Status
|
|
127
|
-
{PROPOSED | ACCEPTED | DEPRECATED | SUPERSEDED}
|
|
128
|
-
|
|
129
|
-
## Contexto
|
|
130
|
-
{Situação que requer decisão}
|
|
131
|
-
|
|
132
|
-
## Opções Consideradas
|
|
133
|
-
|
|
134
|
-
### Opção 1: {Nome}
|
|
135
|
-
- Prós: ...
|
|
136
|
-
- Contras: ...
|
|
137
|
-
- Custo: ...
|
|
138
|
-
|
|
139
|
-
### Opção 2: {Nome}
|
|
140
|
-
- Prós: ...
|
|
141
|
-
- Contras: ...
|
|
142
|
-
- Custo: ...
|
|
143
|
-
|
|
144
|
-
## Decisão
|
|
145
|
-
{Opção escolhida e por quê}
|
|
146
|
-
|
|
147
|
-
## Consequências
|
|
148
|
-
- {Implicação positiva}
|
|
149
|
-
- {Implicação negativa}
|
|
150
|
-
- {Trade-off aceito}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
## Cálculo de ROI
|
|
154
|
-
|
|
155
|
-
```markdown
|
|
156
|
-
## ROI Estimado: {Feature}
|
|
157
|
-
|
|
158
|
-
### Benefícios (anual)
|
|
159
|
-
- Aumento de conversão: R$ X
|
|
160
|
-
- Redução de suporte: R$ Y
|
|
161
|
-
- Economia de tempo: R$ Z
|
|
162
|
-
- **Total Benefícios**: R$ (X+Y+Z)
|
|
163
|
-
|
|
164
|
-
### Custos
|
|
165
|
-
- Desenvolvimento: R$ A (horas × rate)
|
|
166
|
-
- Infraestrutura: R$ B/mês × 12
|
|
167
|
-
- Manutenção: R$ C/ano
|
|
168
|
-
- **Total Custos**: R$ (A+B+C)
|
|
169
|
-
|
|
170
|
-
### ROI
|
|
171
|
-
**ROI = (Benefícios - Custos) / Custos × 100**
|
|
172
|
-
|
|
173
|
-
ROI = (X+Y+Z - A+B+C) / (A+B+C) × 100 = **{N}%**
|
|
174
|
-
|
|
175
|
-
### Payback
|
|
176
|
-
**Meses = Custo Inicial / (Benefício Mensal - Custo Mensal)**
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Red Flags em Requisitos
|
|
180
|
-
|
|
181
|
-
Alertar quando detectar:
|
|
182
|
-
|
|
183
|
-
- "Deve ser igual ao [concorrente X]" sem especificar
|
|
184
|
-
- "Simples" ou "rápido" sem estimativa
|
|
185
|
-
- "Usuários querem..." sem evidência
|
|
186
|
-
- Escopo crescente sem priorização
|
|
187
|
-
- Deadline impossível sem trade-offs
|
|
188
|
-
|
|
189
|
-
## Checklist de Requisitos
|
|
190
|
-
|
|
191
|
-
- [ ] Persona/usuário definido
|
|
192
|
-
- [ ] Problema claramente articulado
|
|
193
|
-
- [ ] Valor de negócio quantificado
|
|
194
|
-
- [ ] Critérios de aceitação escritos
|
|
195
|
-
- [ ] Fora do escopo explícito
|
|
196
|
-
- [ ] Dependências mapeadas
|
|
197
|
-
- [ ] Riscos identificados
|
|
198
|
-
- [ ] Estimativa de esforço
|
|
199
|
-
- [ ] Prioridade definida (RICE/MoSCoW)
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* UserPromptSubmit Hook: Set Terminal Title
|
|
5
|
-
*
|
|
6
|
-
* Event: UserPromptSubmit
|
|
7
|
-
*
|
|
8
|
-
* Extracts a concise title from the user's prompt and sets it as the
|
|
9
|
-
* terminal window title using ANSI escape sequences written to /dev/tty.
|
|
10
|
-
* Also saves to ~/.claude/terminal_title for shell hook integration.
|
|
11
|
-
*
|
|
12
|
-
* Title format: "[first ~40 chars of prompt, truncated at word boundary]"
|
|
13
|
-
*
|
|
14
|
-
* Fail-open: exits 0 on any error.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { readStdin } from '../../shared/stdin-reader.js';
|
|
18
|
-
import { openSync, writeSync, closeSync } from 'fs';
|
|
19
|
-
import { writeFile, mkdir } from 'fs/promises';
|
|
20
|
-
import { homedir } from 'os';
|
|
21
|
-
import { join } from 'path';
|
|
22
|
-
import { logHookActivity } from '../../shared/activity-logger.js';
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const payload = await readStdin();
|
|
26
|
-
if (!payload) process.exit(0);
|
|
27
|
-
|
|
28
|
-
const prompt = payload?.prompt || payload?.user_prompt || payload?.content || '';
|
|
29
|
-
if (!prompt || prompt.length < 3) process.exit(0);
|
|
30
|
-
|
|
31
|
-
// Generate title: clean prompt, truncate at word boundary within 40 chars
|
|
32
|
-
const cleaned = prompt.replace(/[\r\n\t]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
33
|
-
const maxLen = 40;
|
|
34
|
-
let title = cleaned.slice(0, maxLen);
|
|
35
|
-
if (cleaned.length > maxLen) {
|
|
36
|
-
const lastSpace = title.lastIndexOf(' ');
|
|
37
|
-
if (lastSpace > 20) title = title.slice(0, lastSpace);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const prefix = process.env.CLAUDE_TITLE_PREFIX ? `${process.env.CLAUDE_TITLE_PREFIX} ` : '';
|
|
41
|
-
const finalTitle = `${prefix}${title}`;
|
|
42
|
-
|
|
43
|
-
// Save to session-specific file (MORPH_TERMINAL_TITLE_FILE) or fallback to global
|
|
44
|
-
// MORPH_TERMINAL_TITLE_FILE is set by the shell wrapper (Invoke-Claude / claude())
|
|
45
|
-
// per-session so multiple terminal windows don't interfere with each other.
|
|
46
|
-
try {
|
|
47
|
-
const titleFilePath = process.env.MORPH_TERMINAL_TITLE_FILE
|
|
48
|
-
|| join(homedir(), '.claude', 'terminal_title');
|
|
49
|
-
const claudeDir = join(homedir(), '.claude');
|
|
50
|
-
await mkdir(claudeDir, { recursive: true });
|
|
51
|
-
await writeFile(titleFilePath, finalTitle, 'utf-8');
|
|
52
|
-
} catch { /* non-critical */ }
|
|
53
|
-
|
|
54
|
-
// Write ANSI escape to terminal device (bypasses stdout capture by Claude Code)
|
|
55
|
-
// Windows uses \\.\CONOUT$ as the equivalent of /dev/tty
|
|
56
|
-
let wrote = false;
|
|
57
|
-
try {
|
|
58
|
-
const devicePath = process.platform === 'win32' ? '\\\\.\\CONOUT$' : '/dev/tty';
|
|
59
|
-
const tty = openSync(devicePath, 'w');
|
|
60
|
-
writeSync(tty, `\x1b]0;${finalTitle}\x07`);
|
|
61
|
-
closeSync(tty);
|
|
62
|
-
wrote = true;
|
|
63
|
-
} catch { /* terminal may not support direct device access */ }
|
|
64
|
-
|
|
65
|
-
logHookActivity('set-terminal-title', 'UserPromptSubmit', wrote ? 'ok' : 'failed');
|
|
66
|
-
} catch { /* fail-open */ }
|
|
67
|
-
|
|
68
|
-
process.exit(0);
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"sessionId": "",
|
|
3
|
-
"feature": "",
|
|
4
|
-
"phase": "",
|
|
5
|
-
"hooks": [
|
|
6
|
-
{
|
|
7
|
-
"name": "set-terminal-title",
|
|
8
|
-
"event": "UserPromptSubmit",
|
|
9
|
-
"result": "failed",
|
|
10
|
-
"ts": "16:06:20"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"name": "set-terminal-title",
|
|
14
|
-
"event": "UserPromptSubmit",
|
|
15
|
-
"result": "failed",
|
|
16
|
-
"ts": "16:12:07"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"name": "set-terminal-title",
|
|
20
|
-
"event": "UserPromptSubmit",
|
|
21
|
-
"result": "failed",
|
|
22
|
-
"ts": "16:12:15"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"name": "set-terminal-title",
|
|
26
|
-
"event": "UserPromptSubmit",
|
|
27
|
-
"result": "failed",
|
|
28
|
-
"ts": "16:12:15"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"name": "set-terminal-title",
|
|
32
|
-
"event": "UserPromptSubmit",
|
|
33
|
-
"result": "failed",
|
|
34
|
-
"ts": "16:12:40"
|
|
35
|
-
}
|
|
36
|
-
],
|
|
37
|
-
"skills": []
|
|
38
|
-
}
|