forge-workflow 0.0.2 → 0.0.4
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/commands/dev.md +26 -0
- package/.claude/commands/plan.md +141 -9
- package/.claude/commands/premerge.md +0 -3
- package/.claude/commands/rollback.md +4 -4
- package/.claude/commands/ship.md +71 -41
- package/.claude/commands/status.md +9 -38
- package/.claude/commands/validate.md +47 -2
- package/.cline/workflows/dev.md +337 -0
- package/.cline/workflows/plan.md +518 -0
- package/.cline/workflows/premerge.md +173 -0
- package/.cline/workflows/research.md +39 -0
- package/.cline/workflows/review.md +439 -0
- package/.cline/workflows/rollback.md +718 -0
- package/.cline/workflows/ship.md +161 -0
- package/.cline/workflows/sonarcloud.md +146 -0
- package/.cline/workflows/status.md +45 -0
- package/.cline/workflows/validate.md +279 -0
- package/.cline/workflows/verify.md +218 -0
- package/.codex/config.toml +11 -0
- package/.codex/skills/dev/SKILL.md +340 -0
- package/.codex/skills/plan/SKILL.md +521 -0
- package/.codex/skills/premerge/SKILL.md +176 -0
- package/.codex/skills/research/SKILL.md +42 -0
- package/.codex/skills/review/SKILL.md +442 -0
- package/.codex/skills/rollback/SKILL.md +721 -0
- package/.codex/skills/ship/SKILL.md +164 -0
- package/.codex/skills/sonarcloud/SKILL.md +149 -0
- package/.codex/skills/status/SKILL.md +48 -0
- package/.codex/skills/validate/SKILL.md +282 -0
- package/.codex/skills/verify/SKILL.md +221 -0
- package/.cursor/commands/dev.md +337 -0
- package/.cursor/commands/plan.md +518 -0
- package/.cursor/commands/premerge.md +173 -0
- package/.cursor/commands/research.md +39 -0
- package/.cursor/commands/review.md +439 -0
- package/.cursor/commands/rollback.md +718 -0
- package/.cursor/commands/ship.md +161 -0
- package/.cursor/commands/sonarcloud.md +146 -0
- package/.cursor/commands/status.md +45 -0
- package/.cursor/commands/validate.md +279 -0
- package/.cursor/commands/verify.md +218 -0
- package/.cursor/hooks/state/continual-learning-index.json +19 -0
- package/.cursor/hooks/state/continual-learning.json +8 -0
- package/.cursor/rules/permissions-guidance.mdc +37 -0
- package/.github/prompts/dev.prompt.md +342 -0
- package/.github/prompts/plan.prompt.md +523 -0
- package/.github/prompts/premerge.prompt.md +178 -0
- package/.github/prompts/research.prompt.md +44 -0
- package/.github/prompts/review.prompt.md +444 -0
- package/.github/prompts/rollback.prompt.md +723 -0
- package/.github/prompts/ship.prompt.md +166 -0
- package/.github/prompts/sonarcloud.prompt.md +151 -0
- package/.github/prompts/status.prompt.md +50 -0
- package/.github/prompts/validate.prompt.md +284 -0
- package/.github/prompts/verify.prompt.md +223 -0
- package/.kilocode/workflows/dev.md +341 -0
- package/.kilocode/workflows/plan.md +522 -0
- package/.kilocode/workflows/premerge.md +177 -0
- package/.kilocode/workflows/research.md +43 -0
- package/.kilocode/workflows/review.md +443 -0
- package/.kilocode/workflows/rollback.md +722 -0
- package/.kilocode/workflows/ship.md +165 -0
- package/.kilocode/workflows/sonarcloud.md +150 -0
- package/.kilocode/workflows/status.md +49 -0
- package/.kilocode/workflows/validate.md +283 -0
- package/.kilocode/workflows/verify.md +222 -0
- package/.opencode/commands/dev.md +340 -0
- package/.opencode/commands/plan.md +521 -0
- package/.opencode/commands/premerge.md +176 -0
- package/.opencode/commands/research.md +42 -0
- package/.opencode/commands/review.md +442 -0
- package/.opencode/commands/rollback.md +721 -0
- package/.opencode/commands/ship.md +164 -0
- package/.opencode/commands/sonarcloud.md +149 -0
- package/.opencode/commands/status.md +48 -0
- package/.opencode/commands/validate.md +282 -0
- package/.opencode/commands/verify.md +221 -0
- package/.roo/commands/dev.md +341 -0
- package/.roo/commands/plan.md +522 -0
- package/.roo/commands/premerge.md +177 -0
- package/.roo/commands/research.md +43 -0
- package/.roo/commands/review.md +443 -0
- package/.roo/commands/rollback.md +722 -0
- package/.roo/commands/ship.md +165 -0
- package/.roo/commands/sonarcloud.md +150 -0
- package/.roo/commands/status.md +49 -0
- package/.roo/commands/validate.md +283 -0
- package/.roo/commands/verify.md +222 -0
- package/AGENTS.md +7 -1
- package/CLAUDE.md +5 -4
- package/README.md +21 -19
- package/bin/{forge-validate.js → forge-preflight.js} +21 -15
- package/bin/forge.js +209 -138
- package/docs/AGENT_INSTALL_PROMPT.md +1 -1
- package/docs/BEADS_GITHUB_SYNC.md +251 -0
- package/docs/ENHANCED_ONBOARDING.md +8 -8
- package/docs/EXAMPLES.md +4 -4
- package/docs/GREPTILE_SETUP.md +1 -1
- package/docs/MANUAL_REVIEW_GUIDE.md +1 -1
- package/docs/ROADMAP.md +6 -6
- package/docs/SETUP.md +1 -2
- package/docs/TOOLCHAIN.md +15 -234
- package/docs/VALIDATION.md +11 -11
- package/install.sh +33 -39
- package/lib/agents-config.js +3 -3
- package/lib/commands/plan.js +11 -15
- package/lib/commands/recommend.js +2 -2
- package/lib/dep-guard/analyzer.js +294 -0
- package/lib/dep-guard/behavior-detector.js +98 -0
- package/lib/dep-guard/contract-detector.js +162 -0
- package/lib/dep-guard/import-detector.js +498 -0
- package/lib/dep-guard/path-utils.js +13 -0
- package/lib/dep-guard/rubric.js +120 -0
- package/lib/dep-guard/task-parser.js +318 -0
- package/lib/detect-agent.js +191 -0
- package/lib/detect-worktree.js +47 -0
- package/lib/file-hash.js +26 -0
- package/lib/plugin-catalog.js +18 -28
- package/lib/setup-action-log.js +139 -0
- package/lib/setup-summary-renderer.js +106 -0
- package/lib/setup.js +75 -1
- package/lib/workflow-profiles.js +5 -11
- package/package.json +17 -7
- package/skills/parallel-deep-research/SKILL.md +108 -0
- package/skills/parallel-deep-research/evals/README.md +27 -0
- package/skills/parallel-deep-research/evals/evals.json +62 -0
- package/skills/sonarcloud-analysis/SKILL.md +171 -0
- package/skills/sonarcloud-analysis/evals/README.md +27 -0
- package/skills/sonarcloud-analysis/evals/evals.json +50 -0
- package/skills/sonarcloud-analysis/references/api-reference.md +466 -0
- package/docs/WORKFLOW.md +0 -400
- package/docs/planning/PROGRESS.md +0 -396
- package/docs/plans/.gitkeep +0 -0
- package/docs/plans/2026-02-27-forge-test-suite-v2-decisions.md +0 -21
- package/docs/plans/2026-02-27-forge-test-suite-v2-design.md +0 -362
- package/docs/plans/2026-02-27-forge-test-suite-v2-tasks.md +0 -343
- package/docs/plans/2026-03-02-superpowers-gaps-decisions.md +0 -26
- package/docs/plans/2026-03-02-superpowers-gaps-design.md +0 -239
- package/docs/plans/2026-03-02-superpowers-gaps-tasks.md +0 -260
- package/docs/plans/2026-03-04-agent-command-parity-design.md +0 -163
- package/docs/plans/2026-03-04-verify-worktree-cleanup-decisions.md +0 -7
- package/docs/plans/2026-03-04-verify-worktree-cleanup-design.md +0 -165
- package/docs/plans/2026-03-05-forge-uto-decisions.md +0 -6
- package/docs/plans/2026-03-05-forge-uto-design.md +0 -116
- package/docs/plans/2026-03-05-forge-uto-tasks.md +0 -244
- package/docs/plans/2026-03-10-command-creator-and-eval-decisions.md +0 -52
- package/docs/plans/2026-03-10-command-creator-and-eval-design.md +0 -350
- package/docs/plans/2026-03-10-command-creator-and-eval-tasks.md +0 -426
- package/docs/plans/2026-03-10-stale-workflow-refs-decisions.md +0 -8
- package/docs/plans/2026-03-10-stale-workflow-refs-design.md +0 -80
- package/docs/plans/2026-03-10-stale-workflow-refs-tasks.md +0 -90
- package/docs/plans/2026-03-14-beads-plan-context-decisions.md +0 -9
- package/docs/plans/2026-03-14-beads-plan-context-design.md +0 -171
- package/docs/plans/2026-03-14-beads-plan-context-tasks.md +0 -160
- package/docs/plans/2026-03-14-skill-eval-loop-decisions.md +0 -33
- package/docs/plans/2026-03-14-skill-eval-loop-design.md +0 -118
- package/docs/plans/2026-03-14-skill-eval-loop-results.md +0 -78
- package/docs/plans/2026-03-14-skill-eval-loop-tasks.md +0 -160
- package/docs/plans/2026-03-15-agent-command-parity-v2-decisions.md +0 -11
- package/docs/plans/2026-03-15-agent-command-parity-v2-design.md +0 -145
- package/docs/plans/2026-03-15-agent-command-parity-v2-tasks.md +0 -211
- package/docs/research/TEMPLATE.md +0 -292
- package/docs/research/advanced-testing.md +0 -297
- package/docs/research/agent-permissions.md +0 -167
- package/docs/research/dependency-chain.md +0 -328
- package/docs/research/forge-workflow-v2.md +0 -550
- package/docs/research/plugin-architecture.md +0 -772
- package/docs/research/pr4-cli-automation.md +0 -326
- package/docs/research/premerge-verify-restructure.md +0 -205
- package/docs/research/skills-restructure.md +0 -508
- package/docs/research/sonarcloud-perfection-plan.md +0 -166
- package/docs/research/sonarcloud-quality-gate.md +0 -184
- package/docs/research/superpowers-integration.md +0 -403
- package/docs/research/superpowers.md +0 -319
- package/docs/research/test-environment.md +0 -519
package/bin/forge.js
CHANGED
|
@@ -47,10 +47,17 @@ const VERSION = packageJson.version;
|
|
|
47
47
|
|
|
48
48
|
// Load PluginManager for discoverable agent architecture
|
|
49
49
|
const PluginManager = require('../lib/plugin-manager');
|
|
50
|
+
const { scaffoldGithubBeadsSync } = require('../lib/setup');
|
|
50
51
|
|
|
51
52
|
// Load enhanced onboarding modules
|
|
52
53
|
const contextMerge = require(path.join(packageDir, 'lib', 'context-merge'));
|
|
53
54
|
const projectDiscovery = require(path.join(packageDir, 'lib', 'project-discovery'));
|
|
55
|
+
|
|
56
|
+
// Load incremental setup modules
|
|
57
|
+
const { detectEnvironment } = require('../lib/detect-agent');
|
|
58
|
+
const { fileMatchesContent } = require('../lib/file-hash');
|
|
59
|
+
const { SetupActionLog } = require('../lib/setup-action-log');
|
|
60
|
+
const { renderSetupSummary } = require('../lib/setup-summary-renderer');
|
|
54
61
|
// workflowProfiles is loaded but not currently used in the setup flow
|
|
55
62
|
// const _workflowProfiles = require(path.join(packageDir, 'lib', 'workflow-profiles'));
|
|
56
63
|
|
|
@@ -58,6 +65,11 @@ const projectDiscovery = require(path.join(packageDir, 'lib', 'project-discovery
|
|
|
58
65
|
let projectRoot = process.env.INIT_CWD || process.cwd();
|
|
59
66
|
const args = process.argv.slice(2);
|
|
60
67
|
|
|
68
|
+
// Incremental setup state (set during main() from parsed flags)
|
|
69
|
+
let FORCE_MODE = false;
|
|
70
|
+
let VERBOSE_MODE = false;
|
|
71
|
+
let actionLog = new SetupActionLog();
|
|
72
|
+
|
|
61
73
|
// Detected package manager
|
|
62
74
|
let PKG_MANAGER = 'npm';
|
|
63
75
|
|
|
@@ -251,49 +263,26 @@ function _checkWritePermission(filePath) {
|
|
|
251
263
|
}
|
|
252
264
|
}
|
|
253
265
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
// Code quality tool options (reserved for future feature)
|
|
277
|
-
const _CODE_QUALITY_TOOLS = {
|
|
278
|
-
'eslint': {
|
|
279
|
-
name: 'ESLint only',
|
|
280
|
-
description: 'FREE, built-in - No external server required',
|
|
281
|
-
recommended: true
|
|
282
|
-
},
|
|
283
|
-
'sonarcloud': {
|
|
284
|
-
name: 'SonarCloud',
|
|
285
|
-
description: '50k LoC free, cloud-hosted',
|
|
286
|
-
requiresApiKey: true,
|
|
287
|
-
envVars: ['SONAR_TOKEN', 'SONAR_ORGANIZATION', 'SONAR_PROJECT_KEY'],
|
|
288
|
-
getKeyUrl: 'https://sonarcloud.io/account/security'
|
|
289
|
-
},
|
|
290
|
-
'sonarqube': {
|
|
291
|
-
name: 'SonarQube Community',
|
|
292
|
-
description: 'FREE, self-hosted, unlimited LoC',
|
|
293
|
-
envVars: ['SONARQUBE_URL', 'SONARQUBE_TOKEN'],
|
|
294
|
-
dockerCommand: 'docker run -d --name sonarqube -p 9000:9000 sonarqube:community'
|
|
295
|
-
}
|
|
296
|
-
};
|
|
266
|
+
/**
|
|
267
|
+
* Reads workflow command names from .claude/commands/*.md in the package directory.
|
|
268
|
+
* @returns {string[]} Command names (filenames without .md extension)
|
|
269
|
+
*/
|
|
270
|
+
function getWorkflowCommands() {
|
|
271
|
+
const commandsDir = path.join(packageDir, '.claude', 'commands');
|
|
272
|
+
try {
|
|
273
|
+
return fs.readdirSync(commandsDir)
|
|
274
|
+
.filter(f => f.endsWith('.md'))
|
|
275
|
+
.map(f => f.replace(/\.md$/, ''));
|
|
276
|
+
} catch (err) {
|
|
277
|
+
if (err.code === 'ENOENT') {
|
|
278
|
+
console.warn(`Warning: .claude/commands directory not found at ${commandsDir}`);
|
|
279
|
+
} else {
|
|
280
|
+
console.warn(`Warning: failed to read .claude/commands — ${err.code}: ${err.message}`);
|
|
281
|
+
}
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
297
286
|
|
|
298
287
|
// Helper function to safely execute commands (no user input)
|
|
299
288
|
function safeExec(cmd) {
|
|
@@ -469,24 +458,23 @@ alwaysApply: true
|
|
|
469
458
|
|
|
470
459
|
Use these commands via \`/command-name\`:
|
|
471
460
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
9. \`/verify\` - Final documentation verification
|
|
461
|
+
- \`/status\` (utility) - Check current context, active work, recent completions
|
|
462
|
+
1. \`/plan\` - Design intent Q&A, research, branch + task list
|
|
463
|
+
2. \`/dev\` - Subagent-driven TDD per task (spec + quality review)
|
|
464
|
+
3. \`/validate\` - Type check, lint, security, tests (HARD-GATE)
|
|
465
|
+
4. \`/ship\` - Push and create PR with design doc reference
|
|
466
|
+
5. \`/review\` - Handle ALL PR issues (Actions, Greptile, SonarCloud)
|
|
467
|
+
6. \`/premerge\` - Complete docs on feature branch, hand off PR to user
|
|
468
|
+
7. \`/verify\` - Post-merge health check (CI on main, close Beads)
|
|
481
469
|
|
|
482
470
|
See AGENTS.md for full workflow details.
|
|
483
471
|
`;
|
|
484
472
|
|
|
485
473
|
// Helper functions
|
|
486
|
-
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
487
474
|
|
|
488
475
|
function ensureDir(dir) {
|
|
489
476
|
const fullPath = path.resolve(projectRoot, dir);
|
|
477
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
490
478
|
|
|
491
479
|
// SECURITY: Prevent path traversal
|
|
492
480
|
if (!fullPath.startsWith(resolvedProjectRoot)) {
|
|
@@ -503,6 +491,7 @@ function ensureDir(dir) {
|
|
|
503
491
|
function writeFile(filePath, content) {
|
|
504
492
|
try {
|
|
505
493
|
const fullPath = path.resolve(projectRoot, filePath);
|
|
494
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
506
495
|
|
|
507
496
|
// SECURITY: Prevent path traversal
|
|
508
497
|
if (!fullPath.startsWith(resolvedProjectRoot)) {
|
|
@@ -536,6 +525,7 @@ function readFile(filePath) {
|
|
|
536
525
|
function copyFile(src, dest) {
|
|
537
526
|
try {
|
|
538
527
|
const destPath = path.resolve(projectRoot, dest);
|
|
528
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
539
529
|
|
|
540
530
|
// SECURITY: Prevent path traversal
|
|
541
531
|
if (!destPath.startsWith(resolvedProjectRoot)) {
|
|
@@ -544,13 +534,25 @@ function copyFile(src, dest) {
|
|
|
544
534
|
}
|
|
545
535
|
|
|
546
536
|
if (fs.existsSync(src)) {
|
|
537
|
+
// Content-hash comparison: skip if destination already matches source
|
|
538
|
+
if (!FORCE_MODE) {
|
|
539
|
+
const sourceContent = fs.readFileSync(src, 'utf8');
|
|
540
|
+
if (fileMatchesContent(destPath, sourceContent)) {
|
|
541
|
+
actionLog.add(dest, 'skipped', 'identical content');
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
547
546
|
const destDir = path.dirname(destPath);
|
|
548
547
|
if (!fs.existsSync(destDir)) {
|
|
549
548
|
fs.mkdirSync(destDir, { recursive: true });
|
|
550
549
|
}
|
|
550
|
+
const isNew = !fs.existsSync(destPath);
|
|
551
551
|
fs.copyFileSync(src, destPath);
|
|
552
|
+
const action = FORCE_MODE ? 'force-created' : (isNew ? 'created' : 'updated');
|
|
553
|
+
actionLog.add(dest, action);
|
|
552
554
|
return true;
|
|
553
|
-
} else
|
|
555
|
+
} else {
|
|
554
556
|
console.warn(` ⚠ Source file not found: ${src}`);
|
|
555
557
|
}
|
|
556
558
|
} catch (err) {
|
|
@@ -766,7 +768,7 @@ function smartMergeAgentsMd(existingContent, newContent) {
|
|
|
766
768
|
merged += forgeSection + '\n\n';
|
|
767
769
|
|
|
768
770
|
// Add footer
|
|
769
|
-
merged += `---\n\n## 💡 Improving This Workflow\n\nEvery time you give the same instruction twice, add it to this file:\n1. User-specific rules → Add to USER:START section above\n2. Forge workflow improvements → Suggest to forge maintainers\n\n**Keep this file updated as you learn about the project.**\n\n---\n\nSee \`
|
|
771
|
+
merged += `---\n\n## 💡 Improving This Workflow\n\nEvery time you give the same instruction twice, add it to this file:\n1. User-specific rules → Add to USER:START section above\n2. Forge workflow improvements → Suggest to forge maintainers\n\n**Keep this file updated as you learn about the project.**\n\n---\n\nSee \`AGENTS.md\` for complete workflow guide.\nSee \`docs/TOOLCHAIN.md\` for comprehensive tool reference.\n`;
|
|
770
772
|
|
|
771
773
|
return merged;
|
|
772
774
|
}
|
|
@@ -799,7 +801,6 @@ async function detectProjectStatus() {
|
|
|
799
801
|
hasClaudeMd: fs.existsSync(path.join(projectRoot, 'CLAUDE.md')),
|
|
800
802
|
hasClaudeCommands: fs.existsSync(path.join(projectRoot, '.claude/commands')),
|
|
801
803
|
hasEnvLocal: fs.existsSync(path.join(projectRoot, '.env.local')),
|
|
802
|
-
hasDocsWorkflow: fs.existsSync(path.join(projectRoot, 'docs/WORKFLOW.md')),
|
|
803
804
|
existingEnvVars: {},
|
|
804
805
|
agentsMdSize: 0,
|
|
805
806
|
claudeMdSize: 0,
|
|
@@ -832,7 +833,7 @@ async function detectProjectStatus() {
|
|
|
832
833
|
}
|
|
833
834
|
|
|
834
835
|
// Determine installation type
|
|
835
|
-
if (status.hasAgentsMd && status.hasClaudeCommands
|
|
836
|
+
if (status.hasAgentsMd && status.hasClaudeCommands) {
|
|
836
837
|
status.type = 'upgrade'; // Full forge installation exists
|
|
837
838
|
} else if (status.hasClaudeCommands || status.hasEnvLocal) {
|
|
838
839
|
status.type = 'partial'; // Agent-specific files exist (not just base files from postinstall)
|
|
@@ -1764,6 +1765,23 @@ async function configureExternalServices(rl, question, selectedAgents = [], proj
|
|
|
1764
1765
|
// Write all tokens to .env.local (preserving existing values)
|
|
1765
1766
|
const { added, preserved } = writeEnvTokens(tokens, true);
|
|
1766
1767
|
displayEnvTokenResults(added, preserved);
|
|
1768
|
+
|
|
1769
|
+
// GitHub-Beads issue sync setup
|
|
1770
|
+
console.log('');
|
|
1771
|
+
const enableSync = await askYesNo(question, 'Enable GitHub ↔ Beads issue sync?', true);
|
|
1772
|
+
if (enableSync) {
|
|
1773
|
+
try {
|
|
1774
|
+
const result = await scaffoldGithubBeadsSync(projectRoot, packageDir);
|
|
1775
|
+
for (const f of result.created) {
|
|
1776
|
+
console.log(` Created: ${f}`);
|
|
1777
|
+
}
|
|
1778
|
+
for (const f of result.skipped) {
|
|
1779
|
+
console.log(` Skipped: ${f} (already exists)`);
|
|
1780
|
+
}
|
|
1781
|
+
} catch (err) {
|
|
1782
|
+
console.error(` Error scaffolding GitHub-Beads sync: ${err.message}`);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1767
1785
|
}
|
|
1768
1786
|
|
|
1769
1787
|
// Display the Forge banner
|
|
@@ -1782,40 +1800,28 @@ function showBanner(subtitle = 'Universal AI Agent Workflow') {
|
|
|
1782
1800
|
}
|
|
1783
1801
|
}
|
|
1784
1802
|
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
console.log(' Created: docs/WORKFLOW.md');
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
// Copy research TEMPLATE.md
|
|
1798
|
-
const templateSrc = path.join(packageDir, 'docs/research/TEMPLATE.md');
|
|
1799
|
-
if (copyFile(templateSrc, 'docs/research/TEMPLATE.md')) {
|
|
1800
|
-
console.log(' Created: docs/research/TEMPLATE.md');
|
|
1803
|
+
/**
|
|
1804
|
+
* Creates a directory on first use and prints a one-time purpose note.
|
|
1805
|
+
* @param {string} dir - Absolute path to the directory to create.
|
|
1806
|
+
* @param {string} purpose - Human-readable purpose description.
|
|
1807
|
+
* @returns {string|null} Purpose message if created, null if already existed.
|
|
1808
|
+
*/
|
|
1809
|
+
function ensureDirWithNote(dir, purpose) {
|
|
1810
|
+
if (fs.existsSync(dir)) {
|
|
1811
|
+
return null;
|
|
1801
1812
|
}
|
|
1813
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1814
|
+
const display = dir.replace(/\\/g, '/');
|
|
1815
|
+
const msg = `Created ${display} for ${purpose}`;
|
|
1816
|
+
console.log(` ${msg}`);
|
|
1817
|
+
return msg;
|
|
1818
|
+
}
|
|
1802
1819
|
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
## Current Focus
|
|
1809
|
-
<!-- What you're working on -->
|
|
1810
|
-
|
|
1811
|
-
## Completed
|
|
1812
|
-
<!-- Completed features -->
|
|
1813
|
-
|
|
1814
|
-
## Upcoming
|
|
1815
|
-
<!-- Next priorities -->
|
|
1816
|
-
`);
|
|
1817
|
-
console.log(' Created: docs/planning/PROGRESS.md');
|
|
1818
|
-
}
|
|
1820
|
+
// Setup core documentation and directories
|
|
1821
|
+
function setupCoreDocs() {
|
|
1822
|
+
// docs/planning/ and docs/research/ are created lazily on first use
|
|
1823
|
+
// by /plan Phase 1 and Phase 2 respectively, via ensureDirWithNote().
|
|
1824
|
+
// TEMPLATE.md and PROGRESS.md are also deferred to first use.
|
|
1819
1825
|
}
|
|
1820
1826
|
|
|
1821
1827
|
// Minimal installation (postinstall)
|
|
@@ -1881,11 +1887,13 @@ function setupClaudeAgent(skipFiles = {}) {
|
|
|
1881
1887
|
if (skipFiles.claudeCommands) {
|
|
1882
1888
|
console.log(' Skipped: .claude/commands/ (keeping existing)');
|
|
1883
1889
|
} else {
|
|
1884
|
-
|
|
1890
|
+
const cmds = getWorkflowCommands();
|
|
1891
|
+
let copied = 0;
|
|
1892
|
+
cmds.forEach(cmd => {
|
|
1885
1893
|
const src = path.join(packageDir, `.claude/commands/${cmd}.md`);
|
|
1886
|
-
copyFile(src, `.claude/commands/${cmd}.md`)
|
|
1894
|
+
if (copyFile(src, `.claude/commands/${cmd}.md`)) copied++;
|
|
1887
1895
|
});
|
|
1888
|
-
console.log(
|
|
1896
|
+
console.log(` Copied: ${copied} workflow commands`);
|
|
1889
1897
|
}
|
|
1890
1898
|
|
|
1891
1899
|
// Copy rules
|
|
@@ -1930,7 +1938,7 @@ function copyAgentCommands(agent, claudeCommands) {
|
|
|
1930
1938
|
const targetDir = agent.dirs[0]; // First dir is commands/workflows
|
|
1931
1939
|
writeFile(`${targetDir}/${targetFile}`, targetContent);
|
|
1932
1940
|
});
|
|
1933
|
-
console.log(
|
|
1941
|
+
console.log(` Converted: ${Object.keys(claudeCommands).length} workflow commands`);
|
|
1934
1942
|
}
|
|
1935
1943
|
|
|
1936
1944
|
// Helper: Copy rules for agent
|
|
@@ -2052,7 +2060,6 @@ function displayInstallationStatus(projectStatus) {
|
|
|
2052
2060
|
if (projectStatus.hasAgentsMd) console.log(' - AGENTS.md');
|
|
2053
2061
|
if (projectStatus.hasClaudeCommands) console.log(' - .claude/commands/');
|
|
2054
2062
|
if (projectStatus.hasEnvLocal) console.log(' - .env.local');
|
|
2055
|
-
if (projectStatus.hasDocsWorkflow) console.log(' - docs/WORKFLOW.md');
|
|
2056
2063
|
console.log('');
|
|
2057
2064
|
}
|
|
2058
2065
|
|
|
@@ -2302,7 +2309,7 @@ function loadClaudeCommands(selectedAgents) {
|
|
|
2302
2309
|
return claudeCommands;
|
|
2303
2310
|
}
|
|
2304
2311
|
|
|
2305
|
-
|
|
2312
|
+
getWorkflowCommands().forEach(cmd => {
|
|
2306
2313
|
const cmdPath = path.join(projectRoot, `.claude/commands/${cmd}.md`);
|
|
2307
2314
|
const content = readFile(cmdPath);
|
|
2308
2315
|
if (content) {
|
|
@@ -2332,17 +2339,15 @@ function displaySetupSummary(selectedAgents) {
|
|
|
2332
2339
|
console.log('');
|
|
2333
2340
|
console.log('What\'s installed:');
|
|
2334
2341
|
console.log(' - AGENTS.md (universal instructions)');
|
|
2335
|
-
console.log(' - docs/WORKFLOW.md (full workflow guide)');
|
|
2336
|
-
console.log(' - docs/research/TEMPLATE.md (research template)');
|
|
2337
|
-
console.log(' - docs/planning/PROGRESS.md (progress tracking)');
|
|
2338
2342
|
|
|
2343
|
+
const workflowCount = getWorkflowCommands().length;
|
|
2339
2344
|
selectedAgents.forEach(key => {
|
|
2340
2345
|
const agent = AGENTS[key];
|
|
2341
2346
|
if (agent.linkFile) {
|
|
2342
2347
|
console.log(` - ${agent.linkFile} (${agent.name})`);
|
|
2343
2348
|
}
|
|
2344
2349
|
if (agent.hasCommands) {
|
|
2345
|
-
console.log(` - .claude/commands/ (
|
|
2350
|
+
console.log(` - .claude/commands/ (${workflowCount} workflow commands)`);
|
|
2346
2351
|
}
|
|
2347
2352
|
if (agent.hasSkill) {
|
|
2348
2353
|
const skillDir = agent.dirs.find(d => d.includes('/skills/'));
|
|
@@ -2521,11 +2526,15 @@ function parseFlags() {
|
|
|
2521
2526
|
agents: null,
|
|
2522
2527
|
all: false,
|
|
2523
2528
|
help: false,
|
|
2529
|
+
version: false,
|
|
2524
2530
|
path: null,
|
|
2525
2531
|
merge: null, // 'smart'|'preserve'|'replace'
|
|
2526
2532
|
type: null, // 'critical'|'standard'|'simple'|'hotfix'|'docs'|'refactor'
|
|
2527
2533
|
interview: false, // Force context interview
|
|
2528
2534
|
budget: null, // Budget mode for recommend command
|
|
2535
|
+
yes: false, // Non-interactive mode (skip prompts, use defaults)
|
|
2536
|
+
force: false, // Force overwrite even if content is identical
|
|
2537
|
+
verbose: false, // Show file-by-file detail in setup summary
|
|
2529
2538
|
};
|
|
2530
2539
|
|
|
2531
2540
|
for (let i = 0; i < args.length;) {
|
|
@@ -2543,6 +2552,9 @@ function parseFlags() {
|
|
|
2543
2552
|
} else if (arg === '--help' || arg === '-h') {
|
|
2544
2553
|
flags.help = true;
|
|
2545
2554
|
i++;
|
|
2555
|
+
} else if (arg === '--version' || arg === '-V') {
|
|
2556
|
+
flags.version = true;
|
|
2557
|
+
i++;
|
|
2546
2558
|
} else if (arg === '--path' || arg === '-p' || arg.startsWith('--path=')) {
|
|
2547
2559
|
const result = parsePathFlag(args, i);
|
|
2548
2560
|
flags.path = result.value;
|
|
@@ -2559,6 +2571,15 @@ function parseFlags() {
|
|
|
2559
2571
|
const result = parseTypeFlag(args, i);
|
|
2560
2572
|
flags.type = result.value;
|
|
2561
2573
|
i = result.nextIndex;
|
|
2574
|
+
} else if (arg === '--yes' || arg === '-y') {
|
|
2575
|
+
flags.yes = true;
|
|
2576
|
+
i++;
|
|
2577
|
+
} else if (arg === '--force') {
|
|
2578
|
+
flags.force = true;
|
|
2579
|
+
i++;
|
|
2580
|
+
} else if (arg === '--verbose') {
|
|
2581
|
+
flags.verbose = true;
|
|
2582
|
+
i++;
|
|
2562
2583
|
} else if (arg === '--interview') {
|
|
2563
2584
|
flags.interview = true;
|
|
2564
2585
|
i++;
|
|
@@ -2697,7 +2718,7 @@ function showHelp() {
|
|
|
2697
2718
|
console.log('Usage:');
|
|
2698
2719
|
console.log(' npx forge setup [options] Interactive agent configuration');
|
|
2699
2720
|
console.log(' npx forge recommend Show recommended tools for your project');
|
|
2700
|
-
console.log(' npx forge
|
|
2721
|
+
console.log(' npx forge --version Show version');
|
|
2701
2722
|
console.log('');
|
|
2702
2723
|
console.log('Options:');
|
|
2703
2724
|
console.log(' --path, -p <dir> Target project directory (default: current directory)');
|
|
@@ -2716,6 +2737,9 @@ function showHelp() {
|
|
|
2716
2737
|
console.log(' Options: critical, standard, simple, hotfix, docs, refactor');
|
|
2717
2738
|
console.log(' --interview Force context interview (gather project information)');
|
|
2718
2739
|
console.log(' --budget <mode> Budget mode for recommend (free, open-source, startup, professional, custom)');
|
|
2740
|
+
console.log(' --yes, -y Non-interactive setup with sensible defaults');
|
|
2741
|
+
console.log(' Defaults to claude agent, skips prompts');
|
|
2742
|
+
console.log(' --version, -V Show version');
|
|
2719
2743
|
console.log(' --help, -h Show this help message');
|
|
2720
2744
|
console.log('');
|
|
2721
2745
|
console.log('Available agents:');
|
|
@@ -2733,6 +2757,8 @@ function showHelp() {
|
|
|
2733
2757
|
console.log(' npx forge setup --agents=claude,cursor # Same, different syntax');
|
|
2734
2758
|
console.log(' npx forge setup --skip-external # No service configuration');
|
|
2735
2759
|
console.log(' npx forge setup --agents claude --quick # Quick + specific agent');
|
|
2760
|
+
console.log(' npx forge setup --yes # Non-interactive, defaults to claude');
|
|
2761
|
+
console.log(' npx forge setup --yes --agents cursor # Non-interactive, specific agent');
|
|
2736
2762
|
console.log(' npx forge setup --all --skip-external # All agents, no services');
|
|
2737
2763
|
console.log(' npx forge setup --merge=smart # Use intelligent merge for existing files');
|
|
2738
2764
|
console.log(' npx forge setup --type=critical # Set workflow profile manually');
|
|
@@ -3314,11 +3340,9 @@ async function quickSetup(selectedAgents, skipExternal) {
|
|
|
3314
3340
|
checkPrerequisites();
|
|
3315
3341
|
console.log('');
|
|
3316
3342
|
|
|
3317
|
-
// Copy AGENTS.md
|
|
3343
|
+
// Copy AGENTS.md (actionLog tracks it via copyFile)
|
|
3318
3344
|
const agentsSrc = path.join(packageDir, 'AGENTS.md');
|
|
3319
|
-
|
|
3320
|
-
console.log(' Created: AGENTS.md (universal standard)');
|
|
3321
|
-
}
|
|
3345
|
+
copyFile(agentsSrc, 'AGENTS.md');
|
|
3322
3346
|
console.log('');
|
|
3323
3347
|
|
|
3324
3348
|
// Setup core documentation
|
|
@@ -3342,17 +3366,9 @@ async function quickSetup(selectedAgents, skipExternal) {
|
|
|
3342
3366
|
// Configure external services with defaults (unless skipped)
|
|
3343
3367
|
configureDefaultExternalServices(skipExternal);
|
|
3344
3368
|
|
|
3345
|
-
//
|
|
3346
|
-
console.log('');
|
|
3347
|
-
console.log('==============================================');
|
|
3348
|
-
console.log(` Forge v${VERSION} Quick Setup Complete!`);
|
|
3349
|
-
console.log('==============================================');
|
|
3350
|
-
console.log('');
|
|
3351
|
-
console.log('Next steps:');
|
|
3352
|
-
console.log(' 1. Start with: /status');
|
|
3353
|
-
console.log(' 2. Read the guide: docs/WORKFLOW.md');
|
|
3369
|
+
// Progressive setup summary
|
|
3354
3370
|
console.log('');
|
|
3355
|
-
console.log(
|
|
3371
|
+
console.log(renderSetupSummary(actionLog, selectedAgents, VERBOSE_MODE));
|
|
3356
3372
|
console.log('');
|
|
3357
3373
|
}
|
|
3358
3374
|
|
|
@@ -3460,7 +3476,6 @@ function displayExistingInstallation(projectStatus) {
|
|
|
3460
3476
|
if (projectStatus.hasAgentsMd) console.log(' - AGENTS.md');
|
|
3461
3477
|
if (projectStatus.hasClaudeCommands) console.log(' - .claude/commands/');
|
|
3462
3478
|
if (projectStatus.hasEnvLocal) console.log(' - .env.local');
|
|
3463
|
-
if (projectStatus.hasDocsWorkflow) console.log(' - docs/WORKFLOW.md');
|
|
3464
3479
|
console.log('');
|
|
3465
3480
|
}
|
|
3466
3481
|
|
|
@@ -3506,7 +3521,7 @@ function loadAndSetupClaudeCommands(selectedAgents, skipFiles) {
|
|
|
3506
3521
|
}
|
|
3507
3522
|
|
|
3508
3523
|
// Then load the commands (from existing or newly created)
|
|
3509
|
-
|
|
3524
|
+
getWorkflowCommands().forEach(cmd => {
|
|
3510
3525
|
const cmdPath = path.join(projectRoot, `.claude/commands/${cmd}.md`);
|
|
3511
3526
|
const content = readFile(cmdPath);
|
|
3512
3527
|
if (content) {
|
|
@@ -3600,6 +3615,15 @@ async function interactiveSetupWithFlags(flags) {
|
|
|
3600
3615
|
// Prompt for overwrite decisions
|
|
3601
3616
|
const skipFiles = await promptForOverwriteDecisions(question, projectStatus);
|
|
3602
3617
|
|
|
3618
|
+
// Agent auto-detection (suggests but does not force)
|
|
3619
|
+
const envDetection = detectEnvironment(projectRoot);
|
|
3620
|
+
if (envDetection.activeAgent && envDetection.confidence === 'high') {
|
|
3621
|
+
console.log(` Detected: ${envDetection.activeAgent} (${envDetection.activeAgentSource})`);
|
|
3622
|
+
}
|
|
3623
|
+
if (envDetection.configuredAgents.length > 0) {
|
|
3624
|
+
console.log(` Previously configured: ${envDetection.configuredAgents.join(', ')}`);
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3603
3627
|
// STEP 1: Agent Selection (delegated to helper)
|
|
3604
3628
|
const agentKeys = Object.keys(AGENTS);
|
|
3605
3629
|
const selectedAgents = await promptForAgentSelection(question, agentKeys);
|
|
@@ -3686,8 +3710,10 @@ function determineSelectedAgents(flags) {
|
|
|
3686
3710
|
return [];
|
|
3687
3711
|
}
|
|
3688
3712
|
|
|
3689
|
-
//
|
|
3690
|
-
async function
|
|
3713
|
+
// Shared setup executor — used by handleSetupCommand
|
|
3714
|
+
async function executeSetup(config) {
|
|
3715
|
+
const { agents, skipExternal } = config;
|
|
3716
|
+
|
|
3691
3717
|
showBanner('Installing for specified agents...');
|
|
3692
3718
|
console.log('');
|
|
3693
3719
|
|
|
@@ -3695,10 +3721,13 @@ async function handleSetupCommand(selectedAgents, flags) {
|
|
|
3695
3721
|
checkPrerequisites();
|
|
3696
3722
|
console.log('');
|
|
3697
3723
|
|
|
3698
|
-
// Copy AGENTS.md
|
|
3699
|
-
const
|
|
3700
|
-
if (
|
|
3701
|
-
|
|
3724
|
+
// Copy AGENTS.md (only if not exists — preserve user customizations; actionLog tracks it)
|
|
3725
|
+
const agentsDest = path.join(projectRoot, 'AGENTS.md');
|
|
3726
|
+
if (fs.existsSync(agentsDest)) {
|
|
3727
|
+
actionLog.add('AGENTS.md', 'skipped', 'already exists');
|
|
3728
|
+
} else {
|
|
3729
|
+
const agentsSrc = path.join(packageDir, 'AGENTS.md');
|
|
3730
|
+
copyFile(agentsSrc, 'AGENTS.md');
|
|
3702
3731
|
}
|
|
3703
3732
|
console.log('');
|
|
3704
3733
|
|
|
@@ -3706,28 +3735,35 @@ async function handleSetupCommand(selectedAgents, flags) {
|
|
|
3706
3735
|
setupCoreDocs();
|
|
3707
3736
|
console.log('');
|
|
3708
3737
|
|
|
3709
|
-
// Load Claude commands
|
|
3710
|
-
|
|
3738
|
+
// Load Claude commands — use loadAndSetupClaudeCommands when claude is selected
|
|
3739
|
+
// so that .claude/commands/ are seeded before reading them
|
|
3740
|
+
const claudeCommands = agents.includes('claude')
|
|
3741
|
+
? loadAndSetupClaudeCommands(agents)
|
|
3742
|
+
: loadClaudeCommands(agents);
|
|
3711
3743
|
|
|
3712
|
-
// Setup agents
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
setupAgent(agentKey, claudeCommands);
|
|
3716
|
-
}
|
|
3717
|
-
});
|
|
3718
|
-
|
|
3719
|
-
console.log('');
|
|
3720
|
-
console.log('Agent configuration complete!');
|
|
3744
|
+
// Setup agents with progress output (setupSelectedAgents skips claude internally
|
|
3745
|
+
// since loadAndSetupClaudeCommands already handled it above)
|
|
3746
|
+
setupSelectedAgents(agents, claudeCommands);
|
|
3721
3747
|
|
|
3722
3748
|
// Install git hooks for TDD enforcement
|
|
3723
3749
|
console.log('');
|
|
3724
3750
|
installGitHooks();
|
|
3725
3751
|
|
|
3726
3752
|
// External services (unless skipped)
|
|
3727
|
-
await handleExternalServices(
|
|
3753
|
+
await handleExternalServices(skipExternal, agents);
|
|
3728
3754
|
|
|
3755
|
+
// Progressive setup summary
|
|
3756
|
+
console.log('');
|
|
3757
|
+
console.log(renderSetupSummary(actionLog, agents, VERBOSE_MODE));
|
|
3729
3758
|
console.log('');
|
|
3730
|
-
|
|
3759
|
+
}
|
|
3760
|
+
|
|
3761
|
+
// Helper: Handle setup command in non-quick mode
|
|
3762
|
+
async function handleSetupCommand(selectedAgents, flags) {
|
|
3763
|
+
await executeSetup({
|
|
3764
|
+
agents: selectedAgents,
|
|
3765
|
+
skipExternal: flags.skipExternal,
|
|
3766
|
+
});
|
|
3731
3767
|
}
|
|
3732
3768
|
|
|
3733
3769
|
// Helper: Handle external services configuration
|
|
@@ -3761,33 +3797,66 @@ async function main() {
|
|
|
3761
3797
|
const command = args[0];
|
|
3762
3798
|
const flags = parseFlags();
|
|
3763
3799
|
|
|
3800
|
+
// Wire up incremental setup state from parsed flags
|
|
3801
|
+
FORCE_MODE = flags.force;
|
|
3802
|
+
VERBOSE_MODE = flags.verbose;
|
|
3803
|
+
actionLog = new SetupActionLog();
|
|
3804
|
+
|
|
3764
3805
|
// Show help
|
|
3765
3806
|
if (flags.help) {
|
|
3766
3807
|
showHelp();
|
|
3767
3808
|
return;
|
|
3768
3809
|
}
|
|
3769
3810
|
|
|
3811
|
+
// Show version
|
|
3812
|
+
if (flags.version) {
|
|
3813
|
+
console.log(`Forge v${VERSION}`);
|
|
3814
|
+
return;
|
|
3815
|
+
}
|
|
3816
|
+
|
|
3770
3817
|
// Handle --path option: change to target directory
|
|
3771
3818
|
if (flags.path) {
|
|
3772
3819
|
// Update projectRoot after changing directory to maintain state consistency
|
|
3773
3820
|
projectRoot = handlePathSetup(flags.path);
|
|
3774
3821
|
}
|
|
3775
3822
|
|
|
3823
|
+
// First-run detection: check if Forge is configured in this project
|
|
3824
|
+
// Skip for: setup (needs to run to configure), recommend (read-only)
|
|
3825
|
+
// Note: help and version already returned above, so no need to check here
|
|
3826
|
+
if (command !== 'setup' && command !== 'recommend') {
|
|
3827
|
+
const agentsMdPath = path.join(projectRoot, 'AGENTS.md');
|
|
3828
|
+
if (!fs.existsSync(agentsMdPath)) {
|
|
3829
|
+
console.error('[FORGE_SETUP_REQUIRED] Forge is not configured in this project.\n');
|
|
3830
|
+
console.error(' Run: npx forge setup');
|
|
3831
|
+
console.error(' Or: npx forge setup --yes (non-interactive)\n');
|
|
3832
|
+
process.exit(1);
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
|
|
3776
3836
|
if (command === 'setup') {
|
|
3777
3837
|
// Determine agents to install
|
|
3778
3838
|
let selectedAgents = determineSelectedAgents(flags);
|
|
3779
3839
|
|
|
3840
|
+
// Non-interactive mode: --yes defaults to claude agent, skips prompts
|
|
3841
|
+
// Applied before --quick so --quick --yes works correctly
|
|
3842
|
+
if (flags.yes && selectedAgents.length === 0) {
|
|
3843
|
+
selectedAgents = ['claude'];
|
|
3844
|
+
}
|
|
3845
|
+
if (flags.yes) {
|
|
3846
|
+
flags.skipExternal = true;
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3780
3849
|
// Quick mode
|
|
3781
3850
|
if (flags.quick) {
|
|
3782
|
-
// If no agents specified
|
|
3783
|
-
if (selectedAgents.length === 0) {
|
|
3851
|
+
// If no explicit agents specified, use all (--yes default doesn't count)
|
|
3852
|
+
if (selectedAgents.length === 0 || (flags.yes && !flags.agents)) {
|
|
3784
3853
|
selectedAgents = Object.keys(AGENTS);
|
|
3785
3854
|
}
|
|
3786
3855
|
await quickSetup(selectedAgents, flags.skipExternal);
|
|
3787
3856
|
return;
|
|
3788
3857
|
}
|
|
3789
3858
|
|
|
3790
|
-
// Agents specified via flag (non-quick mode)
|
|
3859
|
+
// Agents specified via flag or --yes default (non-quick mode)
|
|
3791
3860
|
if (selectedAgents.length > 0) {
|
|
3792
3861
|
await handleSetupCommand(selectedAgents, flags);
|
|
3793
3862
|
return;
|
|
@@ -4230,3 +4299,5 @@ if (require.main === module) {
|
|
|
4230
4299
|
}
|
|
4231
4300
|
})();
|
|
4232
4301
|
}
|
|
4302
|
+
|
|
4303
|
+
module.exports = { getWorkflowCommands, ensureDirWithNote };
|
|
@@ -201,7 +201,7 @@ Summarize what was completed for the user:
|
|
|
201
201
|
- `/plan <feature-slug>` - Design + research + implementation plan
|
|
202
202
|
- `/dev` - Start TDD development
|
|
203
203
|
|
|
204
|
-
**Need help?** Check
|
|
204
|
+
**Need help?** Check AGENTS.md for full guide
|
|
205
205
|
```
|
|
206
206
|
|
|
207
207
|
---
|