@kisev/skills-opencode 2.0.2 → 2.0.4

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.ru.md CHANGED
@@ -2,97 +2,166 @@
2
2
 
3
3
  [English](README.md) | [Русский](README.ru.md)
4
4
 
5
- `@kisev/skills-opencode` - npm package с capability router, OpenCode runtime,
6
- управлением agent profiles и opt-in installer для agents, commands и plugins. Он
7
- не включает portable skills и не меняет конфигурацию при import, plugin load или
8
- npm lifecycle. Package требует Node.js 22+ и OpenCode 1.18.29+.
5
+ `@kisev/skills-opencode@2.0.4` - optional OpenCode-specific слой. У portable
6
+ skills отдельный lifecycle: их нужно установить независимо через
7
+ [корневую инструкцию](../../README.ru.md).
9
8
 
10
- ## Установка skills
9
+ ## Требования и ownership
11
10
 
12
- Сначала установите нужные portable skills через `npx skills` из публичного
13
- репозитория:
11
+ Package требует Node.js 22+ и OpenCode `>=1.18.29 <1.19.0`.
12
+
13
+ | Component | Project scope | Global scope |
14
+ | ------------------ | ---------------------------- | ------------------------------------------------- |
15
+ | npm package | project `node_modules` | `node_modules` в npm project `~/.config/opencode` |
16
+ | Commands | `.opencode/commands` | `~/.config/opencode/commands` |
17
+ | Agents | `.opencode/agents` | `~/.config/opencode/agents` |
18
+ | Optional wrappers | `.opencode/plugins` | `~/.config/opencode/plugins` |
19
+ | Ownership metadata | `.opencode/.skills-opencode` | `~/.config/opencode/.skills-opencode` |
20
+
21
+ Package и generated wrappers должны оставаться доступными после завершения
22
+ installer. Import, plugin loading и npm lifecycle scripts не устанавливают
23
+ assets или portable skills и не меняют OpenCode configuration.
24
+
25
+ ## Постоянная установка package
26
+
27
+ ### Project scope
28
+
29
+ Установите package в npm project репозитория и запускайте CLI из его корня:
14
30
 
15
31
  ```shell
16
- npx --yes skills add kisev/skills --agent opencode --skill '*' --copy --yes
32
+ cd /path/to/project
33
+ npm install --save-exact @kisev/skills-opencode@2.0.4
34
+ npm exec -- skills-opencode install --scope project --dry-run
17
35
  ```
18
36
 
19
- Для одного skill укажите `--skill <name>`. Для воспроизводимой установки можно
20
- передать URL GitHub tag, например
21
- `https://github.com/kisev/skills/tree/v2.0.0`. Package никогда не устанавливает
22
- и не обновляет skills. Если команда не нашла skill, она сообщает точную команду
23
- `npx skills add` для его установки.
37
+ Package остаётся в project `node_modules`, confirmed assets размещаются в
38
+ `.opencode`.
24
39
 
25
- ## Установка integration
40
+ ### Global scope
26
41
 
27
- Установите npm package там, где OpenCode сможет разрешить plugin:
42
+ Используйте `~/.config/opencode` как постоянный npm project:
28
43
 
29
44
  ```shell
30
- npm install @kisev/skills-opencode@2.0.0
45
+ mkdir -p "$HOME/.config/opencode"
46
+ cd "$HOME/.config/opencode"
47
+ test -f package.json || npm init --yes
48
+ npm install --save-exact @kisev/skills-opencode@2.0.4
49
+ npm exec -- skills-opencode install --scope global --dry-run
31
50
  ```
32
51
 
33
- Interactive selection доступен только в TTY. В non-TTY install нужно явно передать
34
- `--commands`, `--agents` и `--plugins`; selectable plugins по умолчанию пусты.
35
- Wizard показывает skill commands, package commands, fixed agents и
36
- `rules-injector`, `rtk`, `zed-bell`. Сначала покажите план installer. CLI выводит короткую сводку:
37
- счётчики по группам, только изменяемые paths, conflicts, restart flag, digest и
38
- готовую confirm-команду. Эта команда не меняет deployment:
52
+ Сохраните dependency в `package.json` и lock file этого npm project. Confirmed
53
+ assets размещаются в `~/.config/opencode`.
54
+
55
+ ## Выбор assets
56
+
57
+ В TTY команда `install` открывает четыре группы: Skill command adapters, Package
58
+ command adapters, Fixed agents и Selectable plugins. Две command-группы и шесть
59
+ fixed agents изначально выбраны, optional plugins - нет. Skill command adapters
60
+ это OpenCode slash-команды, загружающие уже установленный одноимённый portable
61
+ skill. Package command adapters вызывают package tools. Выбор adapter не выбирает
62
+ и не устанавливает skill.
63
+
64
+ Вне TTY передайте все три selection group. Этот пример выбирает три commands,
65
+ всех fixed agents и ни одного wrapper:
39
66
 
40
67
  ```shell
41
- npm exec -- skills-opencode install --scope global --dry-run
68
+ npm exec -- skills-opencode install --scope project \
69
+ --commands doctor,reconcile,agent-profiles \
70
+ --agents manager,architect,mapper,worker,review,critic \
71
+ --plugins none --dry-run
42
72
  ```
43
73
 
44
- Проверьте сводку и примените только показанный digest:
74
+ Если вне TTY передан любой selection flag, обязательны `--commands`, `--agents`
75
+ и `--plugins`. Точные текущие имена показывает команда:
45
76
 
46
77
  ```shell
47
- npm exec -- skills-opencode install --scope global --confirm <digest>
78
+ npm exec -- skills-opencode capabilities --json
79
+ ```
80
+
81
+ Selectable wrappers: `rules-injector`, `rtk`, `zed-bell`.
82
+
83
+ ## Активация core plugin
84
+
85
+ Installer записывает, нужна ли selection core integration, но никогда не создаёт
86
+ и не меняет `opencode.json`. Добавьте package в user-owned массив `plugin` для
87
+ того же scope, сохранив существующие entries:
88
+
89
+ ```json
90
+ {
91
+ "$schema": "https://opencode.ai/config.json",
92
+ "plugin": ["@kisev/skills-opencode"]
93
+ }
48
94
  ```
49
95
 
50
- `global` устанавливает assets в `~/.config/opencode/agents`,
51
- `~/.config/opencode/commands` и `~/.config/opencode/plugins`. Для текущего
52
- repository используйте `project`:
96
+ Для project scope храните package в project `node_modules`, а configuration - в
97
+ project. Для global scope храните npm project и user configuration в
98
+ `~/.config/opencode`. После activation или изменения assets перезапустите
99
+ OpenCode.
100
+
101
+ ## Preview и confirm
102
+
103
+ Каждая mutation начинается с `--dry-run`. Preview показывает operations,
104
+ conflicts, необходимость restart, срок действия receipt, отдельные plan и
105
+ confirmation digests и точную confirmation command. Если reconcile показывает
106
+ modified managed files или ownership conflicts, он блокируется: receipt и Apply
107
+ command не создаются. Сначала установите или обновите текущий package,
108
+ примените его exact installer confirmation, затем повторите reconcile; ownership
109
+ conflicts нужно разрешить вручную.
53
110
 
54
111
  ```shell
55
112
  npm exec -- skills-opencode install --scope project --dry-run
56
- npm exec -- skills-opencode install --scope project --confirm <digest>
57
113
  ```
58
114
 
59
- Project assets находятся в `.opencode/agents`, `.opencode/commands` и
60
- `.opencode/plugins` текущего working directory. Scope обязателен. Installer не изменяет `opencode.json`, не
61
- перезаписывает неизвестные или изменённые files и сохраняет ownership manifests
62
- только после confirmed apply.
115
+ Обязательный OpenCode flow: persistent npm install, `install --dry-run`, exact
116
+ confirmation command из preview, добавление package в user-owned `plugin` и
117
+ перезапуск OpenCode. Для global-команд используйте persistent npm project в
118
+ `~/.config/opencode`. Перед каждым reconcile установите или обновите package и
119
+ примените его installer plan.
120
+
121
+ Preview имеет deterministic `plan_digest` и unique `confirmation_digest`. Новый
122
+ dry-run в том же scope supersede-ит любой старый unconsumed preview, включая
123
+ preview другой package или agent operation; старая confirmation отклоняется.
63
124
 
64
- Для automation добавьте `--json`. Этот режим сохраняет полный стабильный
65
- machine-readable plan, включая `operations` и `requires_restart`:
125
+ Выполните команду из preview со всеми selection flags. Receipts приватны,
126
+ действуют 10 минут, применяются один раз и связаны с action, scope, root и
127
+ текущим inventory. Apply отклоняет stale state и unsafe conflicts.
128
+
129
+ ## Doctor
130
+
131
+ `doctor` читает integration facts без создания receipts, recovery journals,
132
+ запуска plugins или LSP servers:
66
133
 
67
134
  ```shell
68
- npm exec -- skills-opencode install --scope global --dry-run --json
69
- npm exec -- skills-opencode agent list --scope global --json
135
+ npm exec -- skills-opencode doctor --scope project
136
+ npm exec -- skills-opencode doctor --scope project --json
70
137
  ```
71
138
 
72
- ## Read-only doctor
139
+ Report содержит versions, ownership, drift, collisions, archive counts, redacted
140
+ configuration projections, runtime summaries и LSP facts. Raw configuration,
141
+ environment values, receipts, credentials и secrets не сериализуются. Exit
142
+ status `0` означает чистое состояние, `1` - findings, `2` - invalid input или
143
+ incomplete probe failure.
73
144
 
74
- `doctor` is a read-only installer health report. It never creates lifecycle
75
- state, consumes receipts, recovers journals, starts plugins or starts LSP
76
- servers. `project` and `global` are isolated scopes; unavailable OpenCode host
77
- facts are reported as incomplete rather than inferred.
145
+ ## Обновление
146
+
147
+ В npm project, которому принадлежит dependency, установите точную нужную version,
148
+ покажите и подтвердите `install` с тем же scope и нужной selection, затем
149
+ перезапустите OpenCode:
78
150
 
79
151
  ```shell
80
- npm exec -- skills-opencode doctor --scope project
81
- npm exec -- skills-opencode doctor --scope global --json
152
+ npm install --save-exact @kisev/skills-opencode@2.0.4
153
+ npm exec -- skills-opencode install --scope project --dry-run
82
154
  ```
83
155
 
84
- The JSON report is versioned and contains stable check IDs, package/catalog and
85
- installed-manifest versions, ownership/drift/collision classifications, stage-8
86
- inventory findings, lifecycle/runtime state summaries, redacted config
87
- projections and LSP facts. Secrets, raw config, environment values, receipts
88
- and credentials are never serialized. Exit code `0` is clean, `1` reports
89
- findings, and `2` means invalid input or an incomplete probe failure.
156
+ Используйте полную confirmation command из preview. Installer обновляет только
157
+ files с совпадающими recorded ownership и SHA-256. User-owned или modified
158
+ managed files остаются conflicts. Package update не сбрасывает выбранные agent
159
+ models, variants, additional critics или retained profile configuration.
90
160
 
91
- ## Reconcile retired assets
161
+ ## Reconcile
92
162
 
93
- `reconcile` проверяет только public portable skills, package commands, plugins,
94
- agents и installation metadata выбранного scope. XDG runtime state не читается и
95
- не изменяется. Preview сохраняет private receipt с TTL и печатает digest:
163
+ `reconcile` классифицирует current и historical portable skills, package
164
+ commands, plugins, agents и installation metadata одного scope:
96
165
 
97
166
  ```shell
98
167
  npm exec -- skills-opencode reconcile --scope project --dry-run
@@ -100,143 +169,63 @@ npm exec -- skills-opencode reconcile --scope project --confirm <digest>
100
169
  npm exec -- skills-opencode reconcile --scope global --dry-run --json
101
170
  ```
102
171
 
103
- Retired files с доказанным inventory ownership и exact SHA-256 перемещаются в
104
- private content-addressed XDG archive с index, а не удаляются. Modified-managed,
105
- user-owned, unknown, symlink, unsafe-path, worktree и ambiguous-source entries
106
- остаются conflicts без изменения bytes. Journal target и archive обеспечивает
107
- rollback, recovery и повторный no-op reconcile. Doctor показывает archive и
108
- conflicts read-only.
172
+ Перед reconcile сначала обновите package в принадлежащем ему npm project и
173
+ примените exact installer plan. Reconcile не устанавливает, не обновляет и не
174
+ удаляет portable skills; для них используйте только pinned
175
+ `npx --yes skills@1.5.23` flow.
109
176
 
110
- Добавьте plugin в `opencode.json` вручную:
111
-
112
- ```json
113
- {
114
- "$schema": "https://opencode.ai/config.json",
115
- "plugin": ["@kisev/skills-opencode"]
116
- }
117
- ```
118
-
119
- Полностью перезапустите OpenCode после install, upgrade или uninstall: registry
120
- agents и commands строится до plugin hooks.
177
+ Confirmed reconcile архивирует exact-owned retired assets в private
178
+ content-addressed XDG archive и удаляет их deployed copies. Modified, user-owned,
179
+ unknown, symlink, unsafe и ambiguous entries остаются без изменений как findings
180
+ или conflicts. Worktrees и runtime state сохраняются. Archive доступен для
181
+ просмотра через `doctor`; команд restore или purge нет.
121
182
 
122
183
  ## Управление agents
123
184
 
124
- Рекомендуемый интерфейс - прямой terminal CLI: он не вызывает LLM и не расходует
125
- токены. Fixed roles `manager`, `architect`, `mapper`, `worker`, `review` и
126
- стандартный `critic` всегда сохраняют имена и canonical prompts/permissions.
127
- Меняются только `model` и `variant`:
185
+ Direct CLI управляет models fixed agents и additional critics без LLM call:
128
186
 
129
187
  ```shell
130
188
  npm exec -- skills-opencode agent list --scope global
131
189
  npm exec -- skills-opencode agent configure manager --scope global --dry-run
132
- npm exec -- skills-opencode agent model-set worker --scope global \
133
- --model openai/gpt-5 --variant high --dry-run
190
+ npm exec -- skills-opencode agent model-set worker --scope global --model openai/gpt-5 --variant high --dry-run
191
+ npm exec -- skills-opencode critic add security --scope global --model anthropic/claude-sonnet-4-6 --dry-run
134
192
  npm exec -- skills-opencode agent reconcile --scope global --dry-run
135
193
  ```
136
194
 
137
- `agent configure` предлагает настоящий terminal wizard: стрелками выбираются
138
- agent из inventory, затем provider, только его models и, если metadata выбранной
139
- модели публикует variants, variant. Текущие model/variant и target показываются
140
- перед выбором; доступны `keep`, `change`, `clear variant`, `back` и `cancel`.
141
- Wizard не вызывает LLM, OpenCode Question или refresh catalog. Если catalog
142
- недоступен, он завершается без записи и печатает инструкцию для exact
143
- `--model <provider/model>` с optional `--variant`. Для модели без variants
144
- дополнительный selector не показывается.
195
+ Fixed roles сохраняют имена, prompts и permissions; меняются только model и
196
+ variant. Additional critics используют `critic-<safe-suffix>`. Каждая mutation
197
+ использует тот же contract preview и one-time confirmation.
145
198
 
146
- Additional critic имеет имя `critic-<safe-suffix>`. Стандартный `critic` и fixed
147
- roles нельзя удалить или переименовать:
199
+ ## Uninstall
148
200
 
149
- ```shell
150
- npm exec -- skills-opencode critic add security --scope global \
151
- --model anthropic/claude-sonnet-4-6 --dry-run
152
- npm exec -- skills-opencode critic remove security --scope global --dry-run
153
- ```
201
+ Package должен оставаться доступным до удаления его assets:
154
202
 
155
- Для любой mutation используйте готовую confirm-команду из preview либо замените
156
- `--dry-run` на `--confirm <digest>` и повторите те же аргументы. Человекочитаемый
157
- plan не печатает полный JSON и сворачивает длинные группы paths. Digest связан с
158
- одноразовым private receipt, действует 10 минут и повторно не применяется. Для
159
- machine-readable result добавьте `--json`; поле `requires_restart` сообщает о
160
- необходимости полностью перезапустить OpenCode.
161
-
162
- В global scope profile configuration хранится в
163
- `~/.config/opencode/.skills-opencode/agent-profiles.json`, а semantic deployment
164
- manifest - рядом в `agent-profiles.manifest.json`. Для project scope те же файлы
165
- находятся под `.opencode/.skills-opencode/`. Configuration хранит выбранные
166
- model/variant и additional critics; package update её не сбрасывает. Manifest
167
- хранит package version, exact critic pool и hashes canonical configuration и
168
- rendered files.
169
-
170
- Inventory различает `package-owned`, `managed`, `user-owned`, `drift` и exact-name
171
- `collision`. User-owned и неизвестные agents не изменяются. Collision блокирует
172
- apply; drift исправляется только явным `agent reconcile`. Все mutations проходят
173
- под lifecycle lock, повторно проверяют inventory, используют journaled
174
- all-or-rollback transaction и выполняют final validation. После прерывания
175
- следующая mutation безопасно восстанавливает before-images и требует свежий plan.
176
-
177
- ## Upgrade и uninstall
178
-
179
- После обновления npm package снова выполните dry-run и подтвердите новый digest.
180
- Installer обновляет только files с совпадающим managed SHA-256.
181
-
182
- При первом upgrade с `1.0.0` installer передаёт ownership шести fixed agents из
183
- generic manifest в profile domain только при точном совпадении package/version,
184
- manifest records и SHA-256 каждого файла. Любое отличие остаётся конфликтом.
185
- Commands и plugins продолжают принадлежать generic installer. Uninstall удаляет
186
- неизменённые deployments, но сохраняет profile configuration для последующей
187
- установки.
203
+ 1. Покажите preview и подтвердите удаление package-owned assets.
204
+ 2. Удалите `@kisev/skills-opencode` из user-owned массива `plugin`.
205
+ 3. Удалите dependency из того же npm project.
206
+ 4. Перезапустите OpenCode.
188
207
 
189
208
  ```shell
190
- npm exec -- skills-opencode uninstall --scope global --dry-run
191
- npm exec -- skills-opencode uninstall --scope global --confirm <digest>
209
+ npm exec -- skills-opencode uninstall --scope project --dry-run
210
+ npm exec -- skills-opencode uninstall --scope project --confirm <digest>
211
+ npm uninstall @kisev/skills-opencode
192
212
  ```
193
213
 
194
- Uninstall удаляет только files из ownership manifest, если их SHA-256 не
195
- изменился. Изменённые пользователем files остаются как `conflict`.
196
-
197
- ## Runtime options
198
-
199
- Package экспортирует core OpenCode plugin и independent plugin factories
200
- `rules-injector`, `rtk`, `zed-bell`. Retired plugin APIs и internal lifecycle
201
- implementations public surface не входят:
202
-
203
- Background Attempts создают workspace только через единый managed worktree
204
- owner. Records используют private current-only state, marker и repository
205
- fingerprint; release перепроверяет регистрацию, fingerprint и чистый status и
206
- возвращает `blocked` без удаления при расхождении. Scheduler принимает строгий
207
- five-field cron, не воспроизводит missed slots и пишет receipts `started`,
208
- `completed`, `failed` или `overrun`.
209
-
210
- `rules-injector` fail-soft применяет ограниченный budget и пропускает native
211
- project/global rules. `rtk` fail-open сжимает большой bash output и добавляет
212
- подсказку для edit error, но не содержит ownership guard.
213
-
214
- ## Routing этапа 18
215
-
216
- `doit` владеет полным lifecycle evidence -> plan -> confirmation -> execution ->
217
- checks -> report; `manager` только адаптирует его к OpenCode. Tool `route` имеет
218
- четыре назначения: exploration -> `mapper`, architecture -> `architect`,
219
- implementation -> `worker`, review -> `review` или один `critic`. Documentation и
220
- quick остаются у `doit`.
221
-
222
- Inventory берётся только из resolved host config. Caller не может подменить agents,
223
- capabilities, tools, models или availability. Versioned receipts, cards и
224
- mapper/worker/review/critic reports проверяются на реальных Task dispatch/result
225
- hooks. Card связывает paths, checks, явные VCS operations и отдельные confirmations
226
- для execution, publication и history rewrite.
214
+ Для global scope выполните тот же flow из `~/.config/opencode` с
215
+ `--scope global`. Uninstall архивирует exact manifest-owned assets и сохраняет
216
+ modified files как conflicts вместе с worktrees, runtime state и retained
217
+ profile configuration. Он не удаляет portable skills и не меняет
218
+ `opencode.json`. Команд restore или purge для archive нет.
227
219
 
228
220
  ## Границы
229
221
 
230
- Portable skills в корне `skills/` универсальны и устанавливаются только через
231
- `npx skills`. Этот package поставляет только OpenCode-specific assets и runtime
232
- router. Команды - тонкие adapters: передают `$ARGUMENTS` как недоверенный ввод
233
- в native Skill tool, а target validation, confirmation, batch/review rules и
234
- формат результата остаются ответственностью skill или runner.
235
- `capabilities`, `route` и `doctor` - package tools/commands для catalog, routing и
236
- health. Tool `doctor` and direct CLI share one read-only facts API; `/doctor` is
237
- only a thin adapter. Tool `agent_profiles` и четыре slash-команды `agent-list`,
238
- `agent-model-set`, `critic-add`, `critic-remove` - optional thin UX над теми же
239
- plan/apply contracts. Отдельного skill `agent-profiles` нет.
240
-
241
- Package распространяется по лицензии MIT. Полные инструкции по portable skills,
242
- upgrade и security boundaries находятся в корневом README репозитория.
222
+ - Portable skills и package assets устанавливаются, обновляются и удаляются
223
+ независимо.
224
+ - Commands, соответствующие skills, являются thin adapters; portable skill
225
+ остаётся authoritative и устанавливается отдельно.
226
+ - Package tools: `capabilities`, `route`, `doctor`, `agent_profiles`, `reconcile`;
227
+ у `route` нет slash command.
228
+ - Installer владеет только files с доказанными manifests и exact hashes.
229
+ - Package распространяется по лицензии MIT. Текущий inventory и checks описаны в
230
+ [инвентаре миграции](../../docs/ru/migration-inventory.md) и
231
+ [документе проверки](../../docs/ru/verification.md).
@@ -1,4 +1,4 @@
1
- import { LifecycleError, type FileMutation, type Scope, type TransactionOptions } from "./lifecycle.js";
1
+ import { LifecycleError, type SupersededPlan, type FileMutation, type Scope, type TransactionOptions } from "./lifecycle.js";
2
2
  export { type Scope as AgentProfileScope } from "./lifecycle.js";
3
3
  export declare const FIXED_AGENT_ROLES: readonly ["manager", "architect", "mapper", "worker", "review", "critic"];
4
4
  export type FixedAgentRole = (typeof FIXED_AGENT_ROLES)[number];
@@ -70,6 +70,9 @@ export type AgentProfilePlan = {
70
70
  root: string;
71
71
  operations: AgentProfileOperation[];
72
72
  critic_pool: string[];
73
+ plan_digest: string;
74
+ confirmation_digest?: string;
75
+ superseded_plan?: SupersededPlan;
73
76
  digest: string;
74
77
  receipt_expires_at?: string;
75
78
  requires_restart: boolean;
@@ -520,9 +520,11 @@ export async function buildAgentProfilePlan(request, scope, cwd = process.cwd(),
520
520
  critic_pool: desired?.critic_pool ?? finalManifest?.critic_pool ?? [],
521
521
  requires_restart: mutations.some((item) => item.path.startsWith("agents/")),
522
522
  };
523
+ const planDigest = planDigestBase(base, inventoryDigest, request);
523
524
  const plan = {
524
525
  ...base,
525
- digest: planDigestBase(base, inventoryDigest, request),
526
+ plan_digest: planDigest,
527
+ digest: planDigest,
526
528
  };
527
529
  return {
528
530
  plan,
@@ -639,8 +641,15 @@ export async function previewAgentProfileChange(request, scope, cwd = process.cw
639
641
  if (await recoverTransaction(deploymentRoot(scope, cwd, home), stateRoot))
640
642
  throw new AgentProfileError("recovered_transaction", "Recovered an interrupted transaction; request a fresh plan");
641
643
  const built = await buildAgentProfilePlan(request, scope, cwd, home);
642
- const receipt = await saveReceipt(stateRoot, `agent:${request.action}`, scope, built.plan.root, { request, digest: built.plan.digest });
643
- return { ...built.plan, digest: receipt.digest, receipt_expires_at: receipt.expires_at };
644
+ const receipt = await saveReceipt(stateRoot, `agent:${request.action}`, scope, built.plan.root, { request, plan_digest: built.plan.digest }, Date.now(), built.plan.digest);
645
+ return {
646
+ ...built.plan,
647
+ plan_digest: built.plan.digest,
648
+ confirmation_digest: receipt.digest,
649
+ digest: receipt.digest,
650
+ receipt_expires_at: receipt.expires_at,
651
+ ...(receipt.superseded_plan ? { superseded_plan: receipt.superseded_plan } : {}),
652
+ };
644
653
  });
645
654
  }
646
655
  export async function applyAgentProfileChange(request, scope, confirmationDigest, cwd = process.cwd(), home = homedir(), options = {}) {
@@ -658,7 +667,7 @@ export async function applyAgentProfileChange(request, scope, confirmationDigest
658
667
  if (stable(receipt.request) !== stable(request))
659
668
  throw new AgentProfileError("confirmation_unknown", "Saved confirmation belongs to a different request");
660
669
  const built = await buildAgentProfilePlan(request, scope, cwd, home);
661
- if (built.plan.digest !== receipt.digest)
670
+ if (built.plan.digest !== receipt.plan_digest)
662
671
  throw new AgentProfileError("stale_plan", "Agent inventory changed after preview");
663
672
  if (built.plan.operations.some((item) => item.operation === "conflict" && item.reason.includes("collision")))
664
673
  throw new AgentProfileError("collision", "Exact-name user-owned collision blocks apply");
package/dist/catalog.d.ts CHANGED
@@ -4,5 +4,5 @@ export declare const CATALOG: {
4
4
  readonly agents: readonly ["manager", "architect", "mapper", "worker", "review", "critic"];
5
5
  readonly package_commands: readonly ["capabilities", "doctor", "reconcile", "agent-profiles"];
6
6
  readonly tools: readonly ["capabilities", "route", "doctor", "agent_profiles", "reconcile"];
7
- readonly version: "2.0.2";
7
+ readonly version: "2.0.4";
8
8
  };
package/dist/catalog.js CHANGED
@@ -34,5 +34,5 @@ export const CATALOG = {
34
34
  agents: ["manager", "architect", "mapper", "worker", "review", "critic"],
35
35
  package_commands: ["capabilities", "doctor", "reconcile", "agent-profiles"],
36
36
  tools: ["capabilities", "route", "doctor", "agent_profiles", "reconcile"],
37
- version: "2.0.2",
37
+ version: "2.0.4",
38
38
  };
@@ -119,9 +119,16 @@ export function renderPlan(plan, options) {
119
119
  ? `Restart required: ${plan.requires_restart ? "yes" : "no"}`
120
120
  : `Restart after apply: ${plan.requires_restart ? "yes" : "no"}`);
121
121
  if (!options.applied) {
122
+ if (plan.superseded_plan)
123
+ lines.push("", "Superseded plan:", ` kind: ${terminalSafe(plan.superseded_plan.kind)}`, ` confirmation: ${terminalSafe(plan.superseded_plan.confirmation_digest)}`, ` created: ${terminalSafe(plan.superseded_plan.created_at)}`, ` expires: ${terminalSafe(plan.superseded_plan.expires_at)}`);
122
124
  if (plan.receipt_expires_at)
123
125
  lines.push(`Confirmation expires: ${plan.receipt_expires_at}`);
124
- lines.push(`Digest: ${plan.digest}`);
126
+ if ("plan_digest" in plan)
127
+ lines.push(`Plan digest: ${plan.plan_digest}`);
128
+ if (plan.confirmation_digest ?? plan.digest) {
129
+ lines.push(`Confirmation digest: ${plan.confirmation_digest ?? plan.digest}`);
130
+ lines.push(`Digest: ${plan.confirmation_digest ?? plan.digest}`);
131
+ }
125
132
  if (options.confirmationCommand)
126
133
  lines.push("", "Apply:", ` ${options.confirmationCommand}`);
127
134
  }
@@ -179,15 +186,28 @@ export function renderReconcile(plan, options) {
179
186
  if (plan.retired.length)
180
187
  lines.push("", "Retired:", ...plan.retired.slice(0, 20).map((entry) => ` ${terminalSafe(entry.path)}`));
181
188
  if (plan.conflicts.length)
182
- lines.push("", "Conflicts:", ...plan.conflicts
183
- .slice(0, 20)
184
- .map((entry) => ` ${terminalSafe(entry.path)} (${terminalSafe(entry.reason)})`));
189
+ lines.push("", "Conflicts:", ...plan.conflicts.map((entry) => ` ${terminalSafe(entry.path)} (${terminalSafe(entry.reason)})`));
190
+ if (plan.modified_managed.length)
191
+ lines.push("", "Modified managed:", ...plan.modified_managed.map((entry) => ` ${terminalSafe(entry.path)} (${terminalSafe(entry.reason)})`));
185
192
  if (!options.applied) {
186
- lines.push("", `Digest: ${plan.digest}`);
187
- if (plan.receipt_expires_at)
188
- lines.push(`Confirmation expires: ${plan.receipt_expires_at}`);
189
- if (options.confirmationCommand)
193
+ const blocked = plan.modified_managed.length > 0 || plan.conflicts.length > 0;
194
+ if (blocked) {
195
+ lines.push("", "Blocked:");
196
+ if (plan.modified_managed.length)
197
+ lines.push(` Update managed assets first: ${shellCommand(["install", "--scope", plan.scope, "--dry-run"])}`, " Apply the exact confirmation command from that installer preview, then build a new reconcile preview.");
198
+ if (plan.conflicts.length)
199
+ lines.push(" Manually resolve every ownership conflict listed above before reconciling.");
200
+ }
201
+ else if (options.confirmationCommand) {
202
+ if (plan.superseded_plan)
203
+ lines.push("", "Superseded plan:", ` kind: ${terminalSafe(plan.superseded_plan.kind)}`, ` confirmation: ${terminalSafe(plan.superseded_plan.confirmation_digest)}`, ` created: ${terminalSafe(plan.superseded_plan.created_at)}`, ` expires: ${terminalSafe(plan.superseded_plan.expires_at)}`);
204
+ lines.push("", `Plan digest: ${plan.plan_digest}`);
205
+ if (plan.receipt_expires_at)
206
+ lines.push(`Confirmation expires: ${plan.receipt_expires_at}`);
207
+ if (plan.confirmation_digest ?? plan.digest)
208
+ lines.push(`Confirmation digest: ${plan.confirmation_digest ?? plan.digest}`, `Digest: ${plan.confirmation_digest ?? plan.digest}`);
190
209
  lines.push("", "Apply:", ` ${options.confirmationCommand}`);
210
+ }
191
211
  }
192
212
  return `${lines.join("\n")}\n`;
193
213
  }
package/dist/cli.js CHANGED
@@ -100,6 +100,14 @@ function help() {
100
100
  async function interactiveInstallerSelection() {
101
101
  if (!process.stdin.isTTY || !process.stderr.isTTY)
102
102
  throw new InstallerError("terminal_required", "install requires explicit selection flags outside a terminal");
103
+ process.stderr.write([
104
+ "Portable skills are installed separately through npx skills.",
105
+ "This installer does not install, update, or remove portable skills.",
106
+ "Skill command adapters are OpenCode slash commands that load an already-installed skill with the same name.",
107
+ "Package command adapters invoke package tools.",
108
+ "Selecting a command adapter does not select or install its skill.",
109
+ "\n",
110
+ ].join("\n"));
103
111
  const group = async (label, names, initial) => {
104
112
  const choices = ["Select all", "Select none", ...names];
105
113
  const selected = await selectOption(label, choices, process.stdin, process.stderr, initial);
@@ -112,8 +120,8 @@ async function interactiveInstallerSelection() {
112
120
  return [names[selected - 2]];
113
121
  };
114
122
  const commands = [
115
- ...(await group("Skill commands", SKILL_COMMANDS, 0)),
116
- ...(await group("Package commands", PACKAGE_COMMANDS, 0)),
123
+ ...(await group("Skill command adapters", SKILL_COMMANDS, 0)),
124
+ ...(await group("Package command adapters", PACKAGE_COMMANDS, 0)),
117
125
  ];
118
126
  const agents = await group("Fixed agents", defaultSelection().agents, 0);
119
127
  const plugins = await group("Selectable plugins (none selected by default)", SELECTABLE_PLUGINS, 1);
@@ -345,7 +353,7 @@ async function run(arguments_) {
345
353
  if (options.json)
346
354
  process.stdout.write(`${JSON.stringify({ status: "ok", applied: false, plan }, null, 2)}\n`);
347
355
  else
348
- process.stdout.write(renderReconcile(plan, { applied: false, confirmationCommand: shellCommand(["reconcile", "--scope", options.scope, "--confirm", plan.digest]) }));
356
+ process.stdout.write(renderReconcile(plan, { applied: false, confirmationCommand: plan.confirmable ? shellCommand(["reconcile", "--scope", options.scope, "--confirm", plan.confirmation_digest ?? plan.digest]) : undefined }));
349
357
  }
350
358
  else {
351
359
  const applied = await applyReconcile(options.scope, options.confirm);
@@ -1,4 +1,4 @@
1
- import { LifecycleError, type FileMutation, type Scope, type TransactionOptions } from "./lifecycle.js";
1
+ import { LifecycleError, type SupersededPlan, type FileMutation, type Scope, type TransactionOptions } from "./lifecycle.js";
2
2
  import { CATALOG } from "./catalog.js";
3
3
  import { type FixedAgentRole } from "./agent-profiles.js";
4
4
  export type { Scope } from "./lifecycle.js";
@@ -25,6 +25,9 @@ export type Plan = {
25
25
  package_version: string;
26
26
  selection: InstallerSelection;
27
27
  operations: PlanItem[];
28
+ plan_digest: string;
29
+ confirmation_digest?: string;
30
+ superseded_plan?: SupersededPlan;
28
31
  digest: string;
29
32
  receipt_expires_at?: string;
30
33
  requires_restart: boolean;