forge-workflow 1.1.2 → 1.2.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.
@@ -12,7 +12,9 @@
12
12
  "Bash(git add:*)",
13
13
  "Bash(git commit:*)",
14
14
  "Bash(git push)",
15
- "Bash(\"C:\\\\Program Files\\\\nodejs\\\\npm.cmd\" pkg fix)"
15
+ "Bash(\"C:\\\\Program Files\\\\nodejs\\\\npm.cmd\" pkg fix)",
16
+ "WebFetch(domain:www.aihero.dev)",
17
+ "Bash(\"C:\\\\Program Files\\\\nodejs\\\\npm.cmd\" version minor --no-git-tag-version)"
16
18
  ]
17
19
  }
18
20
  }
package/bin/forge.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Forge v1.1.2 - Universal AI Agent Workflow
4
+ * Forge v1.2.0 - Universal AI Agent Workflow
5
5
  * https://github.com/harshanandak/forge
6
6
  *
7
7
  * Usage:
@@ -11,6 +11,7 @@
11
11
  * npx forge setup --agents claude,cursor,windsurf
12
12
  *
13
13
  * CLI Flags:
14
+ * --path, -p <dir> Target project directory (creates if needed)
14
15
  * --quick, -q Use all defaults, minimal prompts
15
16
  * --skip-external Skip external services configuration
16
17
  * --agents <list> Specify agents (--agents claude cursor OR --agents=claude,cursor)
@@ -19,6 +20,7 @@
19
20
  *
20
21
  * Examples:
21
22
  * npx forge setup --quick # All defaults, no prompts
23
+ * npx forge setup -p ./my-project # Setup in specific directory
22
24
  * npx forge setup --agents claude cursor # Just these agents
23
25
  * npx forge setup --skip-external # No service prompts
24
26
  * npx forge setup --agents claude --quick # Quick + specific agent
@@ -589,6 +591,27 @@ function writeEnvTokens(tokens, preserveExisting = true) {
589
591
  }
590
592
 
591
593
  // Detect existing project installation status
594
+ // Helper function for yes/no prompts with validation
595
+ async function askYesNo(question, prompt, defaultNo = true) {
596
+ const defaultText = defaultNo ? '[n]' : '[y]';
597
+ while (true) {
598
+ const answer = await question(`${prompt} (y/n) ${defaultText}: `);
599
+ const normalized = answer.trim().toLowerCase();
600
+
601
+ // Handle empty input (use default)
602
+ if (normalized === '') return defaultNo ? false : true;
603
+
604
+ // Accept yes variations
605
+ if (normalized === 'y' || normalized === 'yes') return true;
606
+
607
+ // Accept no variations
608
+ if (normalized === 'n' || normalized === 'no') return false;
609
+
610
+ // Invalid input - re-prompt
611
+ console.log(' Please enter y or n');
612
+ }
613
+ }
614
+
592
615
  function detectProjectStatus() {
593
616
  const status = {
594
617
  type: 'fresh', // 'fresh', 'upgrade', or 'partial'
@@ -602,10 +625,10 @@ function detectProjectStatus() {
602
625
  // Determine installation type
603
626
  if (status.hasAgentsMd && status.hasClaudeCommands && status.hasDocsWorkflow) {
604
627
  status.type = 'upgrade'; // Full forge installation exists
605
- } else if (status.hasAgentsMd || status.hasClaudeCommands || status.hasEnvLocal) {
606
- status.type = 'partial'; // Some files exist
628
+ } else if (status.hasClaudeCommands || status.hasEnvLocal) {
629
+ status.type = 'partial'; // Agent-specific files exist (not just base files from postinstall)
607
630
  }
608
- // else: 'fresh' - new installation
631
+ // else: 'fresh' - new installation (or just postinstall baseline with AGENTS.md)
609
632
 
610
633
  // Parse existing env vars if .env.local exists
611
634
  if (status.hasEnvLocal) {
@@ -639,8 +662,8 @@ async function configureExternalServices(rl, question, selectedAgents = [], proj
639
662
  }
640
663
  console.log('');
641
664
 
642
- const reconfigure = await question('Reconfigure external services? (y/n) [n]: ');
643
- if (reconfigure.toLowerCase() !== 'y' && reconfigure.toLowerCase() !== 'yes') {
665
+ const reconfigure = await askYesNo(question, 'Reconfigure external services?', true);
666
+ if (!reconfigure) {
644
667
  console.log('');
645
668
  console.log('Keeping existing configuration.');
646
669
  return;
@@ -652,9 +675,9 @@ async function configureExternalServices(rl, question, selectedAgents = [], proj
652
675
  console.log('(You can also add them later to .env.local)');
653
676
  console.log('');
654
677
 
655
- const configure = await question('Configure external services? (y/n): ');
678
+ const configure = await askYesNo(question, 'Configure external services?', false);
656
679
 
657
- if (configure.toLowerCase() !== 'y' && configure.toLowerCase() !== 'yes') {
680
+ if (!configure) {
658
681
  console.log('');
659
682
  console.log('Skipping external services. You can configure them later by editing .env.local');
660
683
  return;
@@ -865,7 +888,7 @@ function showBanner(subtitle = 'Universal AI Agent Workflow') {
865
888
  console.log(' ██╔══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝ ');
866
889
  console.log(' ██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗');
867
890
  console.log(' ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝');
868
- console.log(' v1.1.2');
891
+ console.log(' v1.2.0');
869
892
  console.log('');
870
893
  if (subtitle) {
871
894
  console.log(` ${subtitle}`);
@@ -874,6 +897,23 @@ function showBanner(subtitle = 'Universal AI Agent Workflow') {
874
897
 
875
898
  // Minimal installation (postinstall)
876
899
  function minimalInstall() {
900
+ // Check if this looks like a project (has package.json)
901
+ const hasPackageJson = fs.existsSync(path.join(projectRoot, 'package.json'));
902
+
903
+ if (!hasPackageJson) {
904
+ console.log('');
905
+ console.log(' ✅ Forge installed successfully!');
906
+ console.log('');
907
+ console.log(' To set up in a project:');
908
+ console.log(' cd your-project');
909
+ console.log(' npx forge setup');
910
+ console.log('');
911
+ console.log(' Or specify a project directory:');
912
+ console.log(' npx forge setup --path ./my-project');
913
+ console.log('');
914
+ return;
915
+ }
916
+
877
917
  showBanner();
878
918
  console.log('');
879
919
 
@@ -1123,6 +1163,11 @@ async function interactiveSetup() {
1123
1163
 
1124
1164
  showBanner('Agent Configuration');
1125
1165
 
1166
+ // Show target directory
1167
+ console.log(` Target directory: ${process.cwd()}`);
1168
+ console.log(' (Use --path <dir> to change target directory)');
1169
+ console.log('');
1170
+
1126
1171
  // Check prerequisites first
1127
1172
  checkPrerequisites();
1128
1173
  console.log('');
@@ -1159,8 +1204,8 @@ async function interactiveSetup() {
1159
1204
 
1160
1205
  // Ask about overwriting AGENTS.md if it exists
1161
1206
  if (projectStatus.hasAgentsMd) {
1162
- const overwriteAgents = await question('Found existing AGENTS.md. Overwrite? (y/n) [n]: ');
1163
- if (overwriteAgents.toLowerCase() !== 'y' && overwriteAgents.toLowerCase() !== 'yes') {
1207
+ const overwriteAgents = await askYesNo(question, 'Found existing AGENTS.md. Overwrite?', true);
1208
+ if (!overwriteAgents) {
1164
1209
  skipFiles.agentsMd = true;
1165
1210
  console.log(' Keeping existing AGENTS.md');
1166
1211
  } else {
@@ -1170,8 +1215,8 @@ async function interactiveSetup() {
1170
1215
 
1171
1216
  // Ask about overwriting .claude/commands/ if it exists
1172
1217
  if (projectStatus.hasClaudeCommands) {
1173
- const overwriteCommands = await question('Found existing .claude/commands/. Overwrite? (y/n) [n]: ');
1174
- if (overwriteCommands.toLowerCase() !== 'y' && overwriteCommands.toLowerCase() !== 'yes') {
1218
+ const overwriteCommands = await askYesNo(question, 'Found existing .claude/commands/. Overwrite?', true);
1219
+ if (!overwriteCommands) {
1175
1220
  skipFiles.claudeCommands = true;
1176
1221
  console.log(' Keeping existing .claude/commands/');
1177
1222
  } else {
@@ -1303,7 +1348,7 @@ async function interactiveSetup() {
1303
1348
  // =============================================
1304
1349
  console.log('');
1305
1350
  console.log('==============================================');
1306
- console.log(' Forge v1.1.2 Setup Complete!');
1351
+ console.log(' Forge v1.2.0 Setup Complete!');
1307
1352
  console.log('==============================================');
1308
1353
  console.log('');
1309
1354
  console.log('What\'s installed:');
@@ -1347,7 +1392,8 @@ function parseFlags() {
1347
1392
  skipExternal: false,
1348
1393
  agents: null,
1349
1394
  all: false,
1350
- help: false
1395
+ help: false,
1396
+ path: null
1351
1397
  };
1352
1398
 
1353
1399
  for (let i = 0; i < args.length; i++) {
@@ -1361,6 +1407,15 @@ function parseFlags() {
1361
1407
  flags.all = true;
1362
1408
  } else if (arg === '--help' || arg === '-h') {
1363
1409
  flags.help = true;
1410
+ } else if (arg === '--path' || arg === '-p') {
1411
+ // --path <directory> or -p <directory>
1412
+ if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
1413
+ flags.path = args[i + 1];
1414
+ i++; // Skip next arg
1415
+ }
1416
+ } else if (arg.startsWith('--path=')) {
1417
+ // --path=/some/dir format
1418
+ flags.path = arg.replace('--path=', '');
1364
1419
  } else if (arg === '--agents') {
1365
1420
  // --agents claude cursor format
1366
1421
  const agentList = [];
@@ -1403,6 +1458,8 @@ function showHelp() {
1403
1458
  console.log(' npx forge Minimal install (AGENTS.md + docs)');
1404
1459
  console.log('');
1405
1460
  console.log('Options:');
1461
+ console.log(' --path, -p <dir> Target project directory (default: current directory)');
1462
+ console.log(' Creates the directory if it doesn\'t exist');
1406
1463
  console.log(' --quick, -q Use all defaults, minimal prompts');
1407
1464
  console.log(' Auto-selects: all agents, GitHub Code Quality, ESLint');
1408
1465
  console.log(' --skip-external Skip external services configuration');
@@ -1421,6 +1478,8 @@ function showHelp() {
1421
1478
  console.log('Examples:');
1422
1479
  console.log(' npx forge setup # Interactive setup');
1423
1480
  console.log(' npx forge setup --quick # All defaults, no prompts');
1481
+ console.log(' npx forge setup -p ./my-project # Setup in specific directory');
1482
+ console.log(' npx forge setup --path=/home/user/app # Same, different syntax');
1424
1483
  console.log(' npx forge setup --agents claude cursor # Just these agents');
1425
1484
  console.log(' npx forge setup --agents=claude,cursor # Same, different syntax');
1426
1485
  console.log(' npx forge setup --skip-external # No service configuration');
@@ -1510,7 +1569,7 @@ async function quickSetup(selectedAgents, skipExternal) {
1510
1569
  // Final summary
1511
1570
  console.log('');
1512
1571
  console.log('==============================================');
1513
- console.log(' Forge v1.1.2 Quick Setup Complete!');
1572
+ console.log(' Forge v1.2.0 Quick Setup Complete!');
1514
1573
  console.log('==============================================');
1515
1574
  console.log('');
1516
1575
  console.log('Next steps:');
@@ -1548,6 +1607,11 @@ async function interactiveSetupWithFlags(flags) {
1548
1607
 
1549
1608
  showBanner('Agent Configuration');
1550
1609
 
1610
+ // Show target directory
1611
+ console.log(` Target directory: ${process.cwd()}`);
1612
+ console.log(' (Use --path <dir> to change target directory)');
1613
+ console.log('');
1614
+
1551
1615
  // Check prerequisites first
1552
1616
  checkPrerequisites();
1553
1617
  console.log('');
@@ -1584,8 +1648,8 @@ async function interactiveSetupWithFlags(flags) {
1584
1648
 
1585
1649
  // Ask about overwriting AGENTS.md if it exists
1586
1650
  if (projectStatus.hasAgentsMd) {
1587
- const overwriteAgents = await question('Found existing AGENTS.md. Overwrite? (y/n) [n]: ');
1588
- if (overwriteAgents.toLowerCase() !== 'y' && overwriteAgents.toLowerCase() !== 'yes') {
1651
+ const overwriteAgents = await askYesNo(question, 'Found existing AGENTS.md. Overwrite?', true);
1652
+ if (!overwriteAgents) {
1589
1653
  skipFiles.agentsMd = true;
1590
1654
  console.log(' Keeping existing AGENTS.md');
1591
1655
  } else {
@@ -1595,8 +1659,8 @@ async function interactiveSetupWithFlags(flags) {
1595
1659
 
1596
1660
  // Ask about overwriting .claude/commands/ if it exists
1597
1661
  if (projectStatus.hasClaudeCommands) {
1598
- const overwriteCommands = await question('Found existing .claude/commands/. Overwrite? (y/n) [n]: ');
1599
- if (overwriteCommands.toLowerCase() !== 'y' && overwriteCommands.toLowerCase() !== 'yes') {
1662
+ const overwriteCommands = await askYesNo(question, 'Found existing .claude/commands/. Overwrite?', true);
1663
+ if (!overwriteCommands) {
1600
1664
  skipFiles.claudeCommands = true;
1601
1665
  console.log(' Keeping existing .claude/commands/');
1602
1666
  } else {
@@ -1733,7 +1797,7 @@ async function interactiveSetupWithFlags(flags) {
1733
1797
  // =============================================
1734
1798
  console.log('');
1735
1799
  console.log('==============================================');
1736
- console.log(' Forge v1.1.2 Setup Complete!');
1800
+ console.log(' Forge v1.2.0 Setup Complete!');
1737
1801
  console.log('==============================================');
1738
1802
  console.log('');
1739
1803
  console.log('What\'s installed:');
@@ -1781,6 +1845,38 @@ async function main() {
1781
1845
  return;
1782
1846
  }
1783
1847
 
1848
+ // Handle --path option: change to target directory
1849
+ if (flags.path) {
1850
+ const targetPath = path.resolve(flags.path);
1851
+
1852
+ // Create directory if it doesn't exist
1853
+ if (!fs.existsSync(targetPath)) {
1854
+ try {
1855
+ fs.mkdirSync(targetPath, { recursive: true });
1856
+ console.log(`Created directory: ${targetPath}`);
1857
+ } catch (err) {
1858
+ console.error(`Error creating directory: ${err.message}`);
1859
+ process.exit(1);
1860
+ }
1861
+ }
1862
+
1863
+ // Verify it's a directory
1864
+ if (!fs.statSync(targetPath).isDirectory()) {
1865
+ console.error(`Error: ${targetPath} is not a directory`);
1866
+ process.exit(1);
1867
+ }
1868
+
1869
+ // Change to target directory
1870
+ try {
1871
+ process.chdir(targetPath);
1872
+ console.log(`Working directory: ${targetPath}`);
1873
+ console.log('');
1874
+ } catch (err) {
1875
+ console.error(`Error changing to directory: ${err.message}`);
1876
+ process.exit(1);
1877
+ }
1878
+ }
1879
+
1784
1880
  if (command === 'setup') {
1785
1881
  // Determine agents to install
1786
1882
  let selectedAgents = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-workflow",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "9-stage TDD workflow for ALL AI coding agents (Claude, Cursor, Windsurf, Kilo, OpenCode, Copilot, Cline, Roo, Aider, Continue, Antigravity)",
5
5
  "bin": {
6
6
  "forge": "bin/forge.js"