lean-spec 0.2.7-dev.20251127024801 → 0.2.7-dev.20251127055844
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.
|
@@ -16,7 +16,7 @@ import stripAnsi from 'strip-ansi';
|
|
|
16
16
|
import matter from 'gray-matter';
|
|
17
17
|
import yaml from 'js-yaml';
|
|
18
18
|
import dayjs3 from 'dayjs';
|
|
19
|
-
import { select, checkbox } from '@inquirer/prompts';
|
|
19
|
+
import { select, checkbox, confirm } from '@inquirer/prompts';
|
|
20
20
|
import { marked } from 'marked';
|
|
21
21
|
import { markedTerminal } from 'marked-terminal';
|
|
22
22
|
|
|
@@ -4494,6 +4494,11 @@ var AI_TOOL_CONFIGS = {
|
|
|
4494
4494
|
detection: {
|
|
4495
4495
|
commands: ["aider"],
|
|
4496
4496
|
configDirs: [".aider"]
|
|
4497
|
+
},
|
|
4498
|
+
cli: {
|
|
4499
|
+
command: "aider",
|
|
4500
|
+
promptFlag: "--message"
|
|
4501
|
+
// Aider doesn't have a simple allow-all flag, uses different interaction model
|
|
4497
4502
|
}
|
|
4498
4503
|
},
|
|
4499
4504
|
claude: {
|
|
@@ -4505,6 +4510,15 @@ var AI_TOOL_CONFIGS = {
|
|
|
4505
4510
|
commands: ["claude"],
|
|
4506
4511
|
configDirs: [".claude"],
|
|
4507
4512
|
envVars: ["ANTHROPIC_API_KEY"]
|
|
4513
|
+
},
|
|
4514
|
+
cli: {
|
|
4515
|
+
command: "claude",
|
|
4516
|
+
promptFlag: "-p",
|
|
4517
|
+
// -p is the print/non-interactive flag
|
|
4518
|
+
promptIsPositional: true,
|
|
4519
|
+
// Prompt is positional argument
|
|
4520
|
+
allowToolsFlag: "--permission-mode acceptEdits"
|
|
4521
|
+
// Auto-accept edit operations
|
|
4508
4522
|
}
|
|
4509
4523
|
},
|
|
4510
4524
|
codex: {
|
|
@@ -4527,6 +4541,11 @@ var AI_TOOL_CONFIGS = {
|
|
|
4527
4541
|
detection: {
|
|
4528
4542
|
commands: ["copilot"],
|
|
4529
4543
|
envVars: ["GITHUB_TOKEN"]
|
|
4544
|
+
},
|
|
4545
|
+
cli: {
|
|
4546
|
+
command: "copilot",
|
|
4547
|
+
promptFlag: "-p",
|
|
4548
|
+
allowToolsFlag: "--allow-all-tools"
|
|
4530
4549
|
}
|
|
4531
4550
|
},
|
|
4532
4551
|
cursor: {
|
|
@@ -4557,6 +4576,13 @@ var AI_TOOL_CONFIGS = {
|
|
|
4557
4576
|
commands: ["gemini"],
|
|
4558
4577
|
configDirs: [".gemini"],
|
|
4559
4578
|
envVars: ["GOOGLE_API_KEY", "GEMINI_API_KEY"]
|
|
4579
|
+
},
|
|
4580
|
+
cli: {
|
|
4581
|
+
command: "gemini",
|
|
4582
|
+
promptFlag: "-p",
|
|
4583
|
+
// Note: deprecated but still works
|
|
4584
|
+
allowToolsFlag: "-y"
|
|
4585
|
+
// YOLO mode
|
|
4560
4586
|
}
|
|
4561
4587
|
},
|
|
4562
4588
|
opencode: {
|
|
@@ -4865,6 +4891,122 @@ async function getProjectName2(cwd) {
|
|
|
4865
4891
|
}
|
|
4866
4892
|
return path17.basename(cwd);
|
|
4867
4893
|
}
|
|
4894
|
+
function buildMergeCommand(cli, promptPath) {
|
|
4895
|
+
const prompt = `Follow the instructions in ${promptPath} to consolidate AGENTS.md. Read the prompt file, then edit AGENTS.md with the merged content.`;
|
|
4896
|
+
const args = [];
|
|
4897
|
+
if (cli.promptIsPositional) {
|
|
4898
|
+
args.push(prompt);
|
|
4899
|
+
if (cli.promptFlag) {
|
|
4900
|
+
args.push(cli.promptFlag);
|
|
4901
|
+
}
|
|
4902
|
+
} else {
|
|
4903
|
+
args.push(cli.promptFlag);
|
|
4904
|
+
args.push(prompt);
|
|
4905
|
+
}
|
|
4906
|
+
if (cli.allowToolsFlag) {
|
|
4907
|
+
const flagParts = cli.allowToolsFlag.split(" ");
|
|
4908
|
+
args.push(...flagParts);
|
|
4909
|
+
}
|
|
4910
|
+
return { command: cli.command, args };
|
|
4911
|
+
}
|
|
4912
|
+
async function executeMergeWithAI(cwd, promptPath, tool, timeoutMs = 12e4) {
|
|
4913
|
+
const config = AI_TOOL_CONFIGS[tool];
|
|
4914
|
+
if (!config.cli) {
|
|
4915
|
+
return {
|
|
4916
|
+
success: false,
|
|
4917
|
+
error: `Tool ${tool} does not have CLI configuration`
|
|
4918
|
+
};
|
|
4919
|
+
}
|
|
4920
|
+
const { command, args } = buildMergeCommand(config.cli, promptPath);
|
|
4921
|
+
const quotedArgs = args.map((arg) => {
|
|
4922
|
+
if (arg.includes(" ") || arg.includes('"') || arg.includes("'")) {
|
|
4923
|
+
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
4924
|
+
}
|
|
4925
|
+
return arg;
|
|
4926
|
+
});
|
|
4927
|
+
const fullCommand = `${command} ${quotedArgs.join(" ")}`;
|
|
4928
|
+
return new Promise((resolve3) => {
|
|
4929
|
+
let output = "";
|
|
4930
|
+
let errorOutput = "";
|
|
4931
|
+
let timedOut = false;
|
|
4932
|
+
const child = spawn(fullCommand, [], {
|
|
4933
|
+
cwd,
|
|
4934
|
+
shell: true,
|
|
4935
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
4936
|
+
});
|
|
4937
|
+
const timeout = setTimeout(() => {
|
|
4938
|
+
timedOut = true;
|
|
4939
|
+
child.kill("SIGTERM");
|
|
4940
|
+
}, timeoutMs);
|
|
4941
|
+
child.stdout?.on("data", (data) => {
|
|
4942
|
+
output += data.toString();
|
|
4943
|
+
process.stdout.write(data);
|
|
4944
|
+
});
|
|
4945
|
+
child.stderr?.on("data", (data) => {
|
|
4946
|
+
errorOutput += data.toString();
|
|
4947
|
+
process.stderr.write(data);
|
|
4948
|
+
});
|
|
4949
|
+
child.on("close", (code) => {
|
|
4950
|
+
clearTimeout(timeout);
|
|
4951
|
+
if (timedOut) {
|
|
4952
|
+
resolve3({
|
|
4953
|
+
success: false,
|
|
4954
|
+
output,
|
|
4955
|
+
error: "Command timed out",
|
|
4956
|
+
timedOut: true
|
|
4957
|
+
});
|
|
4958
|
+
} else if (code === 0) {
|
|
4959
|
+
resolve3({
|
|
4960
|
+
success: true,
|
|
4961
|
+
output
|
|
4962
|
+
});
|
|
4963
|
+
} else {
|
|
4964
|
+
resolve3({
|
|
4965
|
+
success: false,
|
|
4966
|
+
output,
|
|
4967
|
+
error: errorOutput || `Process exited with code ${code}`
|
|
4968
|
+
});
|
|
4969
|
+
}
|
|
4970
|
+
});
|
|
4971
|
+
child.on("error", (err) => {
|
|
4972
|
+
clearTimeout(timeout);
|
|
4973
|
+
resolve3({
|
|
4974
|
+
success: false,
|
|
4975
|
+
error: err.message
|
|
4976
|
+
});
|
|
4977
|
+
});
|
|
4978
|
+
});
|
|
4979
|
+
}
|
|
4980
|
+
async function getCliCapableDetectedTools() {
|
|
4981
|
+
const detectionResults = await detectInstalledAITools();
|
|
4982
|
+
const priorityOrder = ["copilot", "gemini", "claude", "aider", "codex"];
|
|
4983
|
+
const detected = detectionResults.filter((r) => r.detected && AI_TOOL_CONFIGS[r.tool].cli).map((r) => ({
|
|
4984
|
+
tool: r.tool,
|
|
4985
|
+
config: AI_TOOL_CONFIGS[r.tool],
|
|
4986
|
+
reasons: r.reasons
|
|
4987
|
+
}));
|
|
4988
|
+
detected.sort((a, b) => {
|
|
4989
|
+
const aIndex = priorityOrder.indexOf(a.tool);
|
|
4990
|
+
const bIndex = priorityOrder.indexOf(b.tool);
|
|
4991
|
+
const aPriority = aIndex === -1 ? 999 : aIndex;
|
|
4992
|
+
const bPriority = bIndex === -1 ? 999 : bIndex;
|
|
4993
|
+
return aPriority - bPriority;
|
|
4994
|
+
});
|
|
4995
|
+
return detected;
|
|
4996
|
+
}
|
|
4997
|
+
function getDisplayCommand(tool, promptPath) {
|
|
4998
|
+
const config = AI_TOOL_CONFIGS[tool];
|
|
4999
|
+
if (!config.cli) return "";
|
|
5000
|
+
const { command, args } = buildMergeCommand(config.cli, promptPath);
|
|
5001
|
+
const displayArgs = args.map((arg, index) => {
|
|
5002
|
+
const isPromptArg = config.cli.promptIsPositional ? index === 0 : index === 1;
|
|
5003
|
+
if (isPromptArg && arg.includes(" ")) {
|
|
5004
|
+
return `"${arg}"`;
|
|
5005
|
+
}
|
|
5006
|
+
return arg;
|
|
5007
|
+
});
|
|
5008
|
+
return `${command} ${displayArgs.join(" ")}`;
|
|
5009
|
+
}
|
|
4868
5010
|
|
|
4869
5011
|
// src/utils/examples.ts
|
|
4870
5012
|
var EXAMPLES = {
|
|
@@ -4916,6 +5058,55 @@ function exampleExists(name) {
|
|
|
4916
5058
|
var __dirname = path17.dirname(fileURLToPath(import.meta.url));
|
|
4917
5059
|
var TEMPLATES_DIR = path17.join(__dirname, "..", "templates");
|
|
4918
5060
|
var EXAMPLES_DIR = path17.join(TEMPLATES_DIR, "examples");
|
|
5061
|
+
async function attemptAutoMerge(cwd, promptPath, autoExecute) {
|
|
5062
|
+
const cliTools = await getCliCapableDetectedTools();
|
|
5063
|
+
if (cliTools.length === 0) {
|
|
5064
|
+
return false;
|
|
5065
|
+
}
|
|
5066
|
+
const tool = cliTools[0];
|
|
5067
|
+
const displayCmd = getDisplayCommand(tool.tool, promptPath);
|
|
5068
|
+
console.log("");
|
|
5069
|
+
console.log(chalk20.cyan(`\u{1F50D} Detected AI CLI: ${tool.config.description}`));
|
|
5070
|
+
for (const reason of tool.reasons) {
|
|
5071
|
+
console.log(chalk20.gray(` \u2514\u2500 ${reason}`));
|
|
5072
|
+
}
|
|
5073
|
+
console.log("");
|
|
5074
|
+
console.log(chalk20.gray(`Command: ${displayCmd}`));
|
|
5075
|
+
console.log("");
|
|
5076
|
+
let shouldExecute = autoExecute;
|
|
5077
|
+
if (!autoExecute) {
|
|
5078
|
+
shouldExecute = await confirm({
|
|
5079
|
+
message: "Run merge automatically using detected AI CLI?",
|
|
5080
|
+
default: true
|
|
5081
|
+
});
|
|
5082
|
+
}
|
|
5083
|
+
if (!shouldExecute) {
|
|
5084
|
+
console.log(chalk20.gray("Skipping auto-merge. Run the command above manually to merge."));
|
|
5085
|
+
return false;
|
|
5086
|
+
}
|
|
5087
|
+
console.log("");
|
|
5088
|
+
console.log(chalk20.cyan("\u{1F916} Running AI-assisted merge..."));
|
|
5089
|
+
console.log(chalk20.gray(" (This may take a moment)"));
|
|
5090
|
+
console.log("");
|
|
5091
|
+
const result = await executeMergeWithAI(cwd, promptPath, tool.tool);
|
|
5092
|
+
if (result.success) {
|
|
5093
|
+
console.log("");
|
|
5094
|
+
console.log(chalk20.green("\u2713 AGENTS.md merged successfully!"));
|
|
5095
|
+
console.log(chalk20.gray(" Review changes: git diff AGENTS.md"));
|
|
5096
|
+
return true;
|
|
5097
|
+
} else if (result.timedOut) {
|
|
5098
|
+
console.log("");
|
|
5099
|
+
console.log(chalk20.yellow("\u26A0 Merge timed out. Try running the command manually:"));
|
|
5100
|
+
console.log(chalk20.gray(` ${displayCmd}`));
|
|
5101
|
+
return false;
|
|
5102
|
+
} else {
|
|
5103
|
+
console.log("");
|
|
5104
|
+
console.log(chalk20.yellow(`\u26A0 Auto-merge encountered an issue: ${result.error}`));
|
|
5105
|
+
console.log(chalk20.gray(" Try running the command manually:"));
|
|
5106
|
+
console.log(chalk20.gray(` ${displayCmd}`));
|
|
5107
|
+
return false;
|
|
5108
|
+
}
|
|
5109
|
+
}
|
|
4919
5110
|
function initCommand() {
|
|
4920
5111
|
return new Command("init").description("Initialize LeanSpec in current directory").option("-y, --yes", "Skip prompts and use defaults (quick start with standard template)").option("--template <name>", "Use specific template (standard or detailed)").option("--example [name]", "Scaffold an example project for tutorials (interactive if no name provided)").option("--name <dirname>", "Custom directory name for example project").option("--list", "List available example projects").option("--agent-tools <tools>", 'AI tools to create symlinks for (comma-separated: claude,gemini,copilot or "all" or "none")').action(async (options) => {
|
|
4921
5112
|
if (options.list) {
|
|
@@ -5129,6 +5320,7 @@ async function initProject(skipPrompts = false, templateOption, agentToolsOption
|
|
|
5129
5320
|
console.log(chalk20.green("\u2713 Created .lean-spec/config.json"));
|
|
5130
5321
|
const existingFiles = await detectExistingSystemPrompts(cwd);
|
|
5131
5322
|
let skipFiles = [];
|
|
5323
|
+
let mergeCompleted = false;
|
|
5132
5324
|
if (existingFiles.length > 0) {
|
|
5133
5325
|
console.log("");
|
|
5134
5326
|
console.log(chalk20.yellow(`Found existing: ${existingFiles.join(", ")}`));
|
|
@@ -5136,6 +5328,13 @@ async function initProject(skipPrompts = false, templateOption, agentToolsOption
|
|
|
5136
5328
|
console.log(chalk20.gray("Using AI-Assisted Merge for existing AGENTS.md"));
|
|
5137
5329
|
const projectName2 = await getProjectName2(cwd);
|
|
5138
5330
|
await handleExistingFiles("merge-ai", existingFiles, templateDir, cwd, { project_name: projectName2 });
|
|
5331
|
+
const promptPath = path17.join(cwd, ".lean-spec", "MERGE-AGENTS-PROMPT.md");
|
|
5332
|
+
mergeCompleted = await attemptAutoMerge(
|
|
5333
|
+
cwd,
|
|
5334
|
+
promptPath,
|
|
5335
|
+
true
|
|
5336
|
+
/* skipPrompts */
|
|
5337
|
+
);
|
|
5139
5338
|
} else {
|
|
5140
5339
|
const action = await select({
|
|
5141
5340
|
message: "How would you like to handle existing AGENTS.md?",
|
|
@@ -5166,11 +5365,19 @@ async function initProject(skipPrompts = false, templateOption, agentToolsOption
|
|
|
5166
5365
|
await handleExistingFiles(action, existingFiles, templateDir, cwd, { project_name: projectName2 });
|
|
5167
5366
|
if (action === "skip") {
|
|
5168
5367
|
skipFiles = existingFiles;
|
|
5368
|
+
} else if (action === "merge-ai") {
|
|
5369
|
+
const promptPath = path17.join(cwd, ".lean-spec", "MERGE-AGENTS-PROMPT.md");
|
|
5370
|
+
mergeCompleted = await attemptAutoMerge(
|
|
5371
|
+
cwd,
|
|
5372
|
+
promptPath,
|
|
5373
|
+
false
|
|
5374
|
+
/* skipPrompts */
|
|
5375
|
+
);
|
|
5169
5376
|
}
|
|
5170
5377
|
}
|
|
5171
5378
|
}
|
|
5172
5379
|
const projectName = await getProjectName2(cwd);
|
|
5173
|
-
if (!skipFiles.includes("AGENTS.md")) {
|
|
5380
|
+
if (!skipFiles.includes("AGENTS.md") && !mergeCompleted) {
|
|
5174
5381
|
const agentsSourcePath = path17.join(templateDir, "AGENTS.md");
|
|
5175
5382
|
const agentsTargetPath = path17.join(cwd, "AGENTS.md");
|
|
5176
5383
|
try {
|
|
@@ -10997,5 +11204,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
10997
11204
|
}
|
|
10998
11205
|
|
|
10999
11206
|
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 };
|
|
11000
|
-
//# sourceMappingURL=chunk-
|
|
11001
|
-
//# sourceMappingURL=chunk-
|
|
11207
|
+
//# sourceMappingURL=chunk-FTKNRIOE.js.map
|
|
11208
|
+
//# sourceMappingURL=chunk-FTKNRIOE.js.map
|