opencode-orchestrator-uni 0.4.0 → 0.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/lib/commands.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Slash commands of the kit: `/agents-md` and `/spec`.
2
+ * Slash commands of the kit: `/agents-md`, `/spec` and `/handoff`.
3
3
  *
4
4
  * Commands are prompts, not writers: `execute` injects a short workflow prompt
5
5
  * into the current session, and the orchestrator runs it with the usual
6
6
  * evidence/approval rules. The detailed templates and checklists live in
7
- * `docs/<locale>/context-efficiency.md`.
7
+ * `docs/<locale>/context-efficiency.md` and `docs/<locale>/session-discipline.md`.
8
8
  */
9
9
  import type { Plugin } from "@opencode/plugin"
10
10
 
@@ -12,17 +12,22 @@ type CommandLocale = "ru" | "en"
12
12
 
13
13
  /** Arguments typed after the slash command, without a duplicated command token. */
14
14
  function commandArgs(text: string): string {
15
- return text.replace(/^\s*\/(?:agents-md|spec)\b/i, "").trim()
15
+ return text.replace(/^\s*\/(?:agents-md|spec|handoff)\b/i, "").trim()
16
16
  }
17
17
 
18
- const DESCRIPTIONS: Record<CommandLocale, { readonly agentsMd: string; readonly spec: string }> = {
18
+ const DESCRIPTIONS: Record<
19
+ CommandLocale,
20
+ { readonly agentsMd: string; readonly spec: string; readonly handoff: string }
21
+ > = {
19
22
  ru: {
20
23
  agentsMd: "Создать или проверить AGENTS.md по шаблону context-efficiency.md.",
21
24
  spec: "Создать или проверить SPEC.md по шаблону context-efficiency.md.",
25
+ handoff: "Сформировать handoff среза по шаблону session-discipline.md.",
22
26
  },
23
27
  en: {
24
28
  agentsMd: "Create or audit AGENTS.md using the context-efficiency.md template.",
25
29
  spec: "Create or audit SPEC.md using the context-efficiency.md template.",
30
+ handoff: "Write the slice handoff using the session-discipline.md template.",
26
31
  },
27
32
  }
28
33
 
@@ -84,14 +89,43 @@ function specPrompt(locale: CommandLocale, brief: string): string {
84
89
  ].join("\n")
85
90
  }
86
91
 
87
- /** Register `/agents-md` and `/spec`; the cleanup disposes the transform registration. */
92
+ function handoffPrompt(locale: CommandLocale, goal: string): string {
93
+ if (locale === "ru") {
94
+ return [
95
+ "[Команда /handoff]",
96
+ `Срез: ${goal}.`,
97
+ "",
98
+ "Работай по документу session-discipline.md из reference audit-kit.",
99
+ "1. Собери факты только из текущей сессии и рабочего дерева: что сделано, чем подтверждено (команды, тесты, коммиты), что не сделано и почему.",
100
+ "2. Заполни шаблон handoff из session-discipline.md: цель и DoD, сделано, не сделано, решения и причины, риски/принятые ограничения, следующий шаг.",
101
+ "3. Запиши файл `docs/planning/handoff-<YYYY-MM-DD>.md` (нет каталога — корень проекта) инструментом `write`, не shell-heredoc.",
102
+ "4. Ничего не додумывай: неизвестное помечай «не проверено»; секреты и токены в handoff не переноси.",
103
+ "5. Покажи путь файла, краткий diff и открытые вопросы; канонические документы не меняй без подтверждения.",
104
+ "6. Если дерево грязное — предложи коммит по правилам проекта; не коммить без подтверждения владельца.",
105
+ ].join("\n")
106
+ }
107
+ return [
108
+ "[Command /handoff]",
109
+ `Slice: ${goal}.`,
110
+ "",
111
+ "Follow session-discipline.md from the audit-kit reference.",
112
+ "1. Collect facts from the current session and the working tree only: what is done, what proves it (commands, tests, commits), what is not done and why.",
113
+ "2. Fill the handoff template from session-discipline.md: goal and DoD, done, not done, decisions and reasons, risks/accepted limits, next step.",
114
+ "3. Write `docs/planning/handoff-<YYYY-MM-DD>.md` (use the project root when the directory is absent) with the `write` tool, not a shell heredoc.",
115
+ "4. Do not invent facts: mark the unknown as `unverified`; never copy secrets or tokens into the handoff.",
116
+ "5. Show the file path, a short diff and open questions; do not change canonical documents without approval.",
117
+ "6. If the working tree is dirty, propose a commit per the project rules; do not commit without the owner's approval.",
118
+ ].join("\n")
119
+ }
120
+
121
+ /** Register `/agents-md`, `/spec` and `/handoff`; the cleanup disposes the transform registration. */
88
122
  export async function registerKitCommands(ctx: Plugin.Context, locale: CommandLocale): Promise<Plugin.Cleanup> {
89
123
  // The command API is optional in a host build. A missing `list` was already
90
124
  // tolerated; a missing `transform` used to throw out of `setup` and take every
91
125
  // agent and tool with it, so it degrades to a warning plus a no-op cleanup.
92
126
  const command = (ctx as Partial<Plugin.Context>).command
93
127
  if (command === undefined || typeof command.transform !== "function") {
94
- console.warn("orch-uni: this host has no command API; /agents-md and /spec are not registered")
128
+ console.warn("orch-uni: this host has no command API; /agents-md, /spec and /handoff are not registered")
95
129
  return async () => {}
96
130
  }
97
131
  let existing = new Set<string>()
@@ -141,6 +175,28 @@ export async function registerKitCommands(ctx: Plugin.Context, locale: CommandLo
141
175
  },
142
176
  })
143
177
  }
178
+ if (!existing.has("handoff")) {
179
+ editor.add({
180
+ name: "handoff",
181
+ description: DESCRIPTIONS[locale].handoff,
182
+ execute: async (input) => {
183
+ await ctx.session.prompt({
184
+ sessionID: input.sessionID,
185
+ text: handoffPrompt(
186
+ locale,
187
+ commandArgs(input.prompt.text) ||
188
+ (locale === "ru"
189
+ ? "текущая сессия — опиши срез по фактам"
190
+ : "the current session — describe the slice from facts"),
191
+ ),
192
+ ...(input.prompt.files === undefined ? {} : { files: input.prompt.files }),
193
+ ...(input.prompt.agents === undefined ? {} : { agents: input.prompt.agents }),
194
+ ...(input.prompt.skills === undefined ? {} : { skills: input.prompt.skills }),
195
+ delivery: input.delivery,
196
+ })
197
+ },
198
+ })
199
+ }
144
200
  })
145
201
  return async () => {
146
202
  await registration.dispose()