first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
# `detect`
## Purpose
Runs a plugin's optional `detect` stage inside an OpenTelemetry span. The
plugin reports what is already present in the target — existing skill
directories, conflicting files, configuration to migrate — so the host
can show that information to the user before prompts run, and so plugins
can tailor their plans accordingly. Detect is read-only by contract: it
inspects `ctx.fs` but never mutates state.
## Features
- Wraps the plugin call in `installer.detect` span carrying `plugin.id`,
`plugin.version`, and `dry_run` so traces correlate with apply.
- No-op when the plugin omits `detect` — returns `{}` without opening a
span, so plugins that don't need detection pay zero observability
overhead.
- Surfaces plugin-thrown errors unchanged — the host's outer `try/catch`
is responsible for wrapping these in `INSTALLER.PLAN_FAILED` if they
occur post-detect, but detect-stage exceptions propagate raw so plugins
can model their own detection-time error codes.
## Basic Example
```ts
import { runDetect } from "@suaveplan/installer";
const result = await runDetect(myPlugin, ctx);
if (result.alreadyInstalled) {
ctx.cli.write("Plugin is already installed");
}
```
## Advanced Example
```ts
import type { InstallerPlugin } from "@suaveplan/installer";
const plugin: InstallerPlugin<undefined, void> = {
id: "auditor",
version: "0.1.0",
detect: async (ctx) => {
const skillsDir = ctx.fs.pathOf(ctx.cwd, ".suaveplan/skills");
const exists = await ctx.fs.exists(skillsDir);
if (!exists) {
return {
alreadyInstalled: false,
notes: ["fresh target"],
};
}
const entries = await ctx.fs.listDir(skillsDir);
return {
alreadyInstalled: true,
conflicts: entries.filter((e) => e !== "index.json"),
notes: [`${entries.length} entries already present`],
};
},
plan: async () => [],
apply: async () => undefined,
};
```
## API Reference
- `runDetect<TAnswers, THandle>(plugin, ctx): Promise<DetectResult>`
- `DetectResult` — `{ alreadyInstalled?: boolean, conflicts?: string[], notes?: string[] }`. All fields optional.
## Implementation Notes
The span is opened only when `plugin.detect` exists; this is a small
optimisation but it matters when the host is called in a tight loop
(e.g. `list` discovers many plugins). The default empty `DetectResult`
(`{}`) ensures downstream stages never see `undefined` and lets the
`alreadyInstalled` boolean be used in CLI rendering without an extra
guard. Detect doesn't receive answers — those don't exist yet — so any
configuration-dependent detection must happen lazily during plan
instead.