cc-codeconductor 0.2.6 → 0.2.8
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/README.md +18 -1
- package/dist/index.js +782 -24
- package/package.json +2 -1
- package/presets/opencode/README.md +7 -7
- package/presets/opencode/agents/architect.md +0 -1
- package/presets/opencode/agents/docs.md +0 -1
- package/presets/opencode/agents/implementer.md +0 -1
- package/presets/opencode/agents/orchestrator.md +0 -1
- package/presets/opencode/agents/repo-explorer.md +0 -1
- package/presets/opencode/agents/reviewer.md +0 -1
- package/presets/opencode/agents/task-coach.md +0 -1
- package/presets/opencode/agents/tester.md +0 -1
- package/presets/opencode/opencode.jsonc +2 -11
- package/presets/opencode/prompts/v0.2.0/docs.md +1 -1
- package/presets/opencode/prompts/v0.2.0/repo-explorer.md +1 -1
- package/presets/opencode/prompts/v0.2.0/reviewer.md +1 -1
- package/presets/opencode/prompts/v0.2.0/task-coach.md +1 -1
- package/presets/seo-hotel/commands/cc-seo-audit.md +87 -0
- package/presets/seo-hotel/skills/astro-seo/SKILL.md +245 -0
- package/presets/seo-hotel/skills/geo-readiness/SKILL.md +221 -0
- package/presets/seo-hotel/skills/off-page/SKILL.md +201 -0
- package/presets/seo-hotel/skills/schema-validator/SKILL.md +325 -0
- package/presets/seo-hotel/skills/seo-audit/SKILL.md +191 -0
- package/src/presets/manifests/agy.yml +9 -0
- package/src/presets/manifests/opencode.yml +2 -1
- package/src/presets/models/agy.yml +59 -0
- package/src/presets/models/claude.yml +3 -3
- package/src/presets/models/codex.yml +3 -3
- package/src/presets/models/cursor.yml +3 -3
- package/src/presets/models/gemini.yml +3 -3
- package/src/presets/models/opencode.yml +11 -46
package/dist/index.js
CHANGED
|
@@ -7044,7 +7044,7 @@ function getExitCode(error) {
|
|
|
7044
7044
|
// package.json
|
|
7045
7045
|
var package_default = {
|
|
7046
7046
|
name: "cc-codeconductor",
|
|
7047
|
-
version: "0.2.
|
|
7047
|
+
version: "0.2.8",
|
|
7048
7048
|
description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
|
|
7049
7049
|
keywords: [
|
|
7050
7050
|
"ai",
|
|
@@ -7073,6 +7073,7 @@ var package_default = {
|
|
|
7073
7073
|
},
|
|
7074
7074
|
type: "module",
|
|
7075
7075
|
bin: {
|
|
7076
|
+
"cc-codeconductor": "./dist/index.js",
|
|
7076
7077
|
codeconductor: "./dist/index.js"
|
|
7077
7078
|
},
|
|
7078
7079
|
scripts: {
|
|
@@ -7131,6 +7132,13 @@ async function detectProject(rootDir) {
|
|
|
7131
7132
|
runtimes.push("python");
|
|
7132
7133
|
frameworks.push("django");
|
|
7133
7134
|
}
|
|
7135
|
+
const phpSignals = await detectPhp(rootDir);
|
|
7136
|
+
if (phpSignals.length > 0) {
|
|
7137
|
+
signals.push(...phpSignals);
|
|
7138
|
+
languages.push("php");
|
|
7139
|
+
runtimes.push("php");
|
|
7140
|
+
packageManagers.push("composer");
|
|
7141
|
+
}
|
|
7134
7142
|
const astroSignals = await detectAstro(rootDir);
|
|
7135
7143
|
if (astroSignals.length > 0) {
|
|
7136
7144
|
signals.push(...astroSignals);
|
|
@@ -7217,6 +7225,22 @@ async function detectAstro(rootDir) {
|
|
|
7217
7225
|
}
|
|
7218
7226
|
return signals;
|
|
7219
7227
|
}
|
|
7228
|
+
async function detectPhp(rootDir) {
|
|
7229
|
+
const { fileExists: fileExists2 } = await Promise.resolve().then(() => (init_safety(), exports_safety));
|
|
7230
|
+
const signals = [];
|
|
7231
|
+
if (await fileExists2(rootDir, "composer.json")) {
|
|
7232
|
+
signals.push("composer.json");
|
|
7233
|
+
}
|
|
7234
|
+
try {
|
|
7235
|
+
const { readdir } = await import("node:fs/promises");
|
|
7236
|
+
const entries = await readdir(rootDir, { withFileTypes: true });
|
|
7237
|
+
const hasPhpFiles = entries.some((entry) => entry.isFile() && entry.name.endsWith(".php"));
|
|
7238
|
+
if (hasPhpFiles) {
|
|
7239
|
+
signals.push("*.php");
|
|
7240
|
+
}
|
|
7241
|
+
} catch {}
|
|
7242
|
+
return signals;
|
|
7243
|
+
}
|
|
7220
7244
|
|
|
7221
7245
|
// src/commands/detect.command.ts
|
|
7222
7246
|
async function detectCommand(options) {
|
|
@@ -11346,7 +11370,7 @@ var CodeConductorConfigSchema = exports_external.object({
|
|
|
11346
11370
|
profile: exports_external.string().optional()
|
|
11347
11371
|
}),
|
|
11348
11372
|
defaults: exports_external.object({
|
|
11349
|
-
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
|
|
11373
|
+
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor", "agy"]),
|
|
11350
11374
|
overwrite: exports_external.boolean()
|
|
11351
11375
|
}),
|
|
11352
11376
|
presets: exports_external.object({
|
|
@@ -11366,6 +11390,7 @@ var RunnerTargetSchema = exports_external.enum([
|
|
|
11366
11390
|
"codex",
|
|
11367
11391
|
"gemini",
|
|
11368
11392
|
"cursor",
|
|
11393
|
+
"agy",
|
|
11369
11394
|
"all"
|
|
11370
11395
|
]);
|
|
11371
11396
|
var InstallStrategySchema = exports_external.enum([
|
|
@@ -11383,20 +11408,23 @@ var ManifestEntrySchema = exports_external.object({
|
|
|
11383
11408
|
template: exports_external.boolean().optional()
|
|
11384
11409
|
});
|
|
11385
11410
|
var InstallManifestSchema = exports_external.object({
|
|
11386
|
-
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
|
|
11411
|
+
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor", "agy"]),
|
|
11387
11412
|
entries: exports_external.array(ManifestEntrySchema)
|
|
11388
11413
|
});
|
|
11389
11414
|
var ToolProviderNamesSchema = exports_external.record(exports_external.string(), exports_external.string());
|
|
11415
|
+
var PermissionProviderNamesSchema = exports_external.record(exports_external.string(), exports_external.string());
|
|
11390
11416
|
var ModelConfigSchema = exports_external.object({
|
|
11391
|
-
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
|
|
11417
|
+
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor", "agy"]),
|
|
11392
11418
|
agents: exports_external.record(exports_external.string(), exports_external.object({
|
|
11393
11419
|
claude: exports_external.string().optional(),
|
|
11394
11420
|
opencode: exports_external.string().optional(),
|
|
11395
11421
|
codex: exports_external.string().optional(),
|
|
11396
11422
|
gemini: exports_external.string().optional(),
|
|
11397
|
-
cursor: exports_external.string().optional()
|
|
11423
|
+
cursor: exports_external.string().optional(),
|
|
11424
|
+
agy: exports_external.string().optional()
|
|
11398
11425
|
})),
|
|
11399
|
-
tools: exports_external.record(exports_external.string(), ToolProviderNamesSchema).optional()
|
|
11426
|
+
tools: exports_external.record(exports_external.string(), ToolProviderNamesSchema).optional(),
|
|
11427
|
+
permissions: PermissionProviderNamesSchema.optional()
|
|
11400
11428
|
});
|
|
11401
11429
|
function validateCouncilSpec(data) {
|
|
11402
11430
|
return CouncilSpecSchema.parse(data);
|
|
@@ -11720,6 +11748,8 @@ function resolveAssets(target) {
|
|
|
11720
11748
|
return ["agents", "prompts/v0.2.0"];
|
|
11721
11749
|
case "cursor":
|
|
11722
11750
|
return ["agents", "prompts/v0.2.0"];
|
|
11751
|
+
case "agy":
|
|
11752
|
+
return ["agents", "prompts/v0.2.0"];
|
|
11723
11753
|
}
|
|
11724
11754
|
}
|
|
11725
11755
|
|
|
@@ -11903,7 +11933,27 @@ function getResponsibilities(agentId) {
|
|
|
11903
11933
|
devil: `- Challenge assumptions
|
|
11904
11934
|
- Find edge cases
|
|
11905
11935
|
- Identify failure modes
|
|
11906
|
-
- Stress test solutions
|
|
11936
|
+
- Stress test solutions`,
|
|
11937
|
+
"seo-auditor": `- Audit technical SEO
|
|
11938
|
+
- Check meta tags and headings
|
|
11939
|
+
- Validate crawl directives
|
|
11940
|
+
- Assess page speed signals`,
|
|
11941
|
+
"schema-validator": `- Validate Schema.org markup
|
|
11942
|
+
- Check JSON-LD syntax
|
|
11943
|
+
- Verify required properties
|
|
11944
|
+
- Suggest structured data fixes`,
|
|
11945
|
+
"geo-specialist": `- Assess AI-search readiness
|
|
11946
|
+
- Validate llms.txt
|
|
11947
|
+
- Check citable content
|
|
11948
|
+
- Review GEO optimization`,
|
|
11949
|
+
"content-strategist": `- Plan content marketing
|
|
11950
|
+
- Guide off-page SEO
|
|
11951
|
+
- Suggest backlink strategy
|
|
11952
|
+
- Review hotel copywriting`,
|
|
11953
|
+
"astro-specialist": `- Validate Astro SEO patterns
|
|
11954
|
+
- Check static generation
|
|
11955
|
+
- Review Islands Architecture
|
|
11956
|
+
- Optimize image handling`
|
|
11907
11957
|
};
|
|
11908
11958
|
return responsibilities[agentId] || "- Provide critical review";
|
|
11909
11959
|
}
|
|
@@ -12081,7 +12131,7 @@ function createCodexInstaller(spec) {
|
|
|
12081
12131
|
function generateOpenCodeFiles(spec) {
|
|
12082
12132
|
const files = [];
|
|
12083
12133
|
files.push({
|
|
12084
|
-
path: ".opencode/commands/council.md",
|
|
12134
|
+
path: ".opencode/commands/cc-council.md",
|
|
12085
12135
|
content: generateCouncilCommand(spec),
|
|
12086
12136
|
overwrite: false
|
|
12087
12137
|
});
|
|
@@ -12093,17 +12143,20 @@ function generateOpenCodeFiles(spec) {
|
|
|
12093
12143
|
for (const agent of spec.agents) {
|
|
12094
12144
|
files.push({
|
|
12095
12145
|
path: `.opencode/agents/council-${agent.id}.md`,
|
|
12096
|
-
content:
|
|
12146
|
+
content: generateOpenCodeAgentContent(agent),
|
|
12097
12147
|
overwrite: false
|
|
12098
12148
|
});
|
|
12099
12149
|
}
|
|
12100
12150
|
return files;
|
|
12101
12151
|
}
|
|
12102
12152
|
function generateCouncilCommand(spec) {
|
|
12103
|
-
return
|
|
12153
|
+
return `---
|
|
12154
|
+
description: ${yamlString(spec.description)}
|
|
12155
|
+
agent: council-lead
|
|
12156
|
+
subtask: true
|
|
12157
|
+
---
|
|
12104
12158
|
|
|
12105
|
-
|
|
12106
|
-
${spec.description}
|
|
12159
|
+
Run the CodeConductor council for multi-perspective analysis.
|
|
12107
12160
|
|
|
12108
12161
|
## Version
|
|
12109
12162
|
${spec.version}
|
|
@@ -12112,12 +12165,25 @@ ${spec.version}
|
|
|
12112
12165
|
${spec.agents.map((a) => `- ${a.role} (${a.id})`).join(`
|
|
12113
12166
|
`)}
|
|
12114
12167
|
|
|
12115
|
-
##
|
|
12116
|
-
|
|
12168
|
+
## Instructions
|
|
12169
|
+
Coordinate with the council agents and synthesize their perspectives into the configured output contract.
|
|
12117
12170
|
`;
|
|
12118
12171
|
}
|
|
12119
12172
|
function generateCouncilLead(spec) {
|
|
12120
|
-
return
|
|
12173
|
+
return `---
|
|
12174
|
+
description: ${yamlString(`${spec.description} Council lead. Coordinates council members and synthesizes recommendations.`)}
|
|
12175
|
+
mode: subagent
|
|
12176
|
+
permission:
|
|
12177
|
+
read: allow
|
|
12178
|
+
edit: deny
|
|
12179
|
+
bash: deny
|
|
12180
|
+
glob: allow
|
|
12181
|
+
grep: allow
|
|
12182
|
+
webfetch: deny
|
|
12183
|
+
websearch: deny
|
|
12184
|
+
---
|
|
12185
|
+
|
|
12186
|
+
# Council Lead Agent
|
|
12121
12187
|
|
|
12122
12188
|
## Role
|
|
12123
12189
|
Coordinates the council and synthesizes perspectives
|
|
@@ -12136,6 +12202,37 @@ ${spec.agents.map((a) => `- ${a.role}: ${a.focus.join(", ")}`).join(`
|
|
|
12136
12202
|
- Provide final recommendation
|
|
12137
12203
|
`;
|
|
12138
12204
|
}
|
|
12205
|
+
function generateOpenCodeAgentContent(agent) {
|
|
12206
|
+
return `---
|
|
12207
|
+
description: ${yamlString(`${agent.role} council agent. Focus: ${agent.focus.join(", ")}. Context: ${agent.context}. Model hint: ${agent.modelHint}.`)}
|
|
12208
|
+
mode: subagent
|
|
12209
|
+
permission:
|
|
12210
|
+
${generatePermissionBlock(agent.context)}
|
|
12211
|
+
---
|
|
12212
|
+
|
|
12213
|
+
${generateAgentContent(agent)}`;
|
|
12214
|
+
}
|
|
12215
|
+
function generatePermissionBlock(context) {
|
|
12216
|
+
if (context === "repo-readonly") {
|
|
12217
|
+
return ` read: allow
|
|
12218
|
+
edit: deny
|
|
12219
|
+
bash: deny
|
|
12220
|
+
glob: allow
|
|
12221
|
+
grep: allow
|
|
12222
|
+
webfetch: deny
|
|
12223
|
+
websearch: deny`;
|
|
12224
|
+
}
|
|
12225
|
+
return ` read: deny
|
|
12226
|
+
edit: deny
|
|
12227
|
+
bash: deny
|
|
12228
|
+
glob: deny
|
|
12229
|
+
grep: deny
|
|
12230
|
+
webfetch: deny
|
|
12231
|
+
websearch: deny`;
|
|
12232
|
+
}
|
|
12233
|
+
function yamlString(value) {
|
|
12234
|
+
return JSON.stringify(value);
|
|
12235
|
+
}
|
|
12139
12236
|
|
|
12140
12237
|
// src/adapters/opencode/opencode-installer.ts
|
|
12141
12238
|
class OpenCodeInstaller {
|
|
@@ -12319,7 +12416,7 @@ function renderTemplate(content, modelConfig, filePath) {
|
|
|
12319
12416
|
const agentModels = modelConfig.agents[agentRole];
|
|
12320
12417
|
const targetModel = agentModels[modelConfig.target];
|
|
12321
12418
|
let result2 = content.replace(/\{\{MODEL\}\}/g, targetModel ?? "").replace(/\{\{MODEL_CLAUDE\}\}/g, agentModels.claude ?? "").replace(/\{\{MODEL_OPENCODE\}\}/g, agentModels.opencode ?? "").replace(/\{\{MODEL_CODEX\}\}/g, agentModels.codex ?? "").replace(/\{\{MODEL_GEMINI\}\}/g, agentModels.gemini ?? "").replace(/\{\{MODEL_CURSOR\}\}/g, agentModels.cursor ?? "");
|
|
12322
|
-
if (modelConfig.tools) {
|
|
12419
|
+
if (modelConfig.tools || modelConfig.permissions) {
|
|
12323
12420
|
result2 = substituteToolNames(result2, modelConfig);
|
|
12324
12421
|
}
|
|
12325
12422
|
return result2;
|
|
@@ -12348,19 +12445,27 @@ function renderTemplate(content, modelConfig, filePath) {
|
|
|
12348
12445
|
}
|
|
12349
12446
|
}
|
|
12350
12447
|
}
|
|
12351
|
-
if (modelConfig.tools) {
|
|
12448
|
+
if (modelConfig.tools || modelConfig.permissions) {
|
|
12352
12449
|
result = substituteToolNames(result, modelConfig);
|
|
12353
12450
|
}
|
|
12354
12451
|
return result;
|
|
12355
12452
|
}
|
|
12356
12453
|
function substituteToolNames(content, modelConfig) {
|
|
12357
|
-
if (!modelConfig.tools)
|
|
12454
|
+
if (!modelConfig.tools && !modelConfig.permissions)
|
|
12358
12455
|
return content;
|
|
12359
12456
|
const target = modelConfig.target;
|
|
12360
12457
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
12361
12458
|
if (!fmMatch)
|
|
12362
12459
|
return content;
|
|
12363
12460
|
const frontmatter = fmMatch[1];
|
|
12461
|
+
if (target === "opencode" && modelConfig.permissions) {
|
|
12462
|
+
const updatedFrontmatter2 = frontmatter.replace(/^tools:\s*(.+)\n?/m, "");
|
|
12463
|
+
return content.replace(fmMatch[0], `---
|
|
12464
|
+
${updatedFrontmatter2}
|
|
12465
|
+
---`);
|
|
12466
|
+
}
|
|
12467
|
+
if (!modelConfig.tools)
|
|
12468
|
+
return content;
|
|
12364
12469
|
const updatedFrontmatter = frontmatter.replace(/^tools:\s*(.+)$/m, (_match, toolsLine) => {
|
|
12365
12470
|
const baseNames = toolsLine.split(",").map((t) => t.trim());
|
|
12366
12471
|
const mappedNames = baseNames.map((baseName) => {
|
|
@@ -12389,7 +12494,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
12389
12494
|
const incomingContent = isTemplate && modelConfig ? renderTemplate(content, modelConfig, srcPath) : content;
|
|
12390
12495
|
let finalContent = incomingContent;
|
|
12391
12496
|
let action = "written";
|
|
12392
|
-
if (strategy === "append"
|
|
12497
|
+
if (strategy === "append") {
|
|
12393
12498
|
let existing = "";
|
|
12394
12499
|
try {
|
|
12395
12500
|
existing = await readFile4(destPath, "utf-8");
|
|
@@ -12402,7 +12507,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
12402
12507
|
` + incomingContent;
|
|
12403
12508
|
}
|
|
12404
12509
|
action = "appended";
|
|
12405
|
-
} else if (strategy === "merge-json"
|
|
12510
|
+
} else if (strategy === "merge-json") {
|
|
12406
12511
|
let existing = {};
|
|
12407
12512
|
try {
|
|
12408
12513
|
existing = JSON.parse(await readFile4(destPath, "utf-8"));
|
|
@@ -12503,8 +12608,8 @@ async function loadCouncilPreset(projectRoot) {
|
|
|
12503
12608
|
}
|
|
12504
12609
|
|
|
12505
12610
|
// src/core/runner/runner-target.ts
|
|
12506
|
-
var RUNNER_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "all"];
|
|
12507
|
-
var INDIVIDUAL_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor"];
|
|
12611
|
+
var RUNNER_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "agy", "all"];
|
|
12612
|
+
var INDIVIDUAL_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "agy"];
|
|
12508
12613
|
function isRunnerTarget(value) {
|
|
12509
12614
|
return RUNNER_TARGETS.includes(value);
|
|
12510
12615
|
}
|
|
@@ -12660,6 +12765,638 @@ async function installPresetCommand(options) {
|
|
|
12660
12765
|
}
|
|
12661
12766
|
}
|
|
12662
12767
|
|
|
12768
|
+
// src/commands/install-lsp.command.ts
|
|
12769
|
+
import { homedir as homedir4 } from "node:os";
|
|
12770
|
+
import { resolve as resolve8 } from "node:path";
|
|
12771
|
+
|
|
12772
|
+
// src/core/lsp/lsp-config-utils.ts
|
|
12773
|
+
function getLspCommand(lspId) {
|
|
12774
|
+
switch (lspId) {
|
|
12775
|
+
case "typescript":
|
|
12776
|
+
return { command: "typescript-language-server", args: ["--stdio"] };
|
|
12777
|
+
case "php":
|
|
12778
|
+
return { command: "intelephense", args: ["--stdio"] };
|
|
12779
|
+
case "python":
|
|
12780
|
+
return { command: "pylsp", args: [] };
|
|
12781
|
+
case "kotlin":
|
|
12782
|
+
return { command: "kotlin-language-server", args: [] };
|
|
12783
|
+
default:
|
|
12784
|
+
return;
|
|
12785
|
+
}
|
|
12786
|
+
}
|
|
12787
|
+
|
|
12788
|
+
// src/adapters/agy/agy-lsp-generator.ts
|
|
12789
|
+
class AgyLspGenerator {
|
|
12790
|
+
name = "agy-lsp";
|
|
12791
|
+
target = "agy";
|
|
12792
|
+
generate(installedLsps) {
|
|
12793
|
+
const successfulLsps = installedLsps.filter((lsp) => lsp.status !== "failed");
|
|
12794
|
+
if (successfulLsps.length === 0) {
|
|
12795
|
+
return [];
|
|
12796
|
+
}
|
|
12797
|
+
const sections = [
|
|
12798
|
+
"# Agy LSP Configuration (experimental)",
|
|
12799
|
+
"# NOTE: Agy config format may change as the tool evolves",
|
|
12800
|
+
""
|
|
12801
|
+
];
|
|
12802
|
+
for (const lsp of successfulLsps) {
|
|
12803
|
+
const config = getLspCommand(lsp.lspId);
|
|
12804
|
+
if (config) {
|
|
12805
|
+
sections.push(`${lsp.lspId}:`);
|
|
12806
|
+
sections.push(` command: ${config.command}`);
|
|
12807
|
+
if (config.args.length > 0) {
|
|
12808
|
+
sections.push(` args: [${config.args.join(", ")}]`);
|
|
12809
|
+
}
|
|
12810
|
+
sections.push("");
|
|
12811
|
+
}
|
|
12812
|
+
}
|
|
12813
|
+
return [
|
|
12814
|
+
{
|
|
12815
|
+
path: ".agy/tools.yaml",
|
|
12816
|
+
content: sections.join(`
|
|
12817
|
+
`),
|
|
12818
|
+
overwrite: false
|
|
12819
|
+
}
|
|
12820
|
+
];
|
|
12821
|
+
}
|
|
12822
|
+
async isAvailable() {
|
|
12823
|
+
return true;
|
|
12824
|
+
}
|
|
12825
|
+
}
|
|
12826
|
+
function createAgyLspGenerator() {
|
|
12827
|
+
return new AgyLspGenerator;
|
|
12828
|
+
}
|
|
12829
|
+
|
|
12830
|
+
// src/adapters/claude/claude-lsp-generator.ts
|
|
12831
|
+
class ClaudeLspGenerator {
|
|
12832
|
+
name = "claude-lsp";
|
|
12833
|
+
target = "claude";
|
|
12834
|
+
generate(installedLsps) {
|
|
12835
|
+
const successfulLsps = installedLsps.filter((lsp) => lsp.status !== "failed");
|
|
12836
|
+
if (successfulLsps.length === 0) {
|
|
12837
|
+
return [];
|
|
12838
|
+
}
|
|
12839
|
+
const mcpServers = {};
|
|
12840
|
+
for (const lsp of successfulLsps) {
|
|
12841
|
+
const config = getLspCommand(lsp.lspId);
|
|
12842
|
+
if (config) {
|
|
12843
|
+
mcpServers[lsp.lspId] = { command: config.command, args: [...config.args] };
|
|
12844
|
+
}
|
|
12845
|
+
}
|
|
12846
|
+
const content = JSON.stringify({
|
|
12847
|
+
mcpServers
|
|
12848
|
+
}, null, 2);
|
|
12849
|
+
return [
|
|
12850
|
+
{
|
|
12851
|
+
path: ".claude/settings.json",
|
|
12852
|
+
content,
|
|
12853
|
+
overwrite: false
|
|
12854
|
+
}
|
|
12855
|
+
];
|
|
12856
|
+
}
|
|
12857
|
+
async isAvailable() {
|
|
12858
|
+
return true;
|
|
12859
|
+
}
|
|
12860
|
+
}
|
|
12861
|
+
function createClaudeLspGenerator() {
|
|
12862
|
+
return new ClaudeLspGenerator;
|
|
12863
|
+
}
|
|
12864
|
+
|
|
12865
|
+
// src/adapters/codex/codex-lsp-generator.ts
|
|
12866
|
+
var MCP_STARTUP_TIMEOUT_SEC = 120;
|
|
12867
|
+
|
|
12868
|
+
class CodexLspGenerator {
|
|
12869
|
+
name = "codex-lsp";
|
|
12870
|
+
target = "codex";
|
|
12871
|
+
generate(installedLsps) {
|
|
12872
|
+
const successfulLsps = installedLsps.filter((lsp) => lsp.status !== "failed");
|
|
12873
|
+
if (successfulLsps.length === 0) {
|
|
12874
|
+
return [];
|
|
12875
|
+
}
|
|
12876
|
+
const sections = ["# Codex LSP Configuration", ""];
|
|
12877
|
+
for (const lsp of successfulLsps) {
|
|
12878
|
+
const config = getLspCommand(lsp.lspId);
|
|
12879
|
+
if (config) {
|
|
12880
|
+
sections.push(`[mcp_servers.${lsp.lspId}]`);
|
|
12881
|
+
sections.push(`command = "${config.command}"`);
|
|
12882
|
+
if (config.args.length > 0) {
|
|
12883
|
+
sections.push(`args = [${config.args.map((a) => `"${a}"`).join(", ")}]`);
|
|
12884
|
+
}
|
|
12885
|
+
sections.push(`startup_timeout_sec = ${MCP_STARTUP_TIMEOUT_SEC}`);
|
|
12886
|
+
sections.push("");
|
|
12887
|
+
}
|
|
12888
|
+
}
|
|
12889
|
+
return [
|
|
12890
|
+
{
|
|
12891
|
+
path: ".codex/config.toml",
|
|
12892
|
+
content: sections.join(`
|
|
12893
|
+
`),
|
|
12894
|
+
overwrite: false
|
|
12895
|
+
}
|
|
12896
|
+
];
|
|
12897
|
+
}
|
|
12898
|
+
async isAvailable() {
|
|
12899
|
+
return true;
|
|
12900
|
+
}
|
|
12901
|
+
}
|
|
12902
|
+
function createCodexLspGenerator() {
|
|
12903
|
+
return new CodexLspGenerator;
|
|
12904
|
+
}
|
|
12905
|
+
|
|
12906
|
+
// src/adapters/cursor/cursor-lsp-generator.ts
|
|
12907
|
+
class CursorLspGenerator {
|
|
12908
|
+
name = "cursor-lsp";
|
|
12909
|
+
target = "cursor";
|
|
12910
|
+
generate(installedLsps) {
|
|
12911
|
+
const successfulLsps = installedLsps.filter((lsp) => lsp.status !== "failed");
|
|
12912
|
+
if (successfulLsps.length === 0) {
|
|
12913
|
+
return [];
|
|
12914
|
+
}
|
|
12915
|
+
const mcpServers = {};
|
|
12916
|
+
for (const lsp of successfulLsps) {
|
|
12917
|
+
const config = getLspCommand(lsp.lspId);
|
|
12918
|
+
if (config) {
|
|
12919
|
+
mcpServers[lsp.lspId] = { command: config.command, args: [...config.args] };
|
|
12920
|
+
}
|
|
12921
|
+
}
|
|
12922
|
+
const content = JSON.stringify({
|
|
12923
|
+
mcpServers
|
|
12924
|
+
}, null, 2);
|
|
12925
|
+
return [
|
|
12926
|
+
{
|
|
12927
|
+
path: ".cursor/mcp.json",
|
|
12928
|
+
content,
|
|
12929
|
+
overwrite: false
|
|
12930
|
+
}
|
|
12931
|
+
];
|
|
12932
|
+
}
|
|
12933
|
+
async isAvailable() {
|
|
12934
|
+
return true;
|
|
12935
|
+
}
|
|
12936
|
+
}
|
|
12937
|
+
function createCursorLspGenerator() {
|
|
12938
|
+
return new CursorLspGenerator;
|
|
12939
|
+
}
|
|
12940
|
+
|
|
12941
|
+
// src/adapters/gemini/gemini-lsp-generator.ts
|
|
12942
|
+
class GeminiLspGenerator {
|
|
12943
|
+
name = "gemini-lsp";
|
|
12944
|
+
target = "gemini";
|
|
12945
|
+
generate(installedLsps) {
|
|
12946
|
+
const successfulLsps = installedLsps.filter((lsp) => lsp.status !== "failed");
|
|
12947
|
+
if (successfulLsps.length === 0) {
|
|
12948
|
+
return [];
|
|
12949
|
+
}
|
|
12950
|
+
const mcpServers = {};
|
|
12951
|
+
for (const lsp of successfulLsps) {
|
|
12952
|
+
const config = getLspCommand(lsp.lspId);
|
|
12953
|
+
if (config) {
|
|
12954
|
+
mcpServers[lsp.lspId] = { command: config.command, args: [...config.args] };
|
|
12955
|
+
}
|
|
12956
|
+
}
|
|
12957
|
+
const content = JSON.stringify({
|
|
12958
|
+
mcpServers
|
|
12959
|
+
}, null, 2);
|
|
12960
|
+
return [
|
|
12961
|
+
{
|
|
12962
|
+
path: ".gemini/settings.json",
|
|
12963
|
+
content,
|
|
12964
|
+
overwrite: false
|
|
12965
|
+
}
|
|
12966
|
+
];
|
|
12967
|
+
}
|
|
12968
|
+
async isAvailable() {
|
|
12969
|
+
return true;
|
|
12970
|
+
}
|
|
12971
|
+
}
|
|
12972
|
+
function createGeminiLspGenerator() {
|
|
12973
|
+
return new GeminiLspGenerator;
|
|
12974
|
+
}
|
|
12975
|
+
|
|
12976
|
+
// src/adapters/opencode/opencode-lsp-generator.ts
|
|
12977
|
+
class OpenCodeLspGenerator {
|
|
12978
|
+
name = "opencode-lsp";
|
|
12979
|
+
target = "opencode";
|
|
12980
|
+
generate(installedLsps) {
|
|
12981
|
+
const successfulLsps = installedLsps.filter((lsp) => lsp.status !== "failed");
|
|
12982
|
+
if (successfulLsps.length === 0) {
|
|
12983
|
+
return [];
|
|
12984
|
+
}
|
|
12985
|
+
const mcp = {};
|
|
12986
|
+
for (const lsp of successfulLsps) {
|
|
12987
|
+
const config = getLspCommand(lsp.lspId);
|
|
12988
|
+
if (config) {
|
|
12989
|
+
mcp[lsp.lspId] = {
|
|
12990
|
+
type: "local",
|
|
12991
|
+
command: [config.command, ...config.args],
|
|
12992
|
+
enabled: true,
|
|
12993
|
+
timeout: 120000
|
|
12994
|
+
};
|
|
12995
|
+
}
|
|
12996
|
+
}
|
|
12997
|
+
const content = JSON.stringify({
|
|
12998
|
+
$schema: "https://opencode.ai/config.json",
|
|
12999
|
+
mcp
|
|
13000
|
+
}, null, 2);
|
|
13001
|
+
return [
|
|
13002
|
+
{
|
|
13003
|
+
path: ".opencode/opencode.json",
|
|
13004
|
+
content,
|
|
13005
|
+
overwrite: false
|
|
13006
|
+
}
|
|
13007
|
+
];
|
|
13008
|
+
}
|
|
13009
|
+
async isAvailable() {
|
|
13010
|
+
return true;
|
|
13011
|
+
}
|
|
13012
|
+
}
|
|
13013
|
+
function createOpenCodeLspGenerator() {
|
|
13014
|
+
return new OpenCodeLspGenerator;
|
|
13015
|
+
}
|
|
13016
|
+
|
|
13017
|
+
// src/core/lsp/lsp-installer.ts
|
|
13018
|
+
import { execFile } from "node:child_process";
|
|
13019
|
+
import { access as access5, mkdir as mkdir5 } from "node:fs/promises";
|
|
13020
|
+
import { homedir as homedir3 } from "node:os";
|
|
13021
|
+
import { join as join4 } from "node:path";
|
|
13022
|
+
import { promisify } from "node:util";
|
|
13023
|
+
var execFileAsync = promisify(execFile);
|
|
13024
|
+
|
|
13025
|
+
class LspInstaller {
|
|
13026
|
+
lspBinDir;
|
|
13027
|
+
constructor() {
|
|
13028
|
+
this.lspBinDir = join4(homedir3(), ".codeconductor", "lsp", "bin");
|
|
13029
|
+
}
|
|
13030
|
+
async checkInstalled(def) {
|
|
13031
|
+
try {
|
|
13032
|
+
const { stdout } = await execFileAsync("which", [def.binaryName], { timeout: 5000 });
|
|
13033
|
+
const path = stdout.trim();
|
|
13034
|
+
if (path) {
|
|
13035
|
+
const version = await this.getVersion(def);
|
|
13036
|
+
return { installed: true, version, path };
|
|
13037
|
+
}
|
|
13038
|
+
} catch {}
|
|
13039
|
+
if (def.packageManager === "npm" && def.npmDetect) {
|
|
13040
|
+
try {
|
|
13041
|
+
const { stdout } = await execFileAsync("npm", ["list", "-g", def.npmDetect, "--depth=0"], { timeout: 1e4 });
|
|
13042
|
+
if (stdout.includes(def.npmDetect)) {
|
|
13043
|
+
const version = this.parseVersionFromNpmList(stdout, def.npmDetect);
|
|
13044
|
+
return { installed: true, version };
|
|
13045
|
+
}
|
|
13046
|
+
} catch {}
|
|
13047
|
+
}
|
|
13048
|
+
if (def.packageManager === "pip" && def.pipDetect) {
|
|
13049
|
+
try {
|
|
13050
|
+
const { stdout } = await execFileAsync("pip", ["show", def.pipDetect], { timeout: 1e4 });
|
|
13051
|
+
if (stdout.includes("Version:")) {
|
|
13052
|
+
const version = this.parseVersionFromPipShow(stdout);
|
|
13053
|
+
return { installed: true, version };
|
|
13054
|
+
}
|
|
13055
|
+
} catch {}
|
|
13056
|
+
}
|
|
13057
|
+
return { installed: false };
|
|
13058
|
+
}
|
|
13059
|
+
async installLsp(def) {
|
|
13060
|
+
const status = await this.checkInstalled(def);
|
|
13061
|
+
if (status.installed) {
|
|
13062
|
+
return {
|
|
13063
|
+
lspId: def.id,
|
|
13064
|
+
status: "already-installed",
|
|
13065
|
+
version: status.version
|
|
13066
|
+
};
|
|
13067
|
+
}
|
|
13068
|
+
try {
|
|
13069
|
+
switch (def.packageManager) {
|
|
13070
|
+
case "npm":
|
|
13071
|
+
await this.installNpm(def);
|
|
13072
|
+
break;
|
|
13073
|
+
case "pip":
|
|
13074
|
+
await this.installPip(def);
|
|
13075
|
+
break;
|
|
13076
|
+
case "binary":
|
|
13077
|
+
await this.installBinary(def);
|
|
13078
|
+
break;
|
|
13079
|
+
}
|
|
13080
|
+
const newStatus = await this.checkInstalled(def);
|
|
13081
|
+
return {
|
|
13082
|
+
lspId: def.id,
|
|
13083
|
+
status: "installed",
|
|
13084
|
+
version: newStatus.version
|
|
13085
|
+
};
|
|
13086
|
+
} catch (error) {
|
|
13087
|
+
return {
|
|
13088
|
+
lspId: def.id,
|
|
13089
|
+
status: "failed",
|
|
13090
|
+
error: error instanceof Error ? error.message : String(error)
|
|
13091
|
+
};
|
|
13092
|
+
}
|
|
13093
|
+
}
|
|
13094
|
+
async installAll(lsps, options) {
|
|
13095
|
+
const results = [];
|
|
13096
|
+
for (const lsp of lsps) {
|
|
13097
|
+
if (options.dryRun) {
|
|
13098
|
+
const status = await this.checkInstalled(lsp);
|
|
13099
|
+
results.push({
|
|
13100
|
+
lspId: lsp.id,
|
|
13101
|
+
status: status.installed ? "already-installed" : "installed",
|
|
13102
|
+
version: status.version
|
|
13103
|
+
});
|
|
13104
|
+
} else {
|
|
13105
|
+
const result = await this.installLsp(lsp);
|
|
13106
|
+
results.push(result);
|
|
13107
|
+
}
|
|
13108
|
+
}
|
|
13109
|
+
return {
|
|
13110
|
+
results,
|
|
13111
|
+
allSucceeded: results.every((r) => r.status !== "failed")
|
|
13112
|
+
};
|
|
13113
|
+
}
|
|
13114
|
+
async getVersion(def) {
|
|
13115
|
+
try {
|
|
13116
|
+
const { stdout } = await execFileAsync(def.binaryName, [def.versionFlag], { timeout: 5000 });
|
|
13117
|
+
return stdout.trim().split(`
|
|
13118
|
+
`)[0];
|
|
13119
|
+
} catch {
|
|
13120
|
+
return;
|
|
13121
|
+
}
|
|
13122
|
+
}
|
|
13123
|
+
parseVersionFromNpmList(output, packageName) {
|
|
13124
|
+
const match = output.match(new RegExp(`${packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}@([\\d.]+)`));
|
|
13125
|
+
return match?.[1];
|
|
13126
|
+
}
|
|
13127
|
+
parseVersionFromPipShow(output) {
|
|
13128
|
+
const match = output.match(/Version:\s*([\d.]+)/);
|
|
13129
|
+
return match?.[1];
|
|
13130
|
+
}
|
|
13131
|
+
async installNpm(def) {
|
|
13132
|
+
try {
|
|
13133
|
+
await execFileAsync("npm", ["install", "-g", def.package], { timeout: 120000 });
|
|
13134
|
+
} catch (error) {
|
|
13135
|
+
throw new Error(`Failed to install ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13136
|
+
}
|
|
13137
|
+
}
|
|
13138
|
+
async installPip(def) {
|
|
13139
|
+
try {
|
|
13140
|
+
await execFileAsync("pip", ["install", "--user", def.package], { timeout: 120000 });
|
|
13141
|
+
} catch (error) {
|
|
13142
|
+
throw new Error(`Failed to install ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13143
|
+
}
|
|
13144
|
+
}
|
|
13145
|
+
async installBinary(def) {
|
|
13146
|
+
if (!def.binaryPlatforms) {
|
|
13147
|
+
throw new Error(`No binary platforms defined for ${def.serverName}`);
|
|
13148
|
+
}
|
|
13149
|
+
const platformKey = `${process.platform}-${process.arch}`;
|
|
13150
|
+
const binary = def.binaryPlatforms[platformKey];
|
|
13151
|
+
if (!binary) {
|
|
13152
|
+
throw new Error(`No binary available for platform: ${platformKey}`);
|
|
13153
|
+
}
|
|
13154
|
+
await mkdir5(this.lspBinDir, { recursive: true });
|
|
13155
|
+
const destPath = join4(this.lspBinDir, def.binaryName);
|
|
13156
|
+
try {
|
|
13157
|
+
await access5(destPath);
|
|
13158
|
+
return;
|
|
13159
|
+
} catch {}
|
|
13160
|
+
const { execSync } = await import("node:child_process");
|
|
13161
|
+
const isWindows = process.platform === "win32";
|
|
13162
|
+
const extractCmd = binary.url.endsWith(".zip") ? `curl -L "${binary.url}" | tar -xz -C "${this.lspBinDir}"` : `curl -L "${binary.url}" | tar -xz -C "${this.lspBinDir}"`;
|
|
13163
|
+
try {
|
|
13164
|
+
execSync(extractCmd, { timeout: 120000, stdio: "pipe" });
|
|
13165
|
+
if (!isWindows) {
|
|
13166
|
+
execSync(`chmod +x "${destPath}"`, { stdio: "pipe" });
|
|
13167
|
+
}
|
|
13168
|
+
} catch (error) {
|
|
13169
|
+
throw new Error(`Failed to download ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13170
|
+
}
|
|
13171
|
+
}
|
|
13172
|
+
}
|
|
13173
|
+
function createLspInstaller() {
|
|
13174
|
+
return new LspInstaller;
|
|
13175
|
+
}
|
|
13176
|
+
|
|
13177
|
+
// src/core/lsp/lsp-registry.ts
|
|
13178
|
+
var LSP_DEFINITIONS = [
|
|
13179
|
+
{
|
|
13180
|
+
id: "typescript",
|
|
13181
|
+
language: "typescript",
|
|
13182
|
+
serverName: "TypeScript Language Server",
|
|
13183
|
+
packageManager: "npm",
|
|
13184
|
+
package: "typescript-language-server",
|
|
13185
|
+
binaryName: "typescript-language-server",
|
|
13186
|
+
installCmd: "npm install -g typescript-language-server",
|
|
13187
|
+
versionFlag: "--version",
|
|
13188
|
+
npmDetect: "typescript-language-server"
|
|
13189
|
+
},
|
|
13190
|
+
{
|
|
13191
|
+
id: "php",
|
|
13192
|
+
language: "php",
|
|
13193
|
+
serverName: "Intelephense",
|
|
13194
|
+
packageManager: "npm",
|
|
13195
|
+
package: "@bmewburn/vscode-intelephense-client",
|
|
13196
|
+
binaryName: "intelephense",
|
|
13197
|
+
installCmd: "npm install -g @bmewburn/vscode-intelephense-client",
|
|
13198
|
+
versionFlag: "--version",
|
|
13199
|
+
npmDetect: "@bmewburn/vscode-intelephense-client"
|
|
13200
|
+
},
|
|
13201
|
+
{
|
|
13202
|
+
id: "python",
|
|
13203
|
+
language: "python",
|
|
13204
|
+
serverName: "Python LSP Server",
|
|
13205
|
+
packageManager: "pip",
|
|
13206
|
+
package: "python-lsp-server",
|
|
13207
|
+
binaryName: "pylsp",
|
|
13208
|
+
installCmd: "pip install --user python-lsp-server",
|
|
13209
|
+
versionFlag: "--version",
|
|
13210
|
+
pipDetect: "python-lsp-server"
|
|
13211
|
+
},
|
|
13212
|
+
{
|
|
13213
|
+
id: "kotlin",
|
|
13214
|
+
language: "kotlin",
|
|
13215
|
+
serverName: "Kotlin Language Server",
|
|
13216
|
+
packageManager: "binary",
|
|
13217
|
+
package: "kotlin-language-server",
|
|
13218
|
+
binaryName: "kotlin-language-server",
|
|
13219
|
+
installCmd: "Download from GitHub releases",
|
|
13220
|
+
versionFlag: "--version",
|
|
13221
|
+
binaryPlatforms: {
|
|
13222
|
+
"linux-x64": {
|
|
13223
|
+
url: "https://github.com/fwcd/kotlin-language-server/releases/latest/download/server-linux-x64.tar.gz"
|
|
13224
|
+
},
|
|
13225
|
+
"linux-arm64": {
|
|
13226
|
+
url: "https://github.com/fwcd/kotlin-language-server/releases/latest/download/server-linux-arm64.tar.gz"
|
|
13227
|
+
},
|
|
13228
|
+
"darwin-x64": {
|
|
13229
|
+
url: "https://github.com/fwcd/kotlin-language-server/releases/latest/download/server-macos-x64.tar.gz"
|
|
13230
|
+
},
|
|
13231
|
+
"darwin-arm64": {
|
|
13232
|
+
url: "https://github.com/fwcd/kotlin-language-server/releases/latest/download/server-macos-arm64.tar.gz"
|
|
13233
|
+
},
|
|
13234
|
+
"win32-x64": {
|
|
13235
|
+
url: "https://github.com/fwcd/kotlin-language-server/releases/latest/download/server-windows-x64.zip"
|
|
13236
|
+
}
|
|
13237
|
+
}
|
|
13238
|
+
}
|
|
13239
|
+
];
|
|
13240
|
+
function resolveLsps(languages) {
|
|
13241
|
+
const languageToLspId = {
|
|
13242
|
+
typescript: "typescript",
|
|
13243
|
+
javascript: "typescript",
|
|
13244
|
+
php: "php",
|
|
13245
|
+
python: "python",
|
|
13246
|
+
java: "kotlin",
|
|
13247
|
+
kotlin: "kotlin"
|
|
13248
|
+
};
|
|
13249
|
+
const resolvedIds = new Set;
|
|
13250
|
+
for (const lang of languages) {
|
|
13251
|
+
const lspId = languageToLspId[lang.toLowerCase()];
|
|
13252
|
+
if (lspId) {
|
|
13253
|
+
resolvedIds.add(lspId);
|
|
13254
|
+
}
|
|
13255
|
+
}
|
|
13256
|
+
return LSP_DEFINITIONS.filter((def) => resolvedIds.has(def.id));
|
|
13257
|
+
}
|
|
13258
|
+
|
|
13259
|
+
// src/commands/install-lsp.command.ts
|
|
13260
|
+
async function installLspCommand(options) {
|
|
13261
|
+
const { target, lang, dryRun, force, global: isGlobal, output, projectRoot } = options;
|
|
13262
|
+
const baseDir = isGlobal ? homedir4() : projectRoot;
|
|
13263
|
+
try {
|
|
13264
|
+
const runnerTarget = parseRunnerTarget(target);
|
|
13265
|
+
const targets = getIndividualTargets(runnerTarget);
|
|
13266
|
+
let languages;
|
|
13267
|
+
if (lang && lang.length > 0) {
|
|
13268
|
+
languages = lang;
|
|
13269
|
+
} else {
|
|
13270
|
+
const profile = await detectProject(projectRoot);
|
|
13271
|
+
languages = profile.languages;
|
|
13272
|
+
}
|
|
13273
|
+
if (languages.length === 0) {
|
|
13274
|
+
return {
|
|
13275
|
+
code: 1,
|
|
13276
|
+
data: {
|
|
13277
|
+
success: false,
|
|
13278
|
+
command: "install",
|
|
13279
|
+
subcommand: "lsp",
|
|
13280
|
+
errors: ["No languages detected. Use --lang to specify languages manually."]
|
|
13281
|
+
}
|
|
13282
|
+
};
|
|
13283
|
+
}
|
|
13284
|
+
const lsps = resolveLsps(languages);
|
|
13285
|
+
if (lsps.length === 0) {
|
|
13286
|
+
return {
|
|
13287
|
+
code: 1,
|
|
13288
|
+
data: {
|
|
13289
|
+
success: false,
|
|
13290
|
+
command: "install",
|
|
13291
|
+
subcommand: "lsp",
|
|
13292
|
+
errors: [`No LSP servers available for languages: ${languages.join(", ")}`]
|
|
13293
|
+
}
|
|
13294
|
+
};
|
|
13295
|
+
}
|
|
13296
|
+
const installer = createLspInstaller();
|
|
13297
|
+
const installReport = await installer.installAll(lsps, { dryRun });
|
|
13298
|
+
const writeOptions = { dryRun, force };
|
|
13299
|
+
const allConfigResults = [];
|
|
13300
|
+
for (const t of targets) {
|
|
13301
|
+
const generator = getLspConfigGenerator(t);
|
|
13302
|
+
if (!generator) {
|
|
13303
|
+
continue;
|
|
13304
|
+
}
|
|
13305
|
+
const generatedFiles = generator.generate(installReport.results);
|
|
13306
|
+
const resolvedFiles = generatedFiles.map((f) => ({
|
|
13307
|
+
...f,
|
|
13308
|
+
path: resolve8(baseDir, f.path)
|
|
13309
|
+
}));
|
|
13310
|
+
const results = await writeGeneratedFiles(resolvedFiles, writeOptions);
|
|
13311
|
+
for (const result of results) {
|
|
13312
|
+
allConfigResults.push({
|
|
13313
|
+
target: t,
|
|
13314
|
+
path: result.path,
|
|
13315
|
+
success: result.success,
|
|
13316
|
+
error: result.error
|
|
13317
|
+
});
|
|
13318
|
+
}
|
|
13319
|
+
}
|
|
13320
|
+
const errors3 = allConfigResults.filter((r) => !r.success).map((r) => `${r.path}: ${r.error}`);
|
|
13321
|
+
if (output === "json") {
|
|
13322
|
+
return {
|
|
13323
|
+
code: errors3.length > 0 ? 2 : 0,
|
|
13324
|
+
data: {
|
|
13325
|
+
success: errors3.length === 0,
|
|
13326
|
+
command: "install",
|
|
13327
|
+
subcommand: "lsp",
|
|
13328
|
+
targets,
|
|
13329
|
+
languages: [...languages],
|
|
13330
|
+
global: isGlobal,
|
|
13331
|
+
dryRun,
|
|
13332
|
+
lspResults: installReport.results,
|
|
13333
|
+
configResults: allConfigResults
|
|
13334
|
+
}
|
|
13335
|
+
};
|
|
13336
|
+
}
|
|
13337
|
+
console.log(`
|
|
13338
|
+
LSP Installation:`);
|
|
13339
|
+
for (const result of installReport.results) {
|
|
13340
|
+
const icon = result.status === "already-installed" ? "✓" : result.status === "installed" ? "+" : "✗";
|
|
13341
|
+
const version = result.version ? ` (${result.version})` : "";
|
|
13342
|
+
const error = result.error ? `: ${result.error}` : "";
|
|
13343
|
+
console.log(` ${icon} ${result.lspId}${version}${error}`);
|
|
13344
|
+
}
|
|
13345
|
+
console.log(`
|
|
13346
|
+
Config Files:`);
|
|
13347
|
+
for (const result of allConfigResults) {
|
|
13348
|
+
const icon = result.success ? "✓" : "✗";
|
|
13349
|
+
const error = result.error ? `: ${result.error}` : "";
|
|
13350
|
+
console.log(` ${icon} ${result.target}: ${result.path}${error}`);
|
|
13351
|
+
}
|
|
13352
|
+
const note = dryRun ? " (dry-run)" : "";
|
|
13353
|
+
console.log(`
|
|
13354
|
+
${installReport.results.length} LSPs processed, ${allConfigResults.length} config files${note}`);
|
|
13355
|
+
return {
|
|
13356
|
+
code: errors3.length > 0 ? 2 : 0,
|
|
13357
|
+
data: {
|
|
13358
|
+
success: errors3.length === 0,
|
|
13359
|
+
command: "install",
|
|
13360
|
+
subcommand: "lsp",
|
|
13361
|
+
targets,
|
|
13362
|
+
languages: [...languages],
|
|
13363
|
+
global: isGlobal,
|
|
13364
|
+
dryRun,
|
|
13365
|
+
lspResults: installReport.results,
|
|
13366
|
+
configResults: allConfigResults
|
|
13367
|
+
}
|
|
13368
|
+
};
|
|
13369
|
+
} catch (error) {
|
|
13370
|
+
return {
|
|
13371
|
+
code: 1,
|
|
13372
|
+
data: {
|
|
13373
|
+
success: false,
|
|
13374
|
+
command: "install",
|
|
13375
|
+
subcommand: "lsp",
|
|
13376
|
+
errors: [String(error)]
|
|
13377
|
+
}
|
|
13378
|
+
};
|
|
13379
|
+
}
|
|
13380
|
+
}
|
|
13381
|
+
function getLspConfigGenerator(target) {
|
|
13382
|
+
switch (target) {
|
|
13383
|
+
case "opencode":
|
|
13384
|
+
return createOpenCodeLspGenerator();
|
|
13385
|
+
case "claude":
|
|
13386
|
+
return createClaudeLspGenerator();
|
|
13387
|
+
case "codex":
|
|
13388
|
+
return createCodexLspGenerator();
|
|
13389
|
+
case "gemini":
|
|
13390
|
+
return createGeminiLspGenerator();
|
|
13391
|
+
case "cursor":
|
|
13392
|
+
return createCursorLspGenerator();
|
|
13393
|
+
case "agy":
|
|
13394
|
+
return createAgyLspGenerator();
|
|
13395
|
+
default:
|
|
13396
|
+
return;
|
|
13397
|
+
}
|
|
13398
|
+
}
|
|
13399
|
+
|
|
12663
13400
|
// src/commands/update.command.ts
|
|
12664
13401
|
async function updateCommand(options) {
|
|
12665
13402
|
const { dryRun, force, output, projectRoot } = options;
|
|
@@ -12832,7 +13569,12 @@ function parseArgs(args) {
|
|
|
12832
13569
|
const arg = remaining[i];
|
|
12833
13570
|
if (arg.startsWith("--")) {
|
|
12834
13571
|
const [key, value] = arg.slice(2).split("=");
|
|
12835
|
-
if (
|
|
13572
|
+
if (key === "lang") {
|
|
13573
|
+
const langValue = value !== undefined ? value : remaining[++i];
|
|
13574
|
+
if (langValue && typeof langValue === "string") {
|
|
13575
|
+
options[key] = langValue.split(",").map((s) => s.trim());
|
|
13576
|
+
}
|
|
13577
|
+
} else if (value !== undefined) {
|
|
12836
13578
|
options[key] = value;
|
|
12837
13579
|
} else if (remaining[i + 1] && !remaining[i + 1].startsWith("-")) {
|
|
12838
13580
|
options[key] = remaining[++i];
|
|
@@ -12856,6 +13598,7 @@ Commands:
|
|
|
12856
13598
|
detect Detect project stack and recommended presets
|
|
12857
13599
|
install council Install generated council spec files to runner targets
|
|
12858
13600
|
install preset Install full preset (agents, prompts, skills, commands)
|
|
13601
|
+
install lsp Install and configure LSP servers for AI coding tools
|
|
12859
13602
|
doctor Validate configuration and generated files
|
|
12860
13603
|
update Update installed presets
|
|
12861
13604
|
|
|
@@ -12866,6 +13609,7 @@ Options:
|
|
|
12866
13609
|
--force Allow overwriting existing files
|
|
12867
13610
|
--global Install to home directory (~/.claude, ~/.opencode, etc.)
|
|
12868
13611
|
--output, -o Output mode: human or json
|
|
13612
|
+
--lang Comma-separated list of languages (e.g., typescript,php,python)
|
|
12869
13613
|
|
|
12870
13614
|
Examples:
|
|
12871
13615
|
npx cc-codeconductor init
|
|
@@ -12880,6 +13624,9 @@ Examples:
|
|
|
12880
13624
|
npx cc-codeconductor install council --target claude
|
|
12881
13625
|
npx cc-codeconductor install council --target codex
|
|
12882
13626
|
npx cc-codeconductor install council --target all
|
|
13627
|
+
npx cc-codeconductor install lsp --target opencode
|
|
13628
|
+
npx cc-codeconductor install lsp --target all --lang typescript,python
|
|
13629
|
+
npx cc-codeconductor install lsp --target claude --dry-run
|
|
12883
13630
|
npx cc-codeconductor doctor
|
|
12884
13631
|
npx cc-codeconductor update --dry-run
|
|
12885
13632
|
`;
|
|
@@ -12904,7 +13651,7 @@ async function routeCommand(args, projectRoot) {
|
|
|
12904
13651
|
});
|
|
12905
13652
|
case "install": {
|
|
12906
13653
|
const isGlobal = options.global === true || options.global === "true";
|
|
12907
|
-
const VALID_TARGETS = ["opencode", "claude", "codex", "all"];
|
|
13654
|
+
const VALID_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "agy", "all"];
|
|
12908
13655
|
let resolvedSubcommand = subcommand;
|
|
12909
13656
|
let target = options.target;
|
|
12910
13657
|
if (!target && subcommand && VALID_TARGETS.includes(subcommand)) {
|
|
@@ -12912,6 +13659,17 @@ async function routeCommand(args, projectRoot) {
|
|
|
12912
13659
|
resolvedSubcommand = undefined;
|
|
12913
13660
|
}
|
|
12914
13661
|
target = target || "opencode";
|
|
13662
|
+
if (resolvedSubcommand === "lsp") {
|
|
13663
|
+
return installLspCommand({
|
|
13664
|
+
projectRoot,
|
|
13665
|
+
target,
|
|
13666
|
+
lang: options.lang,
|
|
13667
|
+
dryRun: flags.dryRun,
|
|
13668
|
+
force: flags.force,
|
|
13669
|
+
global: isGlobal,
|
|
13670
|
+
output: flags.output
|
|
13671
|
+
});
|
|
13672
|
+
}
|
|
12915
13673
|
if (resolvedSubcommand === "preset") {
|
|
12916
13674
|
return installPresetCommand({
|
|
12917
13675
|
projectRoot,
|