lean-spec 0.2.7-dev.20251126090633 → 0.2.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.
@@ -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) {
@@ -4913,10 +5064,25 @@ async function initProject(skipPrompts = false, templateOption, agentToolsOption
4913
5064
  templateConfig.structure.prefix = "";
4914
5065
  }
4915
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)",
@@ -5127,8 +5293,8 @@ async function scaffoldExample(exampleName, customName) {
5127
5293
  const packageManager = await detectPackageManager();
5128
5294
  console.log(chalk20.gray(`Installing dependencies with ${packageManager}...`));
5129
5295
  try {
5130
- const { execSync: execSync3 } = await import('child_process');
5131
- execSync3(`${packageManager} install`, {
5296
+ const { execSync: execSync4 } = await import('child_process');
5297
+ execSync4(`${packageManager} install`, {
5132
5298
  cwd: targetPath,
5133
5299
  stdio: "inherit"
5134
5300
  });
@@ -6560,11 +6726,11 @@ async function verifyAITool(provider) {
6560
6726
  let installed = false;
6561
6727
  let version;
6562
6728
  try {
6563
- const { execSync: execSync3 } = await import('child_process');
6564
- execSync3(`which ${toolDef.cliCommand}`, { stdio: "ignore" });
6729
+ const { execSync: execSync4 } = await import('child_process');
6730
+ execSync4(`which ${toolDef.cliCommand}`, { stdio: "ignore" });
6565
6731
  installed = true;
6566
6732
  try {
6567
- const versionOutput = execSync3(toolDef.versionCmd, {
6733
+ const versionOutput = execSync4(toolDef.versionCmd, {
6568
6734
  encoding: "utf-8",
6569
6735
  stdio: ["ignore", "pipe", "ignore"]
6570
6736
  });
@@ -10831,5 +10997,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
10831
10997
  }
10832
10998
 
10833
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 };
10834
- //# sourceMappingURL=chunk-GSBLCPXH.js.map
10835
- //# sourceMappingURL=chunk-GSBLCPXH.js.map
11000
+ //# sourceMappingURL=chunk-Q6B3LVO7.js.map
11001
+ //# sourceMappingURL=chunk-Q6B3LVO7.js.map