lean-spec 0.2.7-dev.20251126090057 → 0.2.7-dev.20251126100750

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.
@@ -4486,50 +4486,201 @@ async function copyTemplate(source, target, cwd = process.cwd()) {
4486
4486
  console.log(chalk20.gray(` Use with: lean-spec create <spec-name> --template=${templateName}`));
4487
4487
  }
4488
4488
  var AI_TOOL_CONFIGS = {
4489
+ aider: {
4490
+ file: "AGENTS.md",
4491
+ description: "Aider (uses AGENTS.md)",
4492
+ default: false,
4493
+ usesSymlink: false,
4494
+ detection: {
4495
+ commands: ["aider"],
4496
+ configDirs: [".aider"]
4497
+ }
4498
+ },
4489
4499
  claude: {
4490
4500
  file: "CLAUDE.md",
4491
- description: "Claude Code / Claude Desktop (CLAUDE.md)",
4501
+ description: "Claude Code (CLAUDE.md)",
4492
4502
  default: true,
4493
- usesSymlink: true
4503
+ usesSymlink: true,
4504
+ detection: {
4505
+ commands: ["claude"],
4506
+ configDirs: [".claude"],
4507
+ envVars: ["ANTHROPIC_API_KEY"]
4508
+ }
4494
4509
  },
4495
- gemini: {
4496
- file: "GEMINI.md",
4497
- description: "Gemini CLI (GEMINI.md)",
4510
+ codex: {
4511
+ file: "AGENTS.md",
4512
+ description: "Codex CLI by OpenAI (uses AGENTS.md)",
4498
4513
  default: false,
4499
- usesSymlink: true
4514
+ usesSymlink: false,
4515
+ detection: {
4516
+ commands: ["codex"],
4517
+ configDirs: [".codex"],
4518
+ envVars: ["OPENAI_API_KEY"]
4519
+ }
4500
4520
  },
4501
4521
  copilot: {
4502
4522
  file: "AGENTS.md",
4503
4523
  description: "GitHub Copilot (AGENTS.md - default)",
4504
4524
  default: true,
4505
- usesSymlink: false
4525
+ usesSymlink: false,
4506
4526
  // Primary file, no symlink needed
4527
+ detection: {
4528
+ commands: ["copilot"],
4529
+ envVars: ["GITHUB_TOKEN"]
4530
+ }
4507
4531
  },
4508
4532
  cursor: {
4509
4533
  file: "AGENTS.md",
4510
4534
  description: "Cursor (uses AGENTS.md)",
4511
4535
  default: false,
4512
- usesSymlink: false
4536
+ usesSymlink: false,
4537
+ detection: {
4538
+ configDirs: [".cursor", ".cursorules"],
4539
+ commands: ["cursor"]
4540
+ }
4513
4541
  },
4514
- windsurf: {
4542
+ droid: {
4515
4543
  file: "AGENTS.md",
4516
- description: "Windsurf (uses AGENTS.md)",
4544
+ description: "Droid by Factory (uses AGENTS.md)",
4517
4545
  default: false,
4518
- usesSymlink: false
4546
+ usesSymlink: false,
4547
+ detection: {
4548
+ commands: ["droid"]
4549
+ }
4519
4550
  },
4520
- cline: {
4551
+ gemini: {
4552
+ file: "GEMINI.md",
4553
+ description: "Gemini CLI (GEMINI.md)",
4554
+ default: false,
4555
+ usesSymlink: true,
4556
+ detection: {
4557
+ commands: ["gemini"],
4558
+ configDirs: [".gemini"],
4559
+ envVars: ["GOOGLE_API_KEY", "GEMINI_API_KEY"]
4560
+ }
4561
+ },
4562
+ opencode: {
4521
4563
  file: "AGENTS.md",
4522
- description: "Cline (uses AGENTS.md)",
4564
+ description: "OpenCode (uses AGENTS.md)",
4523
4565
  default: false,
4524
- usesSymlink: false
4566
+ usesSymlink: false,
4567
+ detection: {
4568
+ commands: ["opencode"],
4569
+ configDirs: [".opencode"]
4570
+ }
4525
4571
  },
4526
- warp: {
4572
+ windsurf: {
4527
4573
  file: "AGENTS.md",
4528
- description: "Warp Terminal (uses AGENTS.md)",
4574
+ description: "Windsurf (uses AGENTS.md)",
4529
4575
  default: false,
4530
- usesSymlink: false
4576
+ usesSymlink: false,
4577
+ detection: {
4578
+ configDirs: [".windsurf", ".windsurfrules"],
4579
+ commands: ["windsurf"]
4580
+ }
4531
4581
  }
4532
4582
  };
4583
+ function commandExists(command) {
4584
+ try {
4585
+ const which = process.platform === "win32" ? "where" : "which";
4586
+ execSync(`${which} ${command}`, { stdio: "ignore" });
4587
+ return true;
4588
+ } catch {
4589
+ return false;
4590
+ }
4591
+ }
4592
+ async function configDirExists(dirName) {
4593
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
4594
+ if (!homeDir) return false;
4595
+ try {
4596
+ await fs11.access(path17.join(homeDir, dirName));
4597
+ return true;
4598
+ } catch {
4599
+ return false;
4600
+ }
4601
+ }
4602
+ function envVarExists(varName) {
4603
+ return !!process.env[varName];
4604
+ }
4605
+ async function extensionInstalled(extensionId) {
4606
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
4607
+ if (!homeDir) return false;
4608
+ const extensionDirs = [
4609
+ path17.join(homeDir, ".vscode", "extensions"),
4610
+ path17.join(homeDir, ".vscode-server", "extensions"),
4611
+ path17.join(homeDir, ".cursor", "extensions")
4612
+ ];
4613
+ for (const extDir of extensionDirs) {
4614
+ try {
4615
+ const entries = await fs11.readdir(extDir);
4616
+ if (entries.some((e) => e.toLowerCase().startsWith(extensionId.toLowerCase()))) {
4617
+ return true;
4618
+ }
4619
+ } catch {
4620
+ }
4621
+ }
4622
+ return false;
4623
+ }
4624
+ async function detectInstalledAITools() {
4625
+ const results = [];
4626
+ for (const [toolKey, config] of Object.entries(AI_TOOL_CONFIGS)) {
4627
+ const reasons = [];
4628
+ const detection = config.detection;
4629
+ if (!detection) {
4630
+ results.push({ tool: toolKey, detected: false, reasons: [] });
4631
+ continue;
4632
+ }
4633
+ if (detection.commands) {
4634
+ for (const cmd of detection.commands) {
4635
+ if (commandExists(cmd)) {
4636
+ reasons.push(`'${cmd}' command found`);
4637
+ }
4638
+ }
4639
+ }
4640
+ if (detection.configDirs) {
4641
+ for (const dir of detection.configDirs) {
4642
+ if (await configDirExists(dir)) {
4643
+ reasons.push(`~/${dir} directory found`);
4644
+ }
4645
+ }
4646
+ }
4647
+ if (detection.envVars) {
4648
+ for (const envVar of detection.envVars) {
4649
+ if (envVarExists(envVar)) {
4650
+ reasons.push(`${envVar} env var set`);
4651
+ }
4652
+ }
4653
+ }
4654
+ if (detection.extensions) {
4655
+ for (const ext of detection.extensions) {
4656
+ if (await extensionInstalled(ext)) {
4657
+ reasons.push(`${ext} extension installed`);
4658
+ }
4659
+ }
4660
+ }
4661
+ results.push({
4662
+ tool: toolKey,
4663
+ detected: reasons.length > 0,
4664
+ reasons
4665
+ });
4666
+ }
4667
+ return results;
4668
+ }
4669
+ async function getDefaultAIToolSelection() {
4670
+ const detectionResults = await detectInstalledAITools();
4671
+ const detectedTools = detectionResults.filter((r) => r.detected).map((r) => r.tool);
4672
+ if (detectedTools.length > 0) {
4673
+ const copilotDetected = detectedTools.includes("copilot");
4674
+ if (!copilotDetected) {
4675
+ const usesAgentsMd = detectedTools.some((t) => !AI_TOOL_CONFIGS[t].usesSymlink);
4676
+ if (!usesAgentsMd) {
4677
+ detectedTools.push("copilot");
4678
+ }
4679
+ }
4680
+ return { defaults: detectedTools, detected: detectionResults };
4681
+ }
4682
+ return { defaults: ["copilot"], detected: detectionResults };
4683
+ }
4533
4684
  async function createAgentToolSymlinks(cwd, selectedTools) {
4534
4685
  const results = [];
4535
4686
  const isWindows = process.platform === "win32";
@@ -4805,7 +4956,7 @@ async function initProject(skipPrompts = false, templateOption, agentToolsOption
4805
4956
  if (skipPrompts) {
4806
4957
  console.log(chalk20.gray("Using defaults: quick start with standard template"));
4807
4958
  if (!agentToolsOption) {
4808
- selectedAgentTools = ["claude", "copilot"];
4959
+ selectedAgentTools = ["copilot"];
4809
4960
  }
4810
4961
  console.log("");
4811
4962
  } else if (!templateOption) {
@@ -4912,18 +5063,31 @@ async function initProject(skipPrompts = false, templateOption, agentToolsOption
4912
5063
  templateConfig.structure.pattern = "flat";
4913
5064
  templateConfig.structure.prefix = "";
4914
5065
  }
4915
- if (!skipPrompts && !agentToolsOption && setupMode !== "quick") {
5066
+ if (!skipPrompts && !agentToolsOption) {
5067
+ const { defaults: detectedDefaults, detected: detectionResults } = await getDefaultAIToolSelection();
5068
+ const anyDetected = detectionResults.some((r) => r.detected);
5069
+ if (anyDetected) {
5070
+ console.log("");
5071
+ console.log(chalk20.cyan("\u{1F50D} Detected AI tools:"));
5072
+ for (const result of detectionResults) {
5073
+ if (result.detected) {
5074
+ console.log(chalk20.gray(` ${AI_TOOL_CONFIGS[result.tool].description}`));
5075
+ for (const reason of result.reasons) {
5076
+ console.log(chalk20.gray(` \u2514\u2500 ${reason}`));
5077
+ }
5078
+ }
5079
+ }
5080
+ console.log("");
5081
+ }
4916
5082
  const toolChoices = Object.entries(AI_TOOL_CONFIGS).map(([key, config]) => ({
4917
5083
  name: config.description,
4918
5084
  value: key,
4919
- checked: config.default
5085
+ checked: detectedDefaults.includes(key)
4920
5086
  }));
4921
5087
  selectedAgentTools = await checkbox({
4922
5088
  message: "Which AI tools do you use? (creates symlinks for tool-specific instruction files)",
4923
5089
  choices: toolChoices
4924
5090
  });
4925
- } else if (!agentToolsOption && setupMode === "quick") {
4926
- selectedAgentTools = ["claude", "copilot"];
4927
5091
  }
4928
5092
  const templatesDir = path17.join(cwd, ".lean-spec", "templates");
4929
5093
  try {
@@ -5129,8 +5293,8 @@ async function scaffoldExample(exampleName, customName) {
5129
5293
  const packageManager = await detectPackageManager();
5130
5294
  console.log(chalk20.gray(`Installing dependencies with ${packageManager}...`));
5131
5295
  try {
5132
- const { execSync: execSync3 } = await import('child_process');
5133
- execSync3(`${packageManager} install`, {
5296
+ const { execSync: execSync4 } = await import('child_process');
5297
+ execSync4(`${packageManager} install`, {
5134
5298
  cwd: targetPath,
5135
5299
  stdio: "inherit"
5136
5300
  });
@@ -6562,11 +6726,11 @@ async function verifyAITool(provider) {
6562
6726
  let installed = false;
6563
6727
  let version;
6564
6728
  try {
6565
- const { execSync: execSync3 } = await import('child_process');
6566
- execSync3(`which ${toolDef.cliCommand}`, { stdio: "ignore" });
6729
+ const { execSync: execSync4 } = await import('child_process');
6730
+ execSync4(`which ${toolDef.cliCommand}`, { stdio: "ignore" });
6567
6731
  installed = true;
6568
6732
  try {
6569
- const versionOutput = execSync3(toolDef.versionCmd, {
6733
+ const versionOutput = execSync4(toolDef.versionCmd, {
6570
6734
  encoding: "utf-8",
6571
6735
  stdio: ["ignore", "pipe", "ignore"]
6572
6736
  });
@@ -10833,5 +10997,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
10833
10997
  }
10834
10998
 
10835
10999
  export { agentCommand, analyzeCommand, archiveCommand, backfillCommand, boardCommand, checkCommand, compactCommand, createCommand, createMcpServer, depsCommand, examplesCommand, filesCommand, ganttCommand, initCommand, linkCommand, listCommand, mcpCommand, migrateCommand, openCommand, searchCommand, splitCommand, statsCommand, templatesCommand, timelineCommand, tokensCommand, uiCommand, unlinkCommand, updateCommand, validateCommand, viewCommand };
10836
- //# sourceMappingURL=chunk-DRNMGD4H.js.map
10837
- //# sourceMappingURL=chunk-DRNMGD4H.js.map
11000
+ //# sourceMappingURL=chunk-Q6B3LVO7.js.map
11001
+ //# sourceMappingURL=chunk-Q6B3LVO7.js.map