@polymorphism-tech/morph-spec 4.8.18 → 4.9.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/CLAUDE.md +98 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +15 -56
- package/bin/task-manager.js +115 -14
- package/bin/validate.js +67 -33
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +201 -203
- package/docs/QUICKSTART.md +2 -2
- package/framework/CLAUDE.md +21 -0
- package/framework/agents.json +758 -164
- package/framework/hooks/claude-code/post-tool-use/context-refresh.js +1 -1
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +2 -2
- package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +155 -0
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +1 -1
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +71 -2
- package/framework/hooks/claude-code/statusline.py +76 -30
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +14 -6
- package/framework/hooks/shared/activity-logger.js +0 -24
- package/framework/hooks/shared/phase-utils.js +3 -0
- package/framework/hooks/shared/skill-reminder-helpers.js +79 -0
- package/framework/hooks/shared/stale-task-reset.js +57 -0
- package/framework/hooks/shared/state-reader.js +2 -2
- package/framework/hooks/shared/worktree-helpers.js +53 -0
- package/framework/phases.json +40 -8
- package/framework/skills/level-0-meta/brainstorming/SKILL.md +1 -1
- package/framework/skills/level-0-meta/code-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/code-review-nextjs/SKILL.md +163 -163
- package/framework/skills/level-0-meta/frontend-review/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
- package/framework/skills/level-0-meta/morph-init/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +1 -1
- package/framework/skills/level-0-meta/post-implementation/SKILL.md +59 -12
- package/framework/skills/level-0-meta/simulation-checklist/SKILL.md +1 -1
- package/framework/skills/level-0-meta/terminal-title/SKILL.md +1 -1
- package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +1 -1
- package/framework/skills/level-0-meta/tool-usage-guide/references/tools-per-phase.md +6 -5
- package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +215 -189
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +251 -251
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +382 -365
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +492 -450
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +194 -190
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +270 -270
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +285 -285
- package/framework/standards/STANDARDS.json +640 -88
- package/framework/standards/infrastructure/vercel/vercel-database.md +106 -0
- package/framework/templates/REGISTRY.json +1825 -1909
- package/framework/templates/context/CONTEXT-FEATURE.md +276 -276
- package/framework/templates/docs/onboarding.md +1 -5
- package/framework/workflows/configs/nodejs-cli.json +40 -0
- package/package.json +2 -6
- package/src/commands/agents/dispatch-agents.js +55 -4
- package/src/commands/project/doctor.js +16 -47
- package/src/commands/project/init.js +1 -1
- package/src/commands/project/status.js +2 -2
- package/src/commands/project/update.js +381 -365
- package/src/commands/project/worktree.js +154 -0
- package/src/commands/state/advance-phase.js +120 -30
- package/src/commands/state/approve.js +2 -2
- package/src/commands/state/index.js +7 -8
- package/src/commands/state/phase-runner.js +1 -1
- package/src/commands/state/state.js +61 -6
- package/src/commands/tasks/task.js +78 -99
- package/src/commands/templates/template-render.js +93 -173
- package/src/commands/trust/trust.js +26 -21
- package/src/core/paths/output-schema.js +15 -0
- package/src/core/state/state-manager.js +28 -54
- package/src/core/workflows/workflow-detector.js +9 -87
- package/src/lib/phase-chain/phase-validator.js +330 -0
- package/src/lib/stack/stack-profile.js +88 -0
- package/src/lib/tasks/task-classifier.js +16 -0
- package/src/lib/tasks/test-runner.js +77 -0
- package/src/lib/trust/trust-manager.js +32 -144
- package/src/lib/validators/spec-validator.js +58 -4
- package/src/lib/validators/validation-runner.js +23 -11
- package/src/scripts/setup-infra.js +240 -224
- package/src/utils/agents-installer.js +2 -2
- package/src/utils/banner.js +1 -1
- package/src/utils/claude-settings-manager.js +1 -1
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +258 -8
- package/framework/hooks/dev/check-sync-health.js +0 -117
- package/framework/hooks/dev/guard-version-numbers.js +0 -57
- package/framework/hooks/dev/sync-standards-registry.js +0 -60
- package/framework/hooks/dev/sync-template-registry.js +0 -60
- package/framework/hooks/dev/validate-skill-format.js +0 -70
- package/framework/hooks/dev/validate-standard-format.js +0 -73
- package/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
- package/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
- package/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
- package/framework/workflows/configs/design-impl.json +0 -49
- package/framework/workflows/configs/express.json +0 -45
- package/framework/workflows/configs/fast-track.json +0 -42
- package/framework/workflows/configs/full-morph.json +0 -79
- package/framework/workflows/configs/fusion.json +0 -39
- package/framework/workflows/configs/long-running.json +0 -33
- package/framework/workflows/configs/spec-only.json +0 -43
- package/framework/workflows/configs/ui-refresh.json +0 -49
- package/framework/workflows/configs/zero-touch.json +0 -82
- package/src/commands/project/monitor.js +0 -295
- package/src/commands/project/tutorial.js +0 -115
- package/src/commands/state/validate-phase.js +0 -238
- package/src/commands/templates/generate-contracts.js +0 -445
- package/src/core/orchestrator.js +0 -171
- package/src/core/registry/command-registry.js +0 -28
- package/src/core/registry/index.js +0 -8
- package/src/core/registry/validator-registry.js +0 -204
- package/src/core/templates/template-validator.js +0 -296
- package/src/generator/config-generator.js +0 -206
- package/src/generator/templates/config.json.template +0 -40
- package/src/generator/templates/project.md.template +0 -67
- package/src/lib/agents/micro-agent-factory.js +0 -161
- package/src/lib/analysis/complexity-analyzer.js +0 -441
- package/src/lib/analysis/index.js +0 -7
- package/src/lib/analytics/analytics-engine.js +0 -345
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -298
- package/src/lib/checkpoints/index.js +0 -7
- package/src/lib/context/context-bundler.js +0 -241
- package/src/lib/context/context-optimizer.js +0 -212
- package/src/lib/context/context-tracker.js +0 -273
- package/src/lib/context/core-four-tracker.js +0 -201
- package/src/lib/context/mcp-optimizer.js +0 -200
- package/src/lib/execution/fusion-executor.js +0 -304
- package/src/lib/execution/parallel-executor.js +0 -270
- package/src/lib/hooks/stop-hook-executor.js +0 -286
- package/src/lib/hops/hop-composer.js +0 -221
- package/src/lib/phase-chain/eligibility-checker.js +0 -243
- package/src/lib/threads/thread-coordinator.js +0 -238
- package/src/lib/threads/thread-manager.js +0 -317
- package/src/lib/tracking/artifact-trail.js +0 -202
- package/src/scanner/project-scanner.js +0 -242
- package/src/ui/diff-display.js +0 -91
- package/src/ui/interactive-wizard.js +0 -96
- package/src/ui/user-review.js +0 -211
- package/src/ui/wizard-questions.js +0 -188
- package/src/utils/color-utils.js +0 -70
- package/src/utils/process-handler.js +0 -97
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview ConfigGenerator - Renders templates and generates config files
|
|
3
|
-
* @module morph-spec/generator/config-generator
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFile, access, copyFile } from 'fs/promises';
|
|
7
|
-
import { join, dirname } from 'path';
|
|
8
|
-
import Handlebars from 'handlebars';
|
|
9
|
-
import { fileURLToPath } from 'url';
|
|
10
|
-
import Ajv from 'ajv';
|
|
11
|
-
|
|
12
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
const __dirname = dirname(__filename);
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {import('../types/index.js').ProjectConfig} ProjectConfig
|
|
17
|
-
* @typedef {import('../types/index.js').GeneratedConfigs} GeneratedConfigs
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Validation Error
|
|
22
|
-
*/
|
|
23
|
-
export class ValidationError extends Error {
|
|
24
|
-
constructor(message, field, value) {
|
|
25
|
-
super(message);
|
|
26
|
-
this.name = 'ValidationError';
|
|
27
|
-
this.field = field;
|
|
28
|
-
this.value = value;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* ConfigGenerator - Generates configuration files from ProjectConfig
|
|
34
|
-
* @class
|
|
35
|
-
*/
|
|
36
|
-
export class ConfigGenerator {
|
|
37
|
-
constructor() {
|
|
38
|
-
this.projectMdTemplate = null;
|
|
39
|
-
this.configJsonTemplate = null;
|
|
40
|
-
this.ajv = new Ajv({ allErrors: true });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Load templates from filesystem
|
|
45
|
-
* @private
|
|
46
|
-
*/
|
|
47
|
-
async loadTemplates() {
|
|
48
|
-
if (this.projectMdTemplate && this.configJsonTemplate) {
|
|
49
|
-
return; // Already loaded
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const templatesDir = join(__dirname, 'templates');
|
|
53
|
-
|
|
54
|
-
const [projectMdSource, configJsonSource] = await Promise.all([
|
|
55
|
-
readFile(join(templatesDir, 'project.md.template'), 'utf-8'),
|
|
56
|
-
readFile(join(templatesDir, 'config.json.template'), 'utf-8')
|
|
57
|
-
]);
|
|
58
|
-
|
|
59
|
-
this.projectMdTemplate = Handlebars.compile(projectMdSource);
|
|
60
|
-
this.configJsonTemplate = Handlebars.compile(configJsonSource);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Generate configuration files from project config
|
|
65
|
-
* @param {ProjectConfig} projectConfig - Detected project config
|
|
66
|
-
* @returns {Promise<GeneratedConfigs>}
|
|
67
|
-
*/
|
|
68
|
-
async generate(projectConfig) {
|
|
69
|
-
// Load templates if not already loaded
|
|
70
|
-
await this.loadTemplates();
|
|
71
|
-
|
|
72
|
-
// Add generation timestamp
|
|
73
|
-
const context = {
|
|
74
|
-
...projectConfig,
|
|
75
|
-
generatedAt: new Date().toISOString()
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Render templates
|
|
79
|
-
const projectMd = this.renderProjectMd(context);
|
|
80
|
-
const configJson = this.renderConfigJson(context);
|
|
81
|
-
|
|
82
|
-
// Parse config.json to object
|
|
83
|
-
const configObject = JSON.parse(configJson);
|
|
84
|
-
|
|
85
|
-
// Validate config.json (optional, but good practice)
|
|
86
|
-
// Note: agent-schema.json validation would happen here if we had the schema
|
|
87
|
-
// For now, we just ensure it's valid JSON
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
projectMd,
|
|
91
|
-
configJson,
|
|
92
|
-
configObject
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Render project.md template
|
|
98
|
-
* @param {ProjectConfig} config - Project config
|
|
99
|
-
* @returns {string} Rendered markdown
|
|
100
|
-
*/
|
|
101
|
-
renderProjectMd(config) {
|
|
102
|
-
if (!this.projectMdTemplate) {
|
|
103
|
-
throw new Error('Templates not loaded. Call loadTemplates() first.');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return this.projectMdTemplate(config);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Render config.json template
|
|
111
|
-
* @param {ProjectConfig} config - Project config
|
|
112
|
-
* @returns {string} Rendered JSON string
|
|
113
|
-
*/
|
|
114
|
-
renderConfigJson(config) {
|
|
115
|
-
if (!this.configJsonTemplate) {
|
|
116
|
-
throw new Error('Templates not loaded. Call loadTemplates() first.');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const rendered = this.configJsonTemplate(config);
|
|
120
|
-
|
|
121
|
-
// Validate that rendered output is valid JSON
|
|
122
|
-
try {
|
|
123
|
-
JSON.parse(rendered);
|
|
124
|
-
return rendered;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
throw new ValidationError(
|
|
127
|
-
`Rendered config.json is not valid JSON: ${error.message}`,
|
|
128
|
-
'configJson',
|
|
129
|
-
rendered
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Validate config.json against agent schema
|
|
136
|
-
* @param {string} configJson - JSON string
|
|
137
|
-
* @returns {boolean} True if valid
|
|
138
|
-
* @throws {ValidationError} If validation fails
|
|
139
|
-
*/
|
|
140
|
-
validateConfigJson(configJson) {
|
|
141
|
-
// Parse JSON
|
|
142
|
-
let parsed;
|
|
143
|
-
try {
|
|
144
|
-
parsed = JSON.parse(configJson);
|
|
145
|
-
} catch (error) {
|
|
146
|
-
throw new ValidationError(
|
|
147
|
-
`Invalid JSON: ${error.message}`,
|
|
148
|
-
'configJson',
|
|
149
|
-
configJson
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Basic validation - ensure required fields exist
|
|
154
|
-
const requiredFields = ['name', 'type', 'description', 'stack', 'architecture'];
|
|
155
|
-
for (const field of requiredFields) {
|
|
156
|
-
if (!parsed[field]) {
|
|
157
|
-
throw new ValidationError(
|
|
158
|
-
`Missing required field: ${field}`,
|
|
159
|
-
field,
|
|
160
|
-
parsed
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Validate stack.backend is required
|
|
166
|
-
if (!parsed.stack || !parsed.stack.backend) {
|
|
167
|
-
throw new ValidationError(
|
|
168
|
-
'stack.backend is required',
|
|
169
|
-
'stack.backend',
|
|
170
|
-
parsed
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Backup existing configuration files
|
|
179
|
-
* @param {string} cwd - Current working directory
|
|
180
|
-
* @returns {Promise<void>}
|
|
181
|
-
*/
|
|
182
|
-
async backupExisting(cwd) {
|
|
183
|
-
const projectMdPath = join(cwd, '.morph', 'project.md');
|
|
184
|
-
const configJsonPath = join(cwd, '.morph', 'config', 'config.json');
|
|
185
|
-
|
|
186
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
187
|
-
|
|
188
|
-
// Backup project.md if exists
|
|
189
|
-
try {
|
|
190
|
-
await access(projectMdPath);
|
|
191
|
-
const backupPath = join(cwd, '.morph', `project.md.${timestamp}.backup`);
|
|
192
|
-
await copyFile(projectMdPath, backupPath);
|
|
193
|
-
} catch (error) {
|
|
194
|
-
// File doesn't exist, no need to backup
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Backup config.json if exists
|
|
198
|
-
try {
|
|
199
|
-
await access(configJsonPath);
|
|
200
|
-
const backupPath = join(cwd, '.morph', 'config', `config.json.${timestamp}.backup`);
|
|
201
|
-
await copyFile(configJsonPath, backupPath);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
// File doesn't exist, no need to backup
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../schema/agent-schema.json",
|
|
3
|
-
"name": "{{name}}",
|
|
4
|
-
"type": "{{type}}",
|
|
5
|
-
"description": "{{description}}",
|
|
6
|
-
"stack": {
|
|
7
|
-
{{#if stack.frontend}}
|
|
8
|
-
"frontend": {
|
|
9
|
-
"tech": "{{stack.frontend.tech}}",
|
|
10
|
-
"version": "{{stack.frontend.version}}"{{#if stack.frontend.details}},
|
|
11
|
-
"details": "{{stack.frontend.details}}"{{/if}}
|
|
12
|
-
},
|
|
13
|
-
{{/if}}
|
|
14
|
-
"backend": {
|
|
15
|
-
"tech": "{{stack.backend.tech}}",
|
|
16
|
-
"version": "{{stack.backend.version}}"{{#if stack.backend.details}},
|
|
17
|
-
"details": "{{stack.backend.details}}"{{/if}}
|
|
18
|
-
}{{#if stack.database}},
|
|
19
|
-
"database": {
|
|
20
|
-
"tech": "{{stack.database.tech}}",
|
|
21
|
-
"version": "{{stack.database.version}}"{{#if stack.database.details}},
|
|
22
|
-
"details": "{{stack.database.details}}"{{/if}}
|
|
23
|
-
}{{/if}}{{#if stack.hosting}},
|
|
24
|
-
"hosting": "{{stack.hosting}}"{{/if}}
|
|
25
|
-
},
|
|
26
|
-
"architecture": "{{architecture}}",
|
|
27
|
-
"conventions": "{{conventions}}",
|
|
28
|
-
"infrastructure": {
|
|
29
|
-
"azure": {{hasAzure}},
|
|
30
|
-
"docker": {{hasDocker}},
|
|
31
|
-
"devops": {{hasDevOps}}
|
|
32
|
-
}{{#if repository}},
|
|
33
|
-
"repository": "{{repository}}"{{/if}},
|
|
34
|
-
"meta": {
|
|
35
|
-
"generatedBy": "morph-spec-cli",
|
|
36
|
-
"generatedAt": "{{generatedAt}}",
|
|
37
|
-
"llmConfidence": {{confidence}},
|
|
38
|
-
"autoDetected": true
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# {{name}}
|
|
2
|
-
|
|
3
|
-
> {{description}}
|
|
4
|
-
|
|
5
|
-
## Stack
|
|
6
|
-
|
|
7
|
-
| Component | Technology |
|
|
8
|
-
|-----------|------------|
|
|
9
|
-
{{#if stack.frontend}}
|
|
10
|
-
| **Frontend** | {{stack.frontend.tech}} {{stack.frontend.version}}{{#if stack.frontend.details}} - {{stack.frontend.details}}{{/if}} |
|
|
11
|
-
{{/if}}
|
|
12
|
-
| **Backend** | {{stack.backend.tech}} {{stack.backend.version}}{{#if stack.backend.details}} - {{stack.backend.details}}{{/if}} |
|
|
13
|
-
{{#if stack.database}}
|
|
14
|
-
| **Database** | {{stack.database.tech}} {{stack.database.version}}{{#if stack.database.details}} - {{stack.database.details}}{{/if}} |
|
|
15
|
-
{{/if}}
|
|
16
|
-
{{#if stack.hosting}}
|
|
17
|
-
| **Hosting** | {{stack.hosting}} |
|
|
18
|
-
{{/if}}
|
|
19
|
-
|
|
20
|
-
## Architecture
|
|
21
|
-
|
|
22
|
-
**Pattern:** {{architecture}}
|
|
23
|
-
|
|
24
|
-
{{projectStructure}}
|
|
25
|
-
|
|
26
|
-
## Code Conventions
|
|
27
|
-
|
|
28
|
-
{{conventions}}
|
|
29
|
-
|
|
30
|
-
## Infrastructure
|
|
31
|
-
|
|
32
|
-
{{#if hasAzure}}
|
|
33
|
-
- ✅ **Azure** - Uses Azure infrastructure (Bicep files detected)
|
|
34
|
-
{{else}}
|
|
35
|
-
- ❌ **Azure** - No Azure resources detected
|
|
36
|
-
{{/if}}
|
|
37
|
-
|
|
38
|
-
{{#if hasDocker}}
|
|
39
|
-
- ✅ **Docker** - Containerized (Dockerfile/docker-compose.yml detected)
|
|
40
|
-
{{else}}
|
|
41
|
-
- ❌ **Docker** - Not containerized
|
|
42
|
-
{{/if}}
|
|
43
|
-
|
|
44
|
-
{{#if hasDevOps}}
|
|
45
|
-
- ✅ **CI/CD** - Pipelines detected
|
|
46
|
-
{{else}}
|
|
47
|
-
- ❌ **CI/CD** - No pipelines detected
|
|
48
|
-
{{/if}}
|
|
49
|
-
|
|
50
|
-
{{#if repository}}
|
|
51
|
-
## Repository
|
|
52
|
-
|
|
53
|
-
{{repository}}
|
|
54
|
-
{{/if}}
|
|
55
|
-
|
|
56
|
-
{{#if warnings.length}}
|
|
57
|
-
## ⚠️ Warnings
|
|
58
|
-
|
|
59
|
-
{{#each warnings}}
|
|
60
|
-
- {{this}}
|
|
61
|
-
{{/each}}
|
|
62
|
-
{{/if}}
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
*Auto-generated by MORPH-SPEC CLI on {{generatedAt}}*
|
|
67
|
-
*LLM Confidence: {{confidence}}%*
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Micro Agent Factory — Create ultra-specialized agents
|
|
3
|
-
*
|
|
4
|
-
* Micro-agents are stripped-down versions of base agents with:
|
|
5
|
-
* - Only the standards they need (1-3 files max)
|
|
6
|
-
* - A single focused mission
|
|
7
|
-
* - Minimal context overhead (~2-5K tokens vs 20-50K for full agent)
|
|
8
|
-
*
|
|
9
|
-
* Saved to: .morph/micro-agents/{name}.json
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
13
|
-
import { join } from 'path';
|
|
14
|
-
|
|
15
|
-
const MICRO_AGENTS_DIR = join(process.cwd(), '.morph/micro-agents');
|
|
16
|
-
const AGENTS_CONFIG = join(process.cwd(), '.morph/framework/agents.json');
|
|
17
|
-
|
|
18
|
-
// ============================================================================
|
|
19
|
-
// Agent Creation
|
|
20
|
-
// ============================================================================
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Create a micro-agent from a base agent with a standards subset
|
|
24
|
-
* @param {Object} opts
|
|
25
|
-
* @param {string} opts.name - Micro-agent name (e.g., 'jwt-validator')
|
|
26
|
-
* @param {string} opts.baseAgent - Base agent ID (e.g., 'security-expert')
|
|
27
|
-
* @param {string[]} opts.standards - Relevant standards file paths (1-3 files)
|
|
28
|
-
* @param {string} opts.mission - Focused mission description
|
|
29
|
-
* @param {string[]} [opts.tools] - Tools this micro-agent uses (default: minimal set)
|
|
30
|
-
* @param {Object} [opts.constraints] - Additional constraints
|
|
31
|
-
* @returns {Object} Micro-agent config object
|
|
32
|
-
*/
|
|
33
|
-
export function createMicroAgent({ name, baseAgent, standards, mission, tools = null, constraints = {} }) {
|
|
34
|
-
if (!name || !baseAgent || !mission) {
|
|
35
|
-
throw new Error('name, baseAgent, and mission are required');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (standards && standards.length > 5) {
|
|
39
|
-
throw new Error('Micro-agents should load at most 5 standards files (ideally 1-3)');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const baseAgentConfig = loadBaseAgent(baseAgent);
|
|
43
|
-
|
|
44
|
-
const microAgent = {
|
|
45
|
-
id: name,
|
|
46
|
-
type: 'micro-agent',
|
|
47
|
-
baseAgent,
|
|
48
|
-
createdAt: new Date().toISOString(),
|
|
49
|
-
mission,
|
|
50
|
-
tier: (baseAgentConfig?.tier || 3) + 1, // micro-agents are tier+1 (more specialized)
|
|
51
|
-
domain: baseAgentConfig?.domain || 'general',
|
|
52
|
-
standards: standards || [],
|
|
53
|
-
tools: tools || ['Read', 'Write', 'Edit'],
|
|
54
|
-
constraints: {
|
|
55
|
-
maxStandards: 3,
|
|
56
|
-
maxFiles: 10,
|
|
57
|
-
focusOnly: mission,
|
|
58
|
-
...constraints
|
|
59
|
-
},
|
|
60
|
-
contextEstimate: {
|
|
61
|
-
standards: (standards || []).length * 2000,
|
|
62
|
-
overhead: 1500,
|
|
63
|
-
total: ((standards || []).length * 2000) + 1500
|
|
64
|
-
},
|
|
65
|
-
prompt: generateMicroAgentPrompt({ name, baseAgent, standards, mission, constraints, baseAgentConfig })
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
return microAgent;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Save a micro-agent to disk
|
|
73
|
-
* @param {Object} microAgent - Micro-agent config (from createMicroAgent)
|
|
74
|
-
* @returns {string} Path to saved file
|
|
75
|
-
*/
|
|
76
|
-
export function saveMicroAgent(microAgent) {
|
|
77
|
-
if (!existsSync(MICRO_AGENTS_DIR)) {
|
|
78
|
-
mkdirSync(MICRO_AGENTS_DIR, { recursive: true });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const filePath = join(MICRO_AGENTS_DIR, `${microAgent.id}.json`);
|
|
82
|
-
writeFileSync(filePath, JSON.stringify(microAgent, null, 2), 'utf8');
|
|
83
|
-
return filePath;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* List all micro-agents
|
|
88
|
-
* @returns {Array} Array of micro-agent summaries
|
|
89
|
-
*/
|
|
90
|
-
export function listMicroAgents() {
|
|
91
|
-
if (!existsSync(MICRO_AGENTS_DIR)) return [];
|
|
92
|
-
|
|
93
|
-
return readdirSync(MICRO_AGENTS_DIR)
|
|
94
|
-
.filter(f => f.endsWith('.json'))
|
|
95
|
-
.map(f => {
|
|
96
|
-
try {
|
|
97
|
-
const agent = JSON.parse(readFileSync(join(MICRO_AGENTS_DIR, f), 'utf8'));
|
|
98
|
-
return {
|
|
99
|
-
id: agent.id,
|
|
100
|
-
baseAgent: agent.baseAgent,
|
|
101
|
-
mission: agent.mission,
|
|
102
|
-
standards: agent.standards?.length || 0,
|
|
103
|
-
contextEstimate: agent.contextEstimate?.total || 0,
|
|
104
|
-
createdAt: agent.createdAt
|
|
105
|
-
};
|
|
106
|
-
} catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
.filter(Boolean);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Get a specific micro-agent
|
|
115
|
-
* @param {string} name - Micro-agent name
|
|
116
|
-
* @returns {Object|null} Micro-agent config or null
|
|
117
|
-
*/
|
|
118
|
-
export function getMicroAgent(name) {
|
|
119
|
-
const filePath = join(MICRO_AGENTS_DIR, `${name}.json`);
|
|
120
|
-
if (!existsSync(filePath)) return null;
|
|
121
|
-
try {
|
|
122
|
-
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
123
|
-
} catch {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ============================================================================
|
|
129
|
-
// Helpers
|
|
130
|
-
// ============================================================================
|
|
131
|
-
|
|
132
|
-
function loadBaseAgent(agentId) {
|
|
133
|
-
if (!existsSync(AGENTS_CONFIG)) return null;
|
|
134
|
-
try {
|
|
135
|
-
const config = JSON.parse(readFileSync(AGENTS_CONFIG, 'utf8'));
|
|
136
|
-
return config.agents?.find(a => a.id === agentId) || null;
|
|
137
|
-
} catch {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function generateMicroAgentPrompt({ name, baseAgent, standards, mission, constraints, baseAgentConfig }) {
|
|
143
|
-
const standardsList = standards?.map(s => `- framework/standards/${s}`).join('\n') || '(none)';
|
|
144
|
-
|
|
145
|
-
return `You are a MICRO-AGENT: ${name}
|
|
146
|
-
Base: ${baseAgent} (${baseAgentConfig?.description || 'specialist'})
|
|
147
|
-
|
|
148
|
-
MISSION (single focus):
|
|
149
|
-
${mission}
|
|
150
|
-
|
|
151
|
-
STANDARDS TO LOAD (${standards?.length || 0} files):
|
|
152
|
-
${standardsList}
|
|
153
|
-
|
|
154
|
-
CONSTRAINTS:
|
|
155
|
-
- Focus ONLY on your mission above
|
|
156
|
-
- Do NOT load additional standards or context
|
|
157
|
-
- Maximum ${constraints.maxFiles || 10} files to read/write
|
|
158
|
-
- Report completion when done — do NOT start new tasks
|
|
159
|
-
|
|
160
|
-
This is a minimal-context agent. Stay focused.`;
|
|
161
|
-
}
|