cc-codeconductor 0.2.7 → 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 +781 -24
- package/package.json +1 -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",
|
|
@@ -7132,6 +7132,13 @@ async function detectProject(rootDir) {
|
|
|
7132
7132
|
runtimes.push("python");
|
|
7133
7133
|
frameworks.push("django");
|
|
7134
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
|
+
}
|
|
7135
7142
|
const astroSignals = await detectAstro(rootDir);
|
|
7136
7143
|
if (astroSignals.length > 0) {
|
|
7137
7144
|
signals.push(...astroSignals);
|
|
@@ -7218,6 +7225,22 @@ async function detectAstro(rootDir) {
|
|
|
7218
7225
|
}
|
|
7219
7226
|
return signals;
|
|
7220
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
|
+
}
|
|
7221
7244
|
|
|
7222
7245
|
// src/commands/detect.command.ts
|
|
7223
7246
|
async function detectCommand(options) {
|
|
@@ -11347,7 +11370,7 @@ var CodeConductorConfigSchema = exports_external.object({
|
|
|
11347
11370
|
profile: exports_external.string().optional()
|
|
11348
11371
|
}),
|
|
11349
11372
|
defaults: exports_external.object({
|
|
11350
|
-
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
|
|
11373
|
+
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor", "agy"]),
|
|
11351
11374
|
overwrite: exports_external.boolean()
|
|
11352
11375
|
}),
|
|
11353
11376
|
presets: exports_external.object({
|
|
@@ -11367,6 +11390,7 @@ var RunnerTargetSchema = exports_external.enum([
|
|
|
11367
11390
|
"codex",
|
|
11368
11391
|
"gemini",
|
|
11369
11392
|
"cursor",
|
|
11393
|
+
"agy",
|
|
11370
11394
|
"all"
|
|
11371
11395
|
]);
|
|
11372
11396
|
var InstallStrategySchema = exports_external.enum([
|
|
@@ -11384,20 +11408,23 @@ var ManifestEntrySchema = exports_external.object({
|
|
|
11384
11408
|
template: exports_external.boolean().optional()
|
|
11385
11409
|
});
|
|
11386
11410
|
var InstallManifestSchema = exports_external.object({
|
|
11387
|
-
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
|
|
11411
|
+
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor", "agy"]),
|
|
11388
11412
|
entries: exports_external.array(ManifestEntrySchema)
|
|
11389
11413
|
});
|
|
11390
11414
|
var ToolProviderNamesSchema = exports_external.record(exports_external.string(), exports_external.string());
|
|
11415
|
+
var PermissionProviderNamesSchema = exports_external.record(exports_external.string(), exports_external.string());
|
|
11391
11416
|
var ModelConfigSchema = exports_external.object({
|
|
11392
|
-
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
|
|
11417
|
+
target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor", "agy"]),
|
|
11393
11418
|
agents: exports_external.record(exports_external.string(), exports_external.object({
|
|
11394
11419
|
claude: exports_external.string().optional(),
|
|
11395
11420
|
opencode: exports_external.string().optional(),
|
|
11396
11421
|
codex: exports_external.string().optional(),
|
|
11397
11422
|
gemini: exports_external.string().optional(),
|
|
11398
|
-
cursor: exports_external.string().optional()
|
|
11423
|
+
cursor: exports_external.string().optional(),
|
|
11424
|
+
agy: exports_external.string().optional()
|
|
11399
11425
|
})),
|
|
11400
|
-
tools: exports_external.record(exports_external.string(), ToolProviderNamesSchema).optional()
|
|
11426
|
+
tools: exports_external.record(exports_external.string(), ToolProviderNamesSchema).optional(),
|
|
11427
|
+
permissions: PermissionProviderNamesSchema.optional()
|
|
11401
11428
|
});
|
|
11402
11429
|
function validateCouncilSpec(data) {
|
|
11403
11430
|
return CouncilSpecSchema.parse(data);
|
|
@@ -11721,6 +11748,8 @@ function resolveAssets(target) {
|
|
|
11721
11748
|
return ["agents", "prompts/v0.2.0"];
|
|
11722
11749
|
case "cursor":
|
|
11723
11750
|
return ["agents", "prompts/v0.2.0"];
|
|
11751
|
+
case "agy":
|
|
11752
|
+
return ["agents", "prompts/v0.2.0"];
|
|
11724
11753
|
}
|
|
11725
11754
|
}
|
|
11726
11755
|
|
|
@@ -11904,7 +11933,27 @@ function getResponsibilities(agentId) {
|
|
|
11904
11933
|
devil: `- Challenge assumptions
|
|
11905
11934
|
- Find edge cases
|
|
11906
11935
|
- Identify failure modes
|
|
11907
|
-
- 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`
|
|
11908
11957
|
};
|
|
11909
11958
|
return responsibilities[agentId] || "- Provide critical review";
|
|
11910
11959
|
}
|
|
@@ -12082,7 +12131,7 @@ function createCodexInstaller(spec) {
|
|
|
12082
12131
|
function generateOpenCodeFiles(spec) {
|
|
12083
12132
|
const files = [];
|
|
12084
12133
|
files.push({
|
|
12085
|
-
path: ".opencode/commands/council.md",
|
|
12134
|
+
path: ".opencode/commands/cc-council.md",
|
|
12086
12135
|
content: generateCouncilCommand(spec),
|
|
12087
12136
|
overwrite: false
|
|
12088
12137
|
});
|
|
@@ -12094,17 +12143,20 @@ function generateOpenCodeFiles(spec) {
|
|
|
12094
12143
|
for (const agent of spec.agents) {
|
|
12095
12144
|
files.push({
|
|
12096
12145
|
path: `.opencode/agents/council-${agent.id}.md`,
|
|
12097
|
-
content:
|
|
12146
|
+
content: generateOpenCodeAgentContent(agent),
|
|
12098
12147
|
overwrite: false
|
|
12099
12148
|
});
|
|
12100
12149
|
}
|
|
12101
12150
|
return files;
|
|
12102
12151
|
}
|
|
12103
12152
|
function generateCouncilCommand(spec) {
|
|
12104
|
-
return
|
|
12153
|
+
return `---
|
|
12154
|
+
description: ${yamlString(spec.description)}
|
|
12155
|
+
agent: council-lead
|
|
12156
|
+
subtask: true
|
|
12157
|
+
---
|
|
12105
12158
|
|
|
12106
|
-
|
|
12107
|
-
${spec.description}
|
|
12159
|
+
Run the CodeConductor council for multi-perspective analysis.
|
|
12108
12160
|
|
|
12109
12161
|
## Version
|
|
12110
12162
|
${spec.version}
|
|
@@ -12113,12 +12165,25 @@ ${spec.version}
|
|
|
12113
12165
|
${spec.agents.map((a) => `- ${a.role} (${a.id})`).join(`
|
|
12114
12166
|
`)}
|
|
12115
12167
|
|
|
12116
|
-
##
|
|
12117
|
-
|
|
12168
|
+
## Instructions
|
|
12169
|
+
Coordinate with the council agents and synthesize their perspectives into the configured output contract.
|
|
12118
12170
|
`;
|
|
12119
12171
|
}
|
|
12120
12172
|
function generateCouncilLead(spec) {
|
|
12121
|
-
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
|
|
12122
12187
|
|
|
12123
12188
|
## Role
|
|
12124
12189
|
Coordinates the council and synthesizes perspectives
|
|
@@ -12137,6 +12202,37 @@ ${spec.agents.map((a) => `- ${a.role}: ${a.focus.join(", ")}`).join(`
|
|
|
12137
12202
|
- Provide final recommendation
|
|
12138
12203
|
`;
|
|
12139
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
|
+
}
|
|
12140
12236
|
|
|
12141
12237
|
// src/adapters/opencode/opencode-installer.ts
|
|
12142
12238
|
class OpenCodeInstaller {
|
|
@@ -12320,7 +12416,7 @@ function renderTemplate(content, modelConfig, filePath) {
|
|
|
12320
12416
|
const agentModels = modelConfig.agents[agentRole];
|
|
12321
12417
|
const targetModel = agentModels[modelConfig.target];
|
|
12322
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 ?? "");
|
|
12323
|
-
if (modelConfig.tools) {
|
|
12419
|
+
if (modelConfig.tools || modelConfig.permissions) {
|
|
12324
12420
|
result2 = substituteToolNames(result2, modelConfig);
|
|
12325
12421
|
}
|
|
12326
12422
|
return result2;
|
|
@@ -12349,19 +12445,27 @@ function renderTemplate(content, modelConfig, filePath) {
|
|
|
12349
12445
|
}
|
|
12350
12446
|
}
|
|
12351
12447
|
}
|
|
12352
|
-
if (modelConfig.tools) {
|
|
12448
|
+
if (modelConfig.tools || modelConfig.permissions) {
|
|
12353
12449
|
result = substituteToolNames(result, modelConfig);
|
|
12354
12450
|
}
|
|
12355
12451
|
return result;
|
|
12356
12452
|
}
|
|
12357
12453
|
function substituteToolNames(content, modelConfig) {
|
|
12358
|
-
if (!modelConfig.tools)
|
|
12454
|
+
if (!modelConfig.tools && !modelConfig.permissions)
|
|
12359
12455
|
return content;
|
|
12360
12456
|
const target = modelConfig.target;
|
|
12361
12457
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
12362
12458
|
if (!fmMatch)
|
|
12363
12459
|
return content;
|
|
12364
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;
|
|
12365
12469
|
const updatedFrontmatter = frontmatter.replace(/^tools:\s*(.+)$/m, (_match, toolsLine) => {
|
|
12366
12470
|
const baseNames = toolsLine.split(",").map((t) => t.trim());
|
|
12367
12471
|
const mappedNames = baseNames.map((baseName) => {
|
|
@@ -12390,7 +12494,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
12390
12494
|
const incomingContent = isTemplate && modelConfig ? renderTemplate(content, modelConfig, srcPath) : content;
|
|
12391
12495
|
let finalContent = incomingContent;
|
|
12392
12496
|
let action = "written";
|
|
12393
|
-
if (strategy === "append"
|
|
12497
|
+
if (strategy === "append") {
|
|
12394
12498
|
let existing = "";
|
|
12395
12499
|
try {
|
|
12396
12500
|
existing = await readFile4(destPath, "utf-8");
|
|
@@ -12403,7 +12507,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
|
|
|
12403
12507
|
` + incomingContent;
|
|
12404
12508
|
}
|
|
12405
12509
|
action = "appended";
|
|
12406
|
-
} else if (strategy === "merge-json"
|
|
12510
|
+
} else if (strategy === "merge-json") {
|
|
12407
12511
|
let existing = {};
|
|
12408
12512
|
try {
|
|
12409
12513
|
existing = JSON.parse(await readFile4(destPath, "utf-8"));
|
|
@@ -12504,8 +12608,8 @@ async function loadCouncilPreset(projectRoot) {
|
|
|
12504
12608
|
}
|
|
12505
12609
|
|
|
12506
12610
|
// src/core/runner/runner-target.ts
|
|
12507
|
-
var RUNNER_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "all"];
|
|
12508
|
-
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"];
|
|
12509
12613
|
function isRunnerTarget(value) {
|
|
12510
12614
|
return RUNNER_TARGETS.includes(value);
|
|
12511
12615
|
}
|
|
@@ -12661,6 +12765,638 @@ async function installPresetCommand(options) {
|
|
|
12661
12765
|
}
|
|
12662
12766
|
}
|
|
12663
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
|
+
|
|
12664
13400
|
// src/commands/update.command.ts
|
|
12665
13401
|
async function updateCommand(options) {
|
|
12666
13402
|
const { dryRun, force, output, projectRoot } = options;
|
|
@@ -12833,7 +13569,12 @@ function parseArgs(args) {
|
|
|
12833
13569
|
const arg = remaining[i];
|
|
12834
13570
|
if (arg.startsWith("--")) {
|
|
12835
13571
|
const [key, value] = arg.slice(2).split("=");
|
|
12836
|
-
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) {
|
|
12837
13578
|
options[key] = value;
|
|
12838
13579
|
} else if (remaining[i + 1] && !remaining[i + 1].startsWith("-")) {
|
|
12839
13580
|
options[key] = remaining[++i];
|
|
@@ -12857,6 +13598,7 @@ Commands:
|
|
|
12857
13598
|
detect Detect project stack and recommended presets
|
|
12858
13599
|
install council Install generated council spec files to runner targets
|
|
12859
13600
|
install preset Install full preset (agents, prompts, skills, commands)
|
|
13601
|
+
install lsp Install and configure LSP servers for AI coding tools
|
|
12860
13602
|
doctor Validate configuration and generated files
|
|
12861
13603
|
update Update installed presets
|
|
12862
13604
|
|
|
@@ -12867,6 +13609,7 @@ Options:
|
|
|
12867
13609
|
--force Allow overwriting existing files
|
|
12868
13610
|
--global Install to home directory (~/.claude, ~/.opencode, etc.)
|
|
12869
13611
|
--output, -o Output mode: human or json
|
|
13612
|
+
--lang Comma-separated list of languages (e.g., typescript,php,python)
|
|
12870
13613
|
|
|
12871
13614
|
Examples:
|
|
12872
13615
|
npx cc-codeconductor init
|
|
@@ -12881,6 +13624,9 @@ Examples:
|
|
|
12881
13624
|
npx cc-codeconductor install council --target claude
|
|
12882
13625
|
npx cc-codeconductor install council --target codex
|
|
12883
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
|
|
12884
13630
|
npx cc-codeconductor doctor
|
|
12885
13631
|
npx cc-codeconductor update --dry-run
|
|
12886
13632
|
`;
|
|
@@ -12905,7 +13651,7 @@ async function routeCommand(args, projectRoot) {
|
|
|
12905
13651
|
});
|
|
12906
13652
|
case "install": {
|
|
12907
13653
|
const isGlobal = options.global === true || options.global === "true";
|
|
12908
|
-
const VALID_TARGETS = ["opencode", "claude", "codex", "all"];
|
|
13654
|
+
const VALID_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "agy", "all"];
|
|
12909
13655
|
let resolvedSubcommand = subcommand;
|
|
12910
13656
|
let target = options.target;
|
|
12911
13657
|
if (!target && subcommand && VALID_TARGETS.includes(subcommand)) {
|
|
@@ -12913,6 +13659,17 @@ async function routeCommand(args, projectRoot) {
|
|
|
12913
13659
|
resolvedSubcommand = undefined;
|
|
12914
13660
|
}
|
|
12915
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
|
+
}
|
|
12916
13673
|
if (resolvedSubcommand === "preset") {
|
|
12917
13674
|
return installPresetCommand({
|
|
12918
13675
|
projectRoot,
|