Skip to content

P0: add agent installer workflow#144

Merged
lzehrung merged 11 commits into
mainfrom
p0/agent-installer
Jul 8, 2026
Merged

P0: add agent installer workflow#144
lzehrung merged 11 commits into
mainfrom
p0/agent-installer

Conversation

@lzehrung

@lzehrung lzehrung commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • add top-level install and uninstall commands for supported agent targets
  • support dry-run, print-config, detect, idempotent writes, and safe uninstall of Codegraph-owned config
  • document the installer in README, installation, CLI, MCP, agent workflows, and bundled skill

Verification

  • npm run check
  • node ./dist/cli.js review --base origin/main --head HEAD --summary

Rebased onto main after #143 merged.

@lzehrung lzehrung force-pushed the p0/explore-facade branch from 5c1aa8e to d19ac54 Compare July 6, 2026 14:54
@lzehrung lzehrung force-pushed the p0/agent-installer branch from 86a7083 to 5d8fca4 Compare July 6, 2026 20:11
@lzehrung lzehrung changed the base branch from p0/explore-facade to main July 6, 2026 20:12
@lzehrung lzehrung marked this pull request as ready for review July 6, 2026 20:12
Comment thread src/installer/registry.ts Outdated
const detectedIds = detections
.filter((detection) => detection.detected)
.map((detection, index) => {
const definition = TARGET_DEFINITIONS[index]!;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Wrong array index after filter() in auto-detection

detections.filter(...).map((detection, index) => ...) uses the index of the filtered array, not the original detections array. If some targets are not detected, TARGET_DEFINITIONS[index] will map to the wrong target. For example, if codex and cursor are detected but claude is not, the filtered index 1 maps to TARGET_DEFINITIONS[1] (claude) instead of TARGET_DEFINITIONS[2] (cursor).

Suggested change
const definition = TARGET_DEFINITIONS[index]!;
const definition = TARGET_DEFINITIONS[detections.indexOf(detection)]!;

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5eac588. Auto-detect now keeps detections aligned with their original target objects instead of indexing after filter(), and the regression covers detected Cursor with missing Claude.

Comment thread src/installer/registry.ts
if (existing === null) return change(definition.id, "unchanged", configPath, dryRun);
const next = removeCodegraphConfig(definition, existing, configPath);
if (existing === next) return change(definition.id, "unchanged", configPath, dryRun);
const action = next.trim() ? "update" : "delete";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Empty JSON config files left behind after uninstall

For JSON configs, removeCodegraphConfig returns "{}\n" when the file only contained a Codegraph entry. next.trim() is "{}" (truthy), so the action is "update" instead of "delete". This leaves behind an empty config file instead of removing it, which is inconsistent with TOML uninstall behavior.

Suggested change
const action = next.trim() ? "update" : "delete";
const action = next.trim() === "{}" ? "delete" : next.trim() ? "update" : "delete";

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5eac588. JSON uninstall now renders an empty root object as empty content so removeConfig deletes the file, with a regression for Cursor-only config removal.

@kilo-code-bot

kilo-code-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (4 files)
  • docs/cli.md
  • src/cli/options.ts
  • src/installer/registry.ts
  • tests/installer.test.ts
Previous Review Summaries (7 snapshots, latest commit 20cc49e)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 20cc49e)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (4 files)
  • docs/cli.md
  • src/cli/options.ts
  • src/installer/registry.ts
  • tests/installer.test.ts

Previous review (commit 932b406)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • src/installer/registry.ts
  • tests/installer.test.ts

Previous review (commit 6fa7ec4)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • src/cli/install.ts
  • tests/installer.test.ts

Previous review (commit 3697365)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (12 files)
  • README.md
  • codegraph-skill/codegraph/SKILL.md
  • docs/agent-workflows.md
  • docs/cli.md
  • docs/installation.md
  • docs/mcp.md
  • src/cli/help.ts
  • src/cli/options.ts
  • src/installer/registry.ts
  • tests/cli-command-modules.test.ts
  • tests/cli-options-validation.test.ts
  • tests/installer.test.ts

Previous review (commit 733a0f9)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • tests/cli-regressions.test.ts

Previous review (commit 5eac588)

Status: No Issues Found | Recommendation: Merge

Resolved Issues

  • CRITICAL src/installer/registry.ts - Wrong array index after filter() in auto-detection (fixed in resolveRequestedTargets)
  • WARNING src/installer/registry.ts - Empty JSON config files left behind after uninstall (fixed via renderJsonConfig)
  • WARNING src/installer/registry.ts - JSON-based targets unconditionally overwriting existing entries (fixed via shouldPreserveExistingServer)
  • WARNING src/installer/registry.ts - XDG_CONFIG_HOME vs ~/.config mismatch for OpenCode marker paths (fixed in src/cli/skill.ts)
Files Reviewed (3 files)
  • src/cli/skill.ts - no issues
  • src/installer/registry.ts - no issues
  • tests/installer.test.ts - no issues

Previous review (commit 5d8fca4)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
src/installer/registry.ts 204 Wrong array index after filter() in auto-detection

WARNING

File Line Issue
src/installer/registry.ts 313 Empty JSON config files left behind after uninstall
Files Reviewed (12 files)
  • README.md - no issues
  • codegraph-skill/codegraph/SKILL.md - no issues
  • docs/agent-workflows.md - no issues
  • docs/cli.md - no issues
  • docs/installation.md - no issues
  • docs/mcp.md - no issues
  • src/cli.ts - no issues
  • src/cli/help.ts - no issues
  • src/cli/install.ts - no issues
  • src/cli/options.ts - no issues
  • src/installer/registry.ts - 2 issues
  • tests/installer.test.ts - no issues

Fix these issues in Kilo Cloud


Reviewed by kimi-k2.6-20260420 · Input: 55.2K · Output: 2.5K · Cached: 102.3K

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new top-level installer workflow (codegraph install / codegraph uninstall) that can detect supported agent clients, write/remove Codegraph-owned MCP configuration + skill marker files, and documents the workflow across CLI help and docs.

Changes:

  • Introduces an installer registry that models install targets (Codex/Claude/Cursor/Gemini/OpenCode/Agents), supports detect/print-config/dry-run/idempotent upserts, and scoped uninstall behavior.
  • Wires new install/uninstall commands into the CLI (schemas, command dispatch, help text).
  • Adds end-to-end installer tests and updates documentation + bundled skill guidance.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/installer.test.ts New tests covering detect, print-config, dry-run, idempotent install, scoped uninstall, and malformed JSON handling.
src/installer/registry.ts Core installer target registry + read/merge/remove logic for TOML and JSON-based configs, plus skill marker handling.
src/cli/options.ts Adds install/uninstall command schemas and registers --print-config as a value option.
src/cli/install.ts CLI handler for install/uninstall implementing detect/print-config/install/uninstall flows.
src/cli/help.ts Adds help text + examples for install/uninstall and registers them as known commands.
src/cli.ts Routes install/uninstall to the new handler.
README.md Documents the new installer workflow in “Agent setup”.
docs/mcp.md Adds installer documentation to MCP docs.
docs/installation.md Adds “Agent client setup” section describing installer usage.
docs/cli.md Adds installer examples + a dedicated “Agent client installer” section.
docs/agent-workflows.md Documents installer usage in agent workflows.
codegraph-skill/codegraph/SKILL.md Updates bundled skill guidance to reference installer workflow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/installer/registry.ts
Comment on lines +328 to +345
const parsed = parseConfigJson(existing, configPath);
if (definition.kind === "json-opencode-mcp") {
parsed.mcp = mergeRecord(readRecordProperty(parsed, "mcp"), {
codegraph: {
type: "local",
enabled: true,
command: ["codegraph", "mcp", "serve", "--root", ".", "--stdio"],
},
});
} else {
parsed.mcpServers = mergeRecord(readRecordProperty(parsed, "mcpServers"), {
codegraph: {
type: "stdio",
command: "codegraph",
args: ["mcp", "serve", "--root", ".", "--stdio"],
},
});
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5eac588. JSON install now preserves an existing non-matching codegraph entry instead of overwriting it, and the regression keeps custom command/args/env intact.

Comment thread src/installer/registry.ts
function detectTarget(definition: TargetDefinition, options: InstallOptions): TargetDetection {
const settings = installerSettings(options);
const configPath = definition.configPath?.(settings);
const skillTargetDir = getSkillTargetDirForAgent(definition.id, settings.homeDir, settings.env);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5eac588. getSkillTargetDirForAgent("opencode") now honors XDG_CONFIG_HOME, so detection uses the same base path as the OpenCode config path.

Comment thread src/installer/registry.ts
const settings = installerSettings(options);
const dryRun = options.dryRun ?? false;
const changes: InstallChange[] = [];
const skillTargetDir = getSkillTargetDirForAgent(definition.id, settings.homeDir, settings.env);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5eac588. OpenCode install now writes CODEGRAPH_INSTALLED under XDG_CONFIG_HOME/opencode/skills/codegraph when XDG_CONFIG_HOME is set; the regression asserts no marker is written under ~/.config.

Comment thread src/installer/registry.ts
const settings = installerSettings(options);
const dryRun = options.dryRun ?? false;
const changes: InstallChange[] = [];
const skillTargetDir = getSkillTargetDirForAgent(definition.id, settings.homeDir, settings.env);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5eac588. OpenCode uninstall now removes the marker from the same XDG_CONFIG_HOME-derived path used during install; the regression covers the install/uninstall round trip.

Comment thread src/installer/registry.ts
Comment on lines +359 to +365
if (Object.keys(servers).length) {
parsed[property] = servers;
} else {
delete parsed[property];
}
return `${JSON.stringify(parsed, null, 2)}\n`;
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 5eac588. Removing the last Codegraph-owned JSON entry now returns empty content, so the config file is deleted instead of left as {}; covered by a new uninstall regression.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Comment thread src/installer/registry.ts Outdated
Comment on lines +107 to +109
configPath: ({ env, homeDir }) =>
path.join(env.XDG_CONFIG_HOME ?? path.join(homeDir, ".config"), "opencode", "opencode.json"),
},

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 3697365. OpenCode install now trims XDG_CONFIG_HOME and falls back to ~/.config/opencode/opencode.json when it is unset or blank; the regression test covers the blank-env case.

Comment thread src/installer/registry.ts Outdated
function assertWriteAllowed(options: InstallOptions): void {
if (options.dryRun) return;
if (options.yes) return;
throw new Error("Install writes require --yes. Use --dry-run or --print-config to inspect changes first.");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 3697365. The shared write guard now reports the selected target path without referencing Claude-specific storage, so the message is correct for Codex, Gemini, OpenCode, and other targets.

Comment thread src/installer/registry.ts Outdated
Comment on lines +436 to +442
function isCodegraphJsonServer(value: unknown): boolean {
if (!isJsonRecord(value)) return false;
const command = value.command;
if (command === "codegraph") return true;
if (Array.isArray(command) && command[0] === "codegraph") return true;
return false;
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 3697365. JSON-backed uninstall ownership now requires an exact installer-owned MCP server shape instead of a command prefix match, and the tests cover preserving user-modified commands, args, env, and non-object values.

Comment thread src/cli/install.ts
Comment on lines +37 to +44
if (context.hasFlag("--detect")) {
context.writeJSONLine({ targets: await detectInstallTargets(options) });
return;
}

if (context.command === "install") {
context.writeJSONLine(await installCodegraphTargets(options));
return;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 3697365. Installer commands no longer advertise or accept --json; help/docs/skill guidance now state installer reporting is text-only while analysis commands keep JSON output.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment thread src/cli/install.ts
Comment on lines +22 to +40
export async function handleInstallerCommand(context: InstallerCommandContext): Promise<void> {
const printConfigTarget = context.getOpt("--print-config");
const targetIds = parseInstallerTargets(context);
const options = {
...(targetIds !== undefined ? { targetIds } : {}),
yes: context.hasFlag("--yes"),
dryRun: context.hasFlag("--dry-run"),
};

if (printConfigTarget !== undefined) {
const targetId = parseInstallTargetId(printConfigTarget);
context.writeStdoutLine(printInstallConfig({ targetId }).trimEnd());
return;
}

if (context.hasFlag("--detect")) {
context.writeJSONLine({ targets: await detectInstallTargets(options) });
return;
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 6fa7ec4. install --print-config now fails fast with usage exit 2 when combined with --target, extra positional target selection, --detect, --yes, or --dry-run; tests cover each conflict.

Comment thread src/cli/install.ts
Comment on lines +57 to +59
if (targetOpt !== undefined && positionalTarget !== undefined) {
throw new Error("Use either --target or a positional target, not both.");
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 6fa7ec4. Installer target-selection conflicts now go through the CLI usage-error path with stderr plus exit 2 instead of throwing; tests cover both install and uninstall with --target plus a positional target.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment thread src/installer/registry.ts Outdated
const value = record[property];
if (value === undefined) return {};
if (isJsonRecord(value)) return value;
throw new Error(`Existing ${property} config must be a JSON object before codegraph can merge into it.`);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 932b406. readRecordProperty now uses the generic message before codegraph can update it, which applies to both install merge and uninstall removal paths.

Comment thread tests/installer.test.ts
Comment on lines +20 to +22
function expectInstallerChange(changes: InstallChange[], expected: InstallChange): void {
expect(changes).toContainEqual(expected);
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 932b406. Installer test expectations now normalize expected InstallChange.path values to forward slashes before comparing, matching normalizePathForDisplay on Windows and POSIX.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Comment thread src/installer/registry.ts
Comment on lines +406 to +408
if (definition.kind === "skill-only") {
return `codegraph skill install --agent ${definition.id} --target ${normalizePathForDisplay(skillTargetDir)}\n`;
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 20cc49e. install --print-config agents now emits codegraph skill install --agent agents without --target, so the generated command no longer conflicts with skill install validation; a regression covers it.

Comment thread src/installer/registry.ts
Comment on lines +235 to +240
return {
detected: definition.kind === "skill-only" && pathExists(path.dirname(path.dirname(skillTargetDir))),
reason: `${definition.label} was not detected`,
...(configPath !== undefined ? { configPath: normalizePathForDisplay(configPath) } : {}),
skillTargetDir: normalizePathForDisplay(skillTargetDir),
};

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 20cc49e. Skill-only target detection now reports a positive base directory exists reason when ~/.agents makes the agents target detected; a regression asserts the reason is not contradictory.

Comment thread src/cli/options.ts
Comment on lines +366 to +370
commandSchema(["--detect", "--dry-run", "--yes"], ["--print-config", "--target"], {
kind: "max",
max: 1,
usage: "Usage: codegraph install [target] [--target <ids>] [--yes | --dry-run] [--print-config <target>]",
}),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 20cc49e. The install positional-argument usage string now includes --detect, and the installer regression covers the usage hint.

Comment thread src/cli/options.ts
Comment on lines +374 to +378
commandSchema(["--detect", "--dry-run", "--yes"], ["--target"], {
kind: "max",
max: 1,
usage: "Usage: codegraph uninstall [target] [--target <ids>] [--yes | --dry-run]",
}),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 20cc49e. The uninstall positional-argument usage string now includes --detect, with the same regression covering both install and uninstall usage hints.

Comment thread docs/cli.md
Comment on lines +275 to +278
- `install` configures Codegraph-owned MCP entries, bundled skill payloads, and marker files for supported local agent clients: `codex`, `claude`, `cursor`, `gemini`, `opencode`, and `agents`.
- Writes require `--yes`; use `--dry-run` to preview changed file paths and actions or `--print-config <target>` to print a copyable MCP snippet without writing.
- `uninstall` removes only Codegraph-owned marker blocks, marker files, exact bundled skill payloads, or exact installer-owned MCP entries.
- `skill install` remains the lower-level primitive when you only want to copy the bundled skill directly without MCP config.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 20cc49e. docs/cli.md now documents --detect in the Agent client installer section alongside --dry-run and --print-config.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment thread src/installer/registry.ts Outdated
Comment thread docs/cli.md Outdated
Comment on lines 161 to 166
# Install or preview agent client integration
codegraph install --target codex,claude --dry-run
codegraph install --target codex,claude --yes
codegraph install --print-config codex
codegraph uninstall --target codex --yes
codegraph mcp --help

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 9b93153. codegraph mcp --help moved back under the MCP examples section; the installer examples section now ends with codegraph install --help.

@lzehrung lzehrung merged commit 124e930 into main Jul 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants