@polymorphism-tech/morph-spec 4.10.5 ā 4.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +3 -0
- package/README.md +3 -3
- package/bin/morph-spec.js +90 -1
- package/bin/task-manager.js +59 -6
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents/backend/hangfire-orchestrator.md +1 -1
- package/framework/agents/backend/ms-agent-expert.md +2 -1
- package/framework/agents/integrations/asaas-financial.md +1 -1
- package/framework/agents/integrations/clerk-auth.md +1 -2
- package/framework/agents/integrations/hangfire-integration.md +1 -1
- package/framework/agents/integrations/resend-email.md +1 -1
- package/framework/agents.json +345 -708
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +10 -5
- package/framework/skills/README.md +0 -1
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +42 -13
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +16 -12
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/package.json +1 -1
- 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 +9 -0
- package/src/commands/agents/manage-agents.js +352 -0
- package/src/commands/project/doctor.js +61 -2
- package/src/commands/project/init.js +10 -2
- package/src/commands/project/update.js +12 -25
- package/src/commands/state/advance-phase.js +11 -3
- 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/tasks/task-parser.js +94 -3
- package/src/lib/validators/checkpoint-persistence.js +39 -0
- package/src/lib/validators/content/content-validator.js +9 -1
- package/src/lib/validators/spec-validator.js +2 -2
- package/src/lib/validators/validation-runner.js +20 -0
- 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-settings-manager.js +1 -1
- package/src/utils/hooks-installer.js +0 -245
- package/src/utils/tier-helpers.js +24 -0
- package/framework/agents/architecture/po-pm-advisor.md +0 -203
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +0 -68
package/CLAUDE.md
ADDED
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Spec-driven development framework for multi-stack projects. Turns feature requests into implementation-ready code through structured, AI-orchestrated phases.
|
|
4
4
|
|
|
5
5
|
**Package:** `@polymorphism-tech/morph-spec`
|
|
6
|
-
**Version:** 4.10.
|
|
6
|
+
**Version:** 4.10.7
|
|
7
7
|
**Requires:** Node.js 18+, Claude Code
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -358,7 +358,7 @@ npm install -g @polymorphism-tech/morph-spec
|
|
|
358
358
|
|
|
359
359
|
### Hook not triggering on SessionStart
|
|
360
360
|
|
|
361
|
-
Ensure `.claude/settings.local.json` is not gitignored in your project. The file must be present for Claude Code to load hooks. If it is missing, re-run `morph-spec init --force`.
|
|
361
|
+
Ensure `.claude/settings.local.json` is not gitignored in your project. The file must be present for Claude Code to load hooks. If it is missing, re-run `morph-spec setup-infra` or `morph-spec init --force`.
|
|
362
362
|
|
|
363
363
|
---
|
|
364
364
|
|
|
@@ -376,4 +376,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
|
|
|
376
376
|
|
|
377
377
|
---
|
|
378
378
|
|
|
379
|
-
*morph-spec v4.10.
|
|
379
|
+
*morph-spec v4.10.7 by [Polymorphism Tech](https://polymorphism.tech)*
|
package/bin/morph-spec.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
@@ -26,6 +26,16 @@ import { phaseResetCommand } from '../src/commands/phase/phase-reset.js';
|
|
|
26
26
|
|
|
27
27
|
// Agent commands
|
|
28
28
|
import { dispatchAgentsCommand } from '../src/commands/agents/dispatch-agents.js';
|
|
29
|
+
import { auditDispatchCommand } from '../src/commands/agents/audit-dispatch.js';
|
|
30
|
+
import { detectAgentsCommand } from '../src/commands/agents/detect-agents.js';
|
|
31
|
+
import { agentMetricsCommand } from '../src/commands/agents/agent-metrics.js';
|
|
32
|
+
import {
|
|
33
|
+
agentListCommand,
|
|
34
|
+
agentShowCommand,
|
|
35
|
+
agentAddCommand,
|
|
36
|
+
agentEditCommand,
|
|
37
|
+
agentRemoveCommand,
|
|
38
|
+
} from '../src/commands/agents/manage-agents.js';
|
|
29
39
|
|
|
30
40
|
// Task commands
|
|
31
41
|
import { taskDoneCommand, taskStartCommand, taskNextCommand } from '../src/commands/tasks/task.js';
|
|
@@ -300,9 +310,33 @@ program
|
|
|
300
310
|
.description('Build dispatch config for parallel agent orchestration (design | tasks | implement)')
|
|
301
311
|
.option('--table', 'Human-readable table output instead of JSON')
|
|
302
312
|
.option('--mode <mode>', 'Dispatch mode: validate (includes Tier-4 validators as read-only)')
|
|
313
|
+
.option('--preflight', 'Exit with code 1 if any agent files are missing')
|
|
303
314
|
.option('-v, --verbose', 'Show stack trace on error')
|
|
304
315
|
.action((feature, phase, options) => dispatchAgentsCommand(feature, phase, options));
|
|
305
316
|
|
|
317
|
+
program
|
|
318
|
+
.command('audit-dispatch <feature>')
|
|
319
|
+
.description('Display dispatch audit log for a feature')
|
|
320
|
+
.option('--json', 'Output as JSON instead of table')
|
|
321
|
+
.action((feature, options) => auditDispatchCommand(feature, options));
|
|
322
|
+
|
|
323
|
+
program
|
|
324
|
+
.command('agent-metrics [feature]')
|
|
325
|
+
.description('Display agent performance metrics for a feature')
|
|
326
|
+
.option('--json', 'Output as JSON instead of table')
|
|
327
|
+
.option('--all', 'Aggregate metrics across all features')
|
|
328
|
+
.action((feature, options) => agentMetricsCommand(feature, options));
|
|
329
|
+
|
|
330
|
+
program
|
|
331
|
+
.command('detect-agents <feature>')
|
|
332
|
+
.description('Detect relevant agents for a feature based on proposal keywords')
|
|
333
|
+
.option('--proposal <path>', 'Path to proposal file (default: .morph/features/{feature}/0-proposal/proposal.md)')
|
|
334
|
+
.option('--json', 'Output as JSON (default)')
|
|
335
|
+
.option('--table', 'Human-readable table output')
|
|
336
|
+
.option('--exclude <ids>', 'Comma-separated agent IDs to exclude')
|
|
337
|
+
.option('--refresh', 'Refresh detection using all phase outputs (additive, mutates state)')
|
|
338
|
+
.action((feature, options) => detectAgentsCommand(feature, options));
|
|
339
|
+
|
|
306
340
|
// Scope escalation commands
|
|
307
341
|
const scopeCommand = program
|
|
308
342
|
.command('scope')
|
|
@@ -317,4 +351,59 @@ scopeCommand
|
|
|
317
351
|
.option('--dry-run', 'Show recommendation without executing')
|
|
318
352
|
.action((feature, options) => scopeEscalateCommand(feature, options));
|
|
319
353
|
|
|
354
|
+
// Agent management commands (CRUD)
|
|
355
|
+
const agentCommand = program
|
|
356
|
+
.command('agent')
|
|
357
|
+
.description('Manage agents in agents.json (list, show, add, edit, remove)');
|
|
358
|
+
|
|
359
|
+
agentCommand
|
|
360
|
+
.command('list')
|
|
361
|
+
.description('List all agents with optional filters')
|
|
362
|
+
.option('--group <name>', 'Filter by group')
|
|
363
|
+
.option('--tier <n>', 'Filter by tier (1-4)')
|
|
364
|
+
.option('--json', 'Output as JSON')
|
|
365
|
+
.action((options) => agentListCommand(options));
|
|
366
|
+
|
|
367
|
+
agentCommand
|
|
368
|
+
.command('show <id>')
|
|
369
|
+
.description('Show detailed info for an agent')
|
|
370
|
+
.option('--json', 'Output as JSON')
|
|
371
|
+
.option('--full', 'Show full spawn_prompt (no truncation)')
|
|
372
|
+
.action((id, options) => agentShowCommand(id, options));
|
|
373
|
+
|
|
374
|
+
agentCommand
|
|
375
|
+
.command('add <id>')
|
|
376
|
+
.description('Add a new agent via interactive wizard')
|
|
377
|
+
.option('--non-interactive', 'Use flags instead of prompts')
|
|
378
|
+
.option('--tier <n>', 'Tier (1-4)')
|
|
379
|
+
.option('--title <title>', 'Agent title')
|
|
380
|
+
.option('--group <group>', 'Agent group')
|
|
381
|
+
.option('--active-phases <phases>', 'Comma-separated phases or *')
|
|
382
|
+
.option('--reports-to <id>', 'Parent agent ID or "user"')
|
|
383
|
+
.option('--domains <domains>', 'Comma-separated domains')
|
|
384
|
+
.option('--keywords <keywords>', 'Comma-separated keywords')
|
|
385
|
+
.option('--spawn-prompt <prompt>', 'Teammate spawn prompt')
|
|
386
|
+
.option('--role <role>', 'Teammate role')
|
|
387
|
+
.option('--icon <emoji>', 'Teammate icon')
|
|
388
|
+
.action((id, options) => agentAddCommand(id, options));
|
|
389
|
+
|
|
390
|
+
agentCommand
|
|
391
|
+
.command('edit <id>')
|
|
392
|
+
.description('Edit agent fields via flags')
|
|
393
|
+
.option('--tier <n>', 'New tier')
|
|
394
|
+
.option('--title <title>', 'New title')
|
|
395
|
+
.option('--group <group>', 'New group')
|
|
396
|
+
.option('--active-phases <phases>', 'New phases (comma-sep)')
|
|
397
|
+
.option('--reports-to <id>', 'New reports_to')
|
|
398
|
+
.option('--keywords <keywords>', 'New keywords (comma-sep)')
|
|
399
|
+
.option('--domains <domains>', 'New domains (comma-sep)')
|
|
400
|
+
.option('--spawn-prompt <prompt>', 'New spawn prompt')
|
|
401
|
+
.action((id, options) => agentEditCommand(id, options));
|
|
402
|
+
|
|
403
|
+
agentCommand
|
|
404
|
+
.command('remove <id>')
|
|
405
|
+
.description('Remove an agent from agents.json')
|
|
406
|
+
.option('--force', 'Skip confirmation prompt')
|
|
407
|
+
.action((id, options) => agentRemoveCommand(id, options));
|
|
408
|
+
|
|
320
409
|
program.parse();
|
package/bin/task-manager.js
CHANGED
|
@@ -13,7 +13,7 @@ import { access } from 'fs/promises';
|
|
|
13
13
|
import { execSync } from 'child_process';
|
|
14
14
|
import chalk from 'chalk';
|
|
15
15
|
|
|
16
|
-
import { loadState, saveState } from '../src/core/state/state-manager.js';
|
|
16
|
+
import { loadState, saveState, resolveAgentForTask, updateDispatchAgentTiming } from '../src/core/state/state-manager.js';
|
|
17
17
|
import { parseTasksMd, ensureTaskList, syncCounters } from '../src/lib/tasks/task-parser.js';
|
|
18
18
|
import { isTestTask } from '../src/lib/tasks/task-classifier.js';
|
|
19
19
|
import { runTestSuite } from '../src/lib/tasks/test-runner.js';
|
|
@@ -124,6 +124,7 @@ class TaskManager {
|
|
|
124
124
|
// Persist initial taskList so counters are visible in `state get` even if
|
|
125
125
|
// validation fails later and the completion save never runs.
|
|
126
126
|
syncCounters(feature);
|
|
127
|
+
feature.updatedAt = new Date().toISOString();
|
|
127
128
|
saveState(state);
|
|
128
129
|
|
|
129
130
|
const results = [];
|
|
@@ -160,7 +161,7 @@ class TaskManager {
|
|
|
160
161
|
|
|
161
162
|
// Run validation BEFORE marking tasks as complete
|
|
162
163
|
if (tasksToComplete.length > 0 && !options.skipValidation) {
|
|
163
|
-
const validationResult = await this.runValidation(featureName);
|
|
164
|
+
const validationResult = await this.runValidation(featureName, { mode: 'light' });
|
|
164
165
|
if (options.dryRun) {
|
|
165
166
|
console.log(chalk.cyan('\n ā¹ļø Dry-run ā tasks NOT marked as complete'));
|
|
166
167
|
return [];
|
|
@@ -240,10 +241,14 @@ class TaskManager {
|
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
// Mark tasks as completed (only after validation passes)
|
|
244
|
+
// Mark tasks as completed with agent correlation
|
|
243
245
|
for (const task of tasksToComplete) {
|
|
244
246
|
task.status = 'completed';
|
|
245
247
|
task.completedAt = new Date().toISOString();
|
|
246
|
-
|
|
248
|
+
|
|
249
|
+
// Resolve agent from dispatch (falls back to 'claude')
|
|
250
|
+
const agentId = resolveAgentForTask(featureName, task.id);
|
|
251
|
+
task.completedBy = agentId || 'claude';
|
|
247
252
|
|
|
248
253
|
results.push(task);
|
|
249
254
|
|
|
@@ -255,6 +260,25 @@ class TaskManager {
|
|
|
255
260
|
}
|
|
256
261
|
}
|
|
257
262
|
|
|
263
|
+
// Update dispatch agent timing for completed tasks
|
|
264
|
+
const agentCompletions = new Map();
|
|
265
|
+
for (const task of results) {
|
|
266
|
+
const aid = task.completedBy;
|
|
267
|
+
if (aid && aid !== 'claude') {
|
|
268
|
+
if (!agentCompletions.has(aid)) agentCompletions.set(aid, []);
|
|
269
|
+
agentCompletions.get(aid).push(task.id);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
for (const [aid, taskIds] of agentCompletions) {
|
|
273
|
+
const lastDispatch = feature.dispatchLog?.[feature.dispatchLog.length - 1];
|
|
274
|
+
const dispatchAgent = lastDispatch?.agents?.find(a => a.id === aid);
|
|
275
|
+
const existing = dispatchAgent?.tasksCompleted || [];
|
|
276
|
+
updateDispatchAgentTiming(featureName, aid, {
|
|
277
|
+
completedAt: new Date().toISOString(),
|
|
278
|
+
tasksCompleted: [...new Set([...existing, ...taskIds])]
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
258
282
|
// Sync v3 counters then compute progress
|
|
259
283
|
syncCounters(feature);
|
|
260
284
|
feature.progress = this.calculateProgress(taskList);
|
|
@@ -273,6 +297,7 @@ class TaskManager {
|
|
|
273
297
|
}
|
|
274
298
|
|
|
275
299
|
// Persist state (atomic write via state-manager)
|
|
300
|
+
feature.updatedAt = new Date().toISOString();
|
|
276
301
|
saveState(state);
|
|
277
302
|
|
|
278
303
|
// Run TaskCompleted agent-teams hook for each completed task (non-blocking)
|
|
@@ -335,12 +360,12 @@ class TaskManager {
|
|
|
335
360
|
* @param {string} featureName - Feature name
|
|
336
361
|
* @returns {{ passed: boolean, validators: Object, passRate: number }}
|
|
337
362
|
*/
|
|
338
|
-
async runValidation(featureName) {
|
|
363
|
+
async runValidation(featureName, opts = {}) {
|
|
339
364
|
try {
|
|
340
365
|
console.log(chalk.cyan('\nš Running code validation...'));
|
|
341
366
|
|
|
342
367
|
const { runValidation, formatValidationResults } = await import('../src/lib/validators/validation-runner.js');
|
|
343
|
-
const result = await runValidation('.', featureName, { verbose: true });
|
|
368
|
+
const result = await runValidation('.', featureName, { verbose: true, ...opts });
|
|
344
369
|
|
|
345
370
|
formatValidationResults(result);
|
|
346
371
|
|
|
@@ -498,10 +523,24 @@ class TaskManager {
|
|
|
498
523
|
console.log(chalk.yellow('ā ļø Checkpoint hooks not available, using legacy validation'));
|
|
499
524
|
try {
|
|
500
525
|
const { runValidation } = await import('../src/lib/validators/validation-runner.js');
|
|
501
|
-
const result = await runValidation('.', featureName, { verbose: false });
|
|
526
|
+
const result = await runValidation('.', featureName, { verbose: false, mode: 'full' });
|
|
502
527
|
validationNote = result.passed
|
|
503
528
|
? ' | Validation: PASSED'
|
|
504
529
|
: ` | Validation: ${result.errors} errors, ${result.warnings} warnings`;
|
|
530
|
+
|
|
531
|
+
// Persist checkpoint validation results to disk
|
|
532
|
+
try {
|
|
533
|
+
const { persistCheckpointValidation } = await import('../src/lib/validators/checkpoint-persistence.js');
|
|
534
|
+
persistCheckpointValidation(process.cwd(), featureName, {
|
|
535
|
+
checkpointNum,
|
|
536
|
+
timestamp: new Date().toISOString(),
|
|
537
|
+
passed: result.passed,
|
|
538
|
+
validators: result.results || {},
|
|
539
|
+
summary: result.summary || '',
|
|
540
|
+
});
|
|
541
|
+
} catch {
|
|
542
|
+
// Non-blocking
|
|
543
|
+
}
|
|
505
544
|
} catch {
|
|
506
545
|
// No validation available
|
|
507
546
|
}
|
|
@@ -683,7 +722,21 @@ class TaskManager {
|
|
|
683
722
|
|
|
684
723
|
task.status = 'in_progress';
|
|
685
724
|
task.startedAt = new Date().toISOString();
|
|
725
|
+
|
|
726
|
+
// Agent correlation: resolve which agent is assigned this task from the latest dispatch
|
|
727
|
+
const agentId = resolveAgentForTask(featureName, taskId);
|
|
728
|
+
if (agentId) {
|
|
729
|
+
task.assignedAgent = agentId;
|
|
730
|
+
// Record agent startedAt only for the first task (don't overwrite)
|
|
731
|
+
const lastDispatch = feature.dispatchLog?.[feature.dispatchLog.length - 1];
|
|
732
|
+
const dispatchAgent = lastDispatch?.agents?.find(a => a.id === agentId);
|
|
733
|
+
if (dispatchAgent && !dispatchAgent.startedAt) {
|
|
734
|
+
updateDispatchAgentTiming(featureName, agentId, { startedAt: task.startedAt });
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
686
738
|
syncCounters(feature);
|
|
739
|
+
feature.updatedAt = new Date().toISOString();
|
|
687
740
|
|
|
688
741
|
saveState(state);
|
|
689
742
|
|
package/claude-plugin.json
CHANGED
package/docs/CHEATSHEET.md
CHANGED
package/docs/QUICKSTART.md
CHANGED
|
@@ -3,7 +3,7 @@ name: hangfire-orchestrator
|
|
|
3
3
|
description: Hangfire Orchestrator specialist for background job design, recurring task scheduling, queue management, and idempotent job patterns in .NET. Use when implementing background jobs, scheduling recurring tasks, managing job queues, or integrating Hangfire with .NET services.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
5
|
stacks:
|
|
6
|
-
-
|
|
6
|
+
- hangfire
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Hangfire Orchestrator
|
|
@@ -3,7 +3,8 @@ name: ms-agent-expert
|
|
|
3
3
|
description: Microsoft Agent Framework Expert for Semantic Kernel, AI extensions, function calling, MCP/A2A protocols, and agentic pipeline implementation. Use when building AI agents with Semantic Kernel, implementing tool/plugin calling, designing multi-agent workflows, or integrating LLMs with .NET services.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
5
|
stacks:
|
|
6
|
-
-
|
|
6
|
+
- semantic-kernel
|
|
7
|
+
- ms-agents
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
# Agent Framework Expert
|
|
@@ -3,7 +3,7 @@ name: asaas-financial
|
|
|
3
3
|
description: Asaas Financial specialist for Asaas API integration, payment processing, Pix/boleto billing, and subscription management in Brazilian projects. Use when integrating Asaas payments, implementing billing flows, handling payment webhooks, or building subscription management features.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
5
|
stacks:
|
|
6
|
-
-
|
|
6
|
+
- asaas
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Asaas Financial
|
|
@@ -3,8 +3,7 @@ name: clerk-auth
|
|
|
3
3
|
description: Clerk Auth specialist for Clerk SDK integration, social login, JWT session management, and user management in Next.js and .NET projects. Use when integrating Clerk authentication, implementing social login flows, managing user sessions with Clerk JWT, or protecting routes with Clerk middleware.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
5
|
stacks:
|
|
6
|
-
-
|
|
7
|
-
- nextjs
|
|
6
|
+
- clerk
|
|
8
7
|
---
|
|
9
8
|
|
|
10
9
|
# Clerk Auth
|
|
@@ -3,7 +3,7 @@ name: hangfire-orchestrator-integration
|
|
|
3
3
|
description: Hangfire integration specialist for connecting background jobs with external services, implementing webhook handlers, and building event-driven workflows in .NET. Use when integrating Hangfire with third-party APIs, processing webhooks asynchronously, or designing event-driven job pipelines.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
5
|
stacks:
|
|
6
|
-
-
|
|
6
|
+
- hangfire
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Hangfire Orchestrator
|
|
@@ -3,7 +3,7 @@ name: resend-email
|
|
|
3
3
|
description: Resend Email specialist for transactional email, HTML/React email templates, Resend API integration, and notification systems in .NET and Next.js. Use when implementing email notifications, integrating Resend API, creating transactional email templates, or configuring email delivery in MORPH-SPEC projects.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
5
|
stacks:
|
|
6
|
-
-
|
|
6
|
+
- resend
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Resend Email
|