opencode-codeops 1.4.0 → 1.5.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to CodeOps are recorded here.
4
4
 
5
+ ## 1.5.0 — 2026-09-19
6
+
7
+ ### Features
8
+
9
+ - installer: unify install/update into one command
10
+ - installer: add npm-first install, agents installer, and release tooling
11
+
5
12
  ## 1.4.0 — 2026-09-19
6
13
 
7
14
  ### Features
package/README.md CHANGED
@@ -45,41 +45,46 @@ Add to your `opencode.json`:
45
45
  OpenCode installs the plugin automatically via Bun on next startup. A global config
46
46
  (`~/.config/opencode/opencode.json`) is recommended so CodeOps is active in every project.
47
47
 
48
- ### 2. Install the skills
48
+ ### 2. Install the skills and subagents
49
49
 
50
- OpenCode discovers skills only from the filesystem (`.opencode/skills/` or
51
- `~/.config/opencode/skills/`); it never reads them from a plugin package. The installer is a thin
52
- `npx` wrapper around this package, so the skills always match the published version:
50
+ OpenCode discovers skills and subagents only from the filesystem; it never reads them from a plugin
51
+ package. One command installs both. The installer is a thin `npx` wrapper around this package, so
52
+ the installed files always match the published version:
53
53
 
54
54
  ```bash
55
55
  # Global (recommended) — available in every OpenCode project
56
56
  curl -fsSL https://cdn.jsdelivr.net/npm/opencode-codeops@latest/install.sh | bash
57
57
 
58
- # Project-only — skills live in ./.opencode/skills and are committed with the repo
58
+ # Project-only — files live in ./.opencode and are committed with the repo
59
59
  curl -fsSL https://cdn.jsdelivr.net/npm/opencode-codeops@latest/install.sh | bash -s -- --project
60
60
  ```
61
61
 
62
62
  The same installer runs directly through npm:
63
63
 
64
64
  ```bash
65
- npx -y opencode-codeops@latest install-skills
65
+ npx -y opencode-codeops@latest install
66
+ npx -y opencode-codeops@latest update # alias of install
66
67
  ```
67
68
 
69
+ The scope is auto-detected: inside a CodeOps project (a git repo with `.opencode/` or
70
+ `codeops/.codeops.yml`) it installs into `./.opencode/skills` and `./.opencode/agents`; anywhere
71
+ else it installs globally into `~/.config/opencode/`. Pass `--project` or `--global` to force one.
72
+
68
73
  Pin a version with `CODEOPS_VERSION` (an npm dist-tag or exact version; defaults to `latest`):
69
74
 
70
75
  ```bash
71
- CODEOPS_VERSION=1.4.0 curl -fsSL https://cdn.jsdelivr.net/npm/opencode-codeops@latest/install.sh | bash
76
+ CODEOPS_VERSION=1.5.0 curl -fsSL https://cdn.jsdelivr.net/npm/opencode-codeops@latest/install.sh | bash
72
77
  ```
73
78
 
74
79
  Pin the plugin to the same version so the two cannot drift apart:
75
80
 
76
81
  ```json
77
- { "plugin": ["opencode-codeops@1.4.0"] }
82
+ { "plugin": ["opencode-codeops@1.5.0"] }
78
83
  ```
79
84
 
80
85
  Re-running the installer upgrades an existing install in place. It replaces only the files this
81
- package owns, recorded in `<skills-dir>/.opencode-codeops.json`. Skills you author yourself, or
82
- install with another tool, are left untouched.
86
+ package owns, recorded in `.opencode-codeops.json`. Files you author yourself, or install with
87
+ another tool, are left untouched.
83
88
 
84
89
  Check or remove an install:
85
90
 
@@ -88,47 +93,37 @@ npx -y opencode-codeops@latest status
88
93
  npx -y opencode-codeops@latest uninstall
89
94
  ```
90
95
 
91
- `status` reports the installed version next to the current package version, so a plugin/skills
92
- mismatch is visible. Use `--dry-run` to preview an install; a same-named directory the package
93
- does not own is skipped with a warning, and `--force` replaces it.
94
-
95
- ### 3. Install the subagents
96
-
97
- The 12 CodeOps subagents install into `.opencode/agents/` (project) or `~/.config/opencode/agents/`
98
- (global). The `/setup-codeops` skill does this for a project; do it manually with:
99
-
100
- ```bash
101
- npx -y opencode-codeops@latest install-agents --project
102
- ```
96
+ `status` reports the installed version next to the current package version, so a plugin/files
97
+ mismatch is visible. Use `--dry-run` to preview an install; a same-named file the package does not
98
+ own is skipped with a warning, and `--force` replaces it.
103
99
 
104
100
  ### Local development
105
101
 
106
- Symlink the plugin into your OpenCode plugin directory and link the skills to a checkout, so edits
107
- are picked up without reinstalling:
102
+ Symlink the plugin into your OpenCode plugin directory and link the installed files to a checkout,
103
+ so edits are picked up without reinstalling:
108
104
 
109
105
  ```bash
110
106
  # Plugin (project or global plugin directory)
111
107
  ln -s /path/to/opencode-codeops/plugin/index.ts ~/.config/opencode/plugins/codeops.ts
112
108
 
113
109
  # Skills and agents — link instead of copy
114
- node /path/to/opencode-codeops/bin/index.mjs install-skills --link --global
115
- node /path/to/opencode-codeops/bin/index.mjs install-agents --link --global
110
+ node /path/to/opencode-codeops/bin/index.mjs install --link --global
116
111
  ```
117
112
 
118
113
  ## Setup
119
114
 
120
- After installing the plugin and the skills, initialize CodeOps in your project:
115
+ After installing the plugin and the files, initialize CodeOps in your project:
121
116
 
122
117
  ```
123
118
  /setup-codeops
124
119
  ```
125
120
 
126
- This creates the `codeops/` layout, scaffolds `codeops/codeops.json` and `codeops/.codeops.yml`, installs the 12 CodeOps subagent files into `.opencode/agents/`, and adds a managed section to `AGENTS.md`.
121
+ This creates the `codeops/` layout, scaffolds `codeops/codeops.json` and `codeops/.codeops.yml`, installs the skills and the 12 CodeOps subagent files into `.opencode/`, and adds a managed section to `AGENTS.md`.
127
122
 
128
123
  Commit the result:
129
124
 
130
125
  ```bash
131
- git add codeops/ .opencode/agents/ AGENTS.md
126
+ git add codeops/ .opencode/ AGENTS.md
132
127
  git commit -m "chore: initialize CodeOps"
133
128
  ```
134
129
 
@@ -140,7 +135,7 @@ On every OpenCode session start and after every compaction, the plugin injects:
140
135
 
141
136
  These standards are active without any user action. They do not need to be copied into `AGENTS.md`.
142
137
 
143
- The plugin also warns (non-blocking) if any tool attempts to edit `codeops/.codeops.yml` directly — that file is managed exclusively by the `setup-codeops` skill.
138
+ The plugin also warns (non-blocking) if any tool attempts to edit `codeops/.codeops.yml` directly — that file is managed exclusively by the `setup-codeops` skill — and if the installed skills version differs from the plugin version, so a stale install is visible.
144
139
 
145
140
  ## Agent model configuration
146
141
 
package/bin/index.mjs CHANGED
@@ -2,92 +2,174 @@
2
2
  /**
3
3
  * The `opencode-codeops` command-line entry point.
4
4
  *
5
- * Routes each command to the skills installer (`install-skills.mjs`) or the
6
- * agent installer (`install-agents.mjs`), so one package binary exposes both.
7
- * `status` reports on both installs because the skills and the plugin can drift
8
- * in version and that is the check a user needs most.
5
+ * There is exactly one installation path: a command installs or updates the
6
+ * skills and the subagents together. The two filesystems installers
7
+ * (`install-skills.mjs` and `install-agents.mjs`) are implementation details
8
+ * this module orchestrates; they are never exposed as separate user commands.
9
+ *
10
+ * The scope is auto-detected. Inside a CodeOps project the files go to
11
+ * `.opencode/skills` and `.opencode/agents`; anywhere else they go to the
12
+ * global `~/.config/opencode/` directories. `--project` and `--global` override
13
+ * the detection.
9
14
  *
10
15
  * Usage:
11
- * opencode-codeops install-skills [options] Install or upgrade skills
12
- * opencode-codeops install-agents [options] Install or upgrade subagents
13
- * opencode-codeops status [options] Show installed skills and agents
14
- * opencode-codeops uninstall [options] Remove the managed skills
15
- * opencode-codeops help Show this help
16
+ * opencode-codeops install [options] Install or upgrade skills and agents
17
+ * opencode-codeops update [options] Alias of install
18
+ * opencode-codeops status [options] Show installed versions
19
+ * opencode-codeops uninstall [options] Remove managed files
20
+ * opencode-codeops help Show this help
16
21
  *
17
22
  * @module index
18
23
  */
19
24
 
20
- import { realpathSync } from "node:fs"
25
+ import { execFileSync } from "node:child_process"
26
+ import { existsSync, realpathSync } from "node:fs"
27
+ import { join } from "node:path"
21
28
  import { fileURLToPath } from "node:url"
22
29
 
30
+ /** Commands this CLI understands. */
31
+ const COMMANDS = new Set(["install", "update", "status", "uninstall", "help"])
32
+
23
33
  /** Prints command usage. */
24
34
  function printUsage() {
25
35
  console.log(`CodeOps installer for OpenCode.
26
36
 
27
37
  Usage:
28
- opencode-codeops install-skills [options] Install or upgrade skills (default)
29
- opencode-codeops install-agents [options] Install or upgrade subagents
30
- opencode-codeops status [options] Show installed skills and agents
31
- opencode-codeops uninstall [options] Remove the managed skills
32
- opencode-codeops help Show this help
33
-
34
- Run \`opencode-codeops install-skills --help\` or
35
- \`opencode-codeops install-agents --help\` for the option list.`)
38
+ opencode-codeops install [options] Install or upgrade skills and agents (default)
39
+ opencode-codeops update [options] Alias of install
40
+ opencode-codeops status [options] Show installed skills and agents
41
+ opencode-codeops uninstall [options] Remove managed skills and agents
42
+ opencode-codeops help Show this help
43
+
44
+ Options:
45
+ --project Force the project scope (./.opencode)
46
+ --global Force the global scope (~/.config/opencode)
47
+ --dry-run Show what would happen without writing files
48
+ --force Replace same-named files this package does not own
49
+ --link Symlink to the source instead of copying (development)
50
+ -h, --help Show this help
51
+
52
+ Scope is auto-detected: inside a CodeOps project (a git repo with .opencode/ or
53
+ codeops/.codeops.yml) the files go to .opencode/; otherwise to ~/.config/opencode/.`)
36
54
  }
37
55
 
38
56
  /**
39
- * Chooses the installer module for a command line.
57
+ * Finds the git top-level directory for a working directory.
40
58
  *
41
- * @param argv - Arguments after the executable
42
- * @returns `"agents"` for agent commands, otherwise `"skills"`
59
+ * @param cwd - Directory to start from
60
+ * @returns The repository root, or `cwd` when it is not a git checkout
43
61
  */
44
- export function route(argv) {
45
- const first = argv[0]
46
- if (first === "install-agents" || first === "agents-status" || first === "agents-uninstall") {
47
- return "agents"
62
+ function findProjectRoot(cwd) {
63
+ try {
64
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
65
+ cwd,
66
+ encoding: "utf-8",
67
+ stdio: ["ignore", "pipe", "ignore"],
68
+ }).trim()
69
+ } catch {
70
+ return cwd
48
71
  }
49
- return "skills"
50
72
  }
51
73
 
52
74
  /**
53
- * Runs the requested command.
75
+ * Resolves whether files install into the project or globally.
54
76
  *
55
- * The modules are imported lazily so a skills-only invocation never loads the
56
- * agent installer, and vice versa.
77
+ * Explicit flags win. Otherwise a git repository is a CodeOps project when it
78
+ * already contains `.opencode/` or `codeops/.codeops.yml`; anything else uses
79
+ * the global scope.
80
+ *
81
+ * @param options - Scope flags from the command line
82
+ * @param options.project - Force the project scope
83
+ * @param options.global - Force the global scope
84
+ * @param cwd - Directory the command ran from
85
+ * @returns `"project"` or `"global"`
57
86
  */
58
- async function run() {
59
- const args = process.argv.slice(2)
60
- const first = args[0]
87
+ export function resolveScope(options, cwd) {
88
+ if (options.project) return "project"
89
+ if (options.global) return "global"
90
+
91
+ const root = findProjectRoot(cwd)
92
+ if (existsSync(join(root, ".opencode")) || existsSync(join(root, "codeops", ".codeops.yml"))) {
93
+ return "project"
94
+ }
95
+ return "global"
96
+ }
97
+
98
+ /**
99
+ * Runs one command against both installers.
100
+ *
101
+ * @param command - `install`, `status`, or `uninstall`
102
+ * @param rest - Options to forward, without the scope flags (re-added below)
103
+ * @param io - Injectable environment (`cwd`, `home`) for tests
104
+ * @returns The combined process exit code
105
+ */
106
+ async function runCombined(command, rest, io) {
107
+ const skills = await import("./install-skills.mjs")
108
+ const agents = await import("./install-agents.mjs")
109
+
110
+ const cwd = io.cwd ?? process.cwd()
111
+ const scope = resolveScope(
112
+ { project: rest.includes("--project"), global: rest.includes("--global") },
113
+ cwd
114
+ )
115
+ const passed = rest.filter((arg) => arg !== "--project" && arg !== "--global")
116
+ const scoped = [scope === "project" ? "--project" : "--global", ...passed]
117
+
118
+ const skillsCode = skills.main([command, ...scoped], io)
119
+ const agentsCode = agents.main([command, ...scoped], io)
120
+ return skillsCode || agentsCode
121
+ }
122
+
123
+ /**
124
+ * Dispatches a command line.
125
+ *
126
+ * Bare options (for example `opencode-codeops --project`) mean `install`, so the
127
+ * common case needs no subcommand.
128
+ *
129
+ * @param argv - Arguments after the executable
130
+ * @param io - Injectable environment (`cwd`, `home`) for tests
131
+ * @returns The process exit code
132
+ */
133
+ export async function dispatch(argv, io = {}) {
134
+ const first = argv[0]
135
+ const rest = argv.slice(1)
61
136
 
62
137
  if (!first || first === "help" || first === "-h" || first === "--help") {
63
138
  printUsage()
64
- return
139
+ return 0
65
140
  }
66
141
 
67
- if (first === "status") {
68
- const skills = await import("./install-skills.mjs")
69
- const agents = await import("./install-agents.mjs")
70
- const skillsCode = skills.main(["status", ...args.slice(1)])
71
- const agentsCode = agents.main(["agents-status", ...args.slice(1)])
72
- process.exitCode = skillsCode || agentsCode
73
- return
142
+ // Keep the unified help; the internal installers still document the old
143
+ // component commands in their own usage text.
144
+ if (rest.includes("--help") || rest.includes("-h")) {
145
+ printUsage()
146
+ return 0
74
147
  }
75
148
 
76
- if (route(args) === "agents") {
77
- const { main } = await import("./install-agents.mjs")
78
- process.exitCode = main(args)
79
- return
149
+ if (first.startsWith("-")) return runCombined("install", argv, io)
150
+
151
+ if (!COMMANDS.has(first)) {
152
+ console.error(`error: unknown command '${first}'`)
153
+ printUsage()
154
+ return 2
80
155
  }
81
156
 
82
- const { main } = await import("./install-skills.mjs")
83
- process.exitCode = main(args)
157
+ const command = first === "update" ? "install" : first
158
+ return runCombined(command, rest, io)
84
159
  }
85
160
 
86
161
  /**
87
- * True when this module is the process entry point.
162
+ * Runs the CLI when this module is the process entry point.
88
163
  *
89
164
  * The comparison resolves symlinks because npm installs the bin as a symlink in
90
165
  * `node_modules/.bin`, so `process.argv[1]` is the link path, not the real path.
166
+ */
167
+ async function run() {
168
+ process.exitCode = await dispatch(process.argv.slice(2))
169
+ }
170
+
171
+ /**
172
+ * True when this module is the process entry point.
91
173
  *
92
174
  * @returns True when this file is the entry point
93
175
  */
@@ -81,20 +81,17 @@ export function readPackageVersion() {
81
81
  /**
82
82
  * Resolves the directory that holds the packaged agent definitions.
83
83
  *
84
- * The `CODEOPS_PLUGIN_ROOT` environment variable overrides the default, which
85
- * lets a checkout run the installer before the package is published.
84
+ * The package's own `agents/` directory is always the source, so the installed
85
+ * agents match the package that was invoked. The `CODEOPS_PLUGIN_ROOT`
86
+ * environment variable is deliberately ignored: the plugin exports it into
87
+ * every shell, and honouring it would install a different checkout's agents.
88
+ * Pass `--source` to override for development.
86
89
  *
87
90
  * @param override - Optional explicit agents directory
88
91
  * @returns Absolute path to the agents directory
89
92
  */
90
93
  export function resolveSourceDir(override) {
91
94
  if (override) return resolve(override)
92
-
93
- const envRoot = process.env.CODEOPS_PLUGIN_ROOT
94
- if (envRoot && existsSync(join(envRoot, "agents"))) {
95
- return join(envRoot, "agents")
96
- }
97
-
98
95
  return join(PACKAGE_ROOT, "agents")
99
96
  }
100
97
 
@@ -375,7 +372,7 @@ export function main(argv, io = {}) {
375
372
  const cwd = io.cwd ?? process.cwd()
376
373
  const version = io.version ?? readPackageVersion()
377
374
  const sourceDir = io.source ?? resolveSourceDir(options.source)
378
- const targetDir = resolveTarget(options, cwd, "agents")
375
+ const targetDir = resolveTarget(options, cwd, "agents", io.home)
379
376
 
380
377
  if (!existsSync(sourceDir)) {
381
378
  console.error(`error: agents directory not found: ${sourceDir}`)
@@ -404,8 +401,8 @@ export function main(argv, io = {}) {
404
401
  if (command === "uninstall") {
405
402
  const result = uninstallAgents({ targetDir, dryRun: options.dryRun })
406
403
  if (result.error) {
407
- console.error(`error: ${result.error} at ${targetDir}`)
408
- return 1
404
+ console.log(`nothing to remove at ${targetDir} (${result.error})`)
405
+ return 0
409
406
  }
410
407
  console.log(
411
408
  `${options.dryRun ? "would remove" : "removed"} ${result.removed.length} agent(s); ` +
@@ -81,20 +81,17 @@ export function readPackageVersion() {
81
81
  /**
82
82
  * Resolves the directory that holds the shipped skills.
83
83
  *
84
- * The `CODEOPS_PLUGIN_ROOT` environment variable overrides the default. That
85
- * lets a checkout run the installer before the package is published.
84
+ * The package's own `skills/` directory is always the source, so the installed
85
+ * skills match the package that was invoked. The `CODEOPS_PLUGIN_ROOT`
86
+ * environment variable is deliberately ignored: the plugin exports it into
87
+ * every shell, and honouring it would install a different checkout's skills.
88
+ * Pass `--source` to override for development.
86
89
  *
87
90
  * @param override - Optional explicit skills directory
88
91
  * @returns Absolute path to the skills directory
89
92
  */
90
93
  export function resolveSourceDir(override) {
91
94
  if (override) return resolve(override)
92
-
93
- const envRoot = process.env.CODEOPS_PLUGIN_ROOT
94
- if (envRoot && existsSync(join(envRoot, "skills"))) {
95
- return join(envRoot, "skills")
96
- }
97
-
98
95
  return join(PACKAGE_ROOT, "skills")
99
96
  }
100
97
 
@@ -385,7 +382,7 @@ export function main(argv, io = {}) {
385
382
  const cwd = io.cwd ?? process.cwd()
386
383
  const version = io.version ?? readPackageVersion()
387
384
  const sourceDir = io.source ?? resolveSourceDir(options.source)
388
- const targetDir = resolveTarget(options, cwd, "skills")
385
+ const targetDir = resolveTarget(options, cwd, "skills", io.home)
389
386
 
390
387
  if (!existsSync(sourceDir)) {
391
388
  console.error(`error: skills directory not found: ${sourceDir}`)
@@ -414,8 +411,8 @@ export function main(argv, io = {}) {
414
411
  if (command === "uninstall") {
415
412
  const result = uninstallSkills({ targetDir, dryRun: options.dryRun })
416
413
  if (result.error) {
417
- console.error(`error: ${result.error} at ${targetDir}`)
418
- return 1
414
+ console.log(`nothing to remove at ${targetDir} (${result.error})`)
415
+ return 0
419
416
  }
420
417
  console.log(
421
418
  `${options.dryRun ? "would remove" : "removed"} ${result.removed.length} skill(s); ` +
@@ -167,12 +167,13 @@ export function linkEntry({ from, dest, type = "dir" }) {
167
167
  * @param options.project - Use a project-relative directory instead of global
168
168
  * @param cwd - Project root used for `--project`
169
169
  * @param kind - Subdirectory name under `.opencode` (for example `skills`)
170
+ * @param home - Home directory used for the global default (injectable for tests)
170
171
  * @returns Absolute path to the target directory
171
172
  */
172
- export function resolveTarget(options, cwd, kind) {
173
+ export function resolveTarget(options, cwd, kind, home = homedir()) {
173
174
  if (options.target) return resolve(options.target)
174
175
  if (options.project) return resolve(cwd, ".opencode", kind)
175
- return join(homedir(), ".config", "opencode", kind)
176
+ return join(home, ".config", "opencode", kind)
176
177
  }
177
178
 
178
179
  /**
package/install.sh CHANGED
@@ -17,11 +17,11 @@
17
17
  # defaults to "latest").
18
18
  #
19
19
  # Subcommands (see `npx opencode-codeops help` for the full option list):
20
- # install-skills Install or upgrade the skills (default)
21
- # install-agents Install or upgrade the subagents
22
- # status Show the installed skills and agents version
23
- # uninstall Remove the managed skills
24
- # help Show installer help
20
+ # install Install or upgrade skills and subagents (default)
21
+ # update Alias of install
22
+ # status Show the installed versions
23
+ # uninstall Remove the managed files
24
+ # help Show installer help
25
25
 
26
26
  set -euo pipefail
27
27
 
@@ -40,12 +40,12 @@ if ! command -v npx >/dev/null 2>&1; then
40
40
  exit 1
41
41
  fi
42
42
 
43
- # The subcommand is optional and defaults to install-skills. Any remaining
44
- # arguments are passed through to the installer unchanged.
45
- subcommand="install-skills"
43
+ # The subcommand is optional and defaults to install. Any remaining arguments
44
+ # are passed through to the installer unchanged.
45
+ subcommand="install"
46
46
  if (( $# > 0 )); then
47
47
  case "$1" in
48
- install | install-skills | install-agents | status | uninstall | help)
48
+ install | update | status | uninstall | help)
49
49
  subcommand="$1"
50
50
  shift
51
51
  ;;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-codeops",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Specification-first engineering for complex systems — CodeOps plugin for OpenCode",
5
5
  "type": "module",
6
6
  "engines": {
package/plugin/index.ts CHANGED
@@ -79,9 +79,9 @@ function installedSkillsVersion(skillsDir: string): string | undefined {
79
79
 
80
80
  // ---------------------------------------------------------------------------
81
81
  // Helper — warn (non-blocking) when the installed skills were written by a
82
- // different CodeOps version than this plugin. The plugin and the skills are
82
+ // different CodeOps version than this plugin. The plugin and the files are
83
83
  // installed by separate commands, so their versions can drift; a mismatch
84
- // usually means the skills need `npx opencode-codeops install-skills` again.
84
+ // usually means the skills need `npx opencode-codeops update` again.
85
85
  // ---------------------------------------------------------------------------
86
86
  async function warnOnVersionSkew(
87
87
  client: Parameters<Plugin>[0]["client"],
@@ -103,7 +103,7 @@ async function warnOnVersionSkew(
103
103
  message:
104
104
  `CodeOps skills at ${skillsDir} are version ${installed}, ` +
105
105
  `but the plugin is version ${packageVersion}. ` +
106
- `Run \`npx opencode-codeops@${packageVersion} install-skills\` to match them.`,
106
+ `Run \`npx opencode-codeops@${packageVersion} update\` to match them.`,
107
107
  },
108
108
  })
109
109
  }
@@ -76,24 +76,24 @@ default when it is absent — so this line is a convenience/pin, not a requireme
76
76
  - Scaffolding is intentionally simple, so it lives in skill prose; only the *migration* path
77
77
  needs the deterministic engine. For migration, see [migration.md](migration.md).
78
78
 
79
- ## Agent file installation
79
+ ## Project file installation
80
80
 
81
- After scaffolding, install the CodeOps OpenCode agent definitions into the project. Prefer the
82
- installer bundled with the running plugin, so the agent version always matches the plugin version:
81
+ After scaffolding, install the CodeOps skills and subagents into the project. Prefer the installer
82
+ bundled with the running plugin, so the installed files always match the plugin version:
83
83
 
84
84
  ```bash
85
- node "${CODEOPS_PLUGIN_ROOT}/bin/install-agents.mjs" --project
85
+ node "${CODEOPS_PLUGIN_ROOT}/bin/index.mjs" install --project
86
86
  ```
87
87
 
88
88
  When the plugin is not active, the published installer does the same thing:
89
89
 
90
90
  ```bash
91
- npx -y opencode-codeops@latest install-agents --project
91
+ npx -y opencode-codeops@latest install --project
92
92
  ```
93
93
 
94
- This installs the 12 CodeOps subagent definitions (`executor`, `explorer`, `correctness-reviewer`,
95
- etc.) into `.opencode/agents/` where OpenCode will discover and load them automatically. These files
96
- are safe to commit to git. Files the installer owns are replaced on upgrade; same-named files it
97
- does not own are left untouched (pass `--force` to replace them). Users can override individual
98
- agent models in `opencode.json` under the `agent` key.
94
+ This installs the skills into `.opencode/skills/` and the 12 subagent definitions (`executor`,
95
+ `explorer`, `correctness-reviewer`, etc.) into `.opencode/agents/`, where OpenCode discovers and
96
+ loads them automatically. These files are safe to commit to git. Files the installer owns are
97
+ replaced on upgrade; same-named files it does not own are left untouched (pass `--force` to replace
98
+ them). Users can override individual agent models in `opencode.json` under the `agent` key.
99
99