Browse Source

chore: prepare 2.0.6 release

Zerdeşt Taifour 1 month ago
parent
commit
482db66c3d

+ 3 - 1
README.md

@@ -198,6 +198,8 @@ verification while specialists do the work in their own lanes.
 - **[oh-my-opencode-slim skill](#oh-my-opencode-slim-skill)** — a bundled
 - **[oh-my-opencode-slim skill](#oh-my-opencode-slim-skill)** — a bundled
   configuration skill that helps tune models, prompts, custom agents, MCP access,
   configuration skill that helps tune models, prompts, custom agents, MCP access,
   presets, and plugin behavior safely.
   presets, and plugin behavior safely.
+- **Release Smoke Test** — validates packed release candidates and bugfixes
+  before public publish with isolated OpenCode runtime smokes.
 
 
 #### Background Agents
 #### Background Agents
 
 
@@ -662,7 +664,7 @@ Use this section as a map: start with installation, then jump to features, confi
 | **[Configuration](docs/configuration.md)** | Config file locations, JSONC support, prompt overrides, and full option reference |
 | **[Configuration](docs/configuration.md)** | Config file locations, JSONC support, prompt overrides, and full option reference |
 | **[Background Orchestration](docs/background-orchestration.md)** | Scheduler-first orchestrator model built around native background subagents |
 | **[Background Orchestration](docs/background-orchestration.md)** | Scheduler-first orchestrator model built around native background subagents |
 | **[Maintainer Guide](docs/maintainers.md)** | Issue triage rules, label meanings, support routing, and repo maintenance workflow |
 | **[Maintainer Guide](docs/maintainers.md)** | Issue triage rules, label meanings, support routing, and repo maintenance workflow |
-| **[Skills](docs/skills.md)** | Bundled skills such as `simplify`, `codemap`, `clonedeps`, `deepwork`, `reflect`, `worktrees`, and `oh-my-opencode-slim` |
+| **[Skills](docs/skills.md)** | Bundled skills such as `simplify`, `codemap`, `clonedeps`, `deepwork`, `reflect`, `worktrees`, `release-smoke-test`, and `oh-my-opencode-slim` |
 | **[MCPs](docs/mcps.md)** | `websearch`, `context7`, `gh_grep`, and how MCP permissions work per agent |
 | **[MCPs](docs/mcps.md)** | `websearch`, `context7`, `gh_grep`, and how MCP permissions work per agent |
 | **[Tools](docs/tools.md)** | Built-in tool capabilities like `webfetch`, LSP tools, code search, and formatters |
 | **[Tools](docs/tools.md)** | Built-in tool capabilities like `webfetch`, LSP tools, code search, and formatters |
 
 

+ 27 - 0
docs/skills.md

@@ -18,6 +18,7 @@ Bundled skills are installed by the `oh-my-opencode-slim` installer.
 | [`deepwork`](#deepwork) | Heavy/complex coding sessions workflow | `orchestrator` |
 | [`deepwork`](#deepwork) | Heavy/complex coding sessions workflow | `orchestrator` |
 | [`reflect`](#reflect) | Review repeated work and suggest reusable workflow improvements | `orchestrator` |
 | [`reflect`](#reflect) | Review repeated work and suggest reusable workflow improvements | `orchestrator` |
 | [`worktrees`](#worktrees) | Safe Git worktree lane management | `orchestrator` |
 | [`worktrees`](#worktrees) | Safe Git worktree lane management | `orchestrator` |
+| [`release-smoke-test`](#release-smoke-test) | Packed release-candidate and bugfix smoke validation | `orchestrator` |
 | [`oh-my-opencode-slim`](#oh-my-opencode-slim) | Plugin configuration and self-improvement guidance | `orchestrator` |
 | [`oh-my-opencode-slim`](#oh-my-opencode-slim) | Plugin configuration and self-improvement guidance | `orchestrator` |
 
 
 ---
 ---
@@ -210,6 +211,32 @@ This should apply on the next OpenCode run; restart OpenCode if you need it imme
 
 
 ---
 ---
 
 
+## release-smoke-test
+
+**Validate packed release candidates and bugfixes before public publish.**
+
+`release-smoke-test` is an orchestrator-only skill for proving a release branch
+works as an installed package artifact. It builds and packs the candidate,
+installs the tarball into a throwaway app, runs OpenCode with a sanitized
+temporary config, verifies the active `plugin_origins`, and searches isolated
+logs for the crash signature being fixed.
+
+Use it for release hardening, runtime compatibility checks, and model-specific
+smokes such as OpenCode 1.17.11 malformed message transform regressions.
+
+Typical request:
+
+```text
+Use release-smoke-test to validate this release candidate before npm publish.
+```
+
+The skill distinguishes fully isolated smokes from host-provider smokes. If a
+model such as GPT-5.5 Fast needs provider aliases from the current machine, the
+skill records that limitation instead of treating it as equivalent to a clean
+`env -i` smoke.
+
+---
+
 ## Skills Assignment
 ## Skills Assignment
 
 
 Control which skills each agent can use in `~/.config/opencode/oh-my-opencode-slim.json` (or `.jsonc`):
 Control which skills each agent can use in `~/.config/opencode/oh-my-opencode-slim.json` (or `.jsonc`):

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "oh-my-opencode-slim",
   "name": "oh-my-opencode-slim",
-  "version": "2.0.5",
+  "version": "2.0.6",
   "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
   "description": "Lightweight agent orchestration plugin for OpenCode - a slimmed-down fork of oh-my-opencode",
   "main": "dist/index.js",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",
   "types": "dist/index.d.ts",

+ 1 - 0
scripts/verify-release-artifact.ts

@@ -37,6 +37,7 @@ const packagedRequiredFiles = [
   'src/skills/deepwork/SKILL.md',
   'src/skills/deepwork/SKILL.md',
   'src/skills/reflect/SKILL.md',
   'src/skills/reflect/SKILL.md',
   'src/skills/oh-my-opencode-slim/SKILL.md',
   'src/skills/oh-my-opencode-slim/SKILL.md',
+  'src/skills/release-smoke-test/SKILL.md',
   'src/skills/worktrees/SKILL.md',
   'src/skills/worktrees/SKILL.md',
 ];
 ];
 
 

+ 1 - 0
src/agents/index.test.ts

@@ -296,6 +296,7 @@ describe('skill permissions', () => {
     // CUSTOM_SKILLS loop must also add a named codemap entry for orchestrator
     // CUSTOM_SKILLS loop must also add a named codemap entry for orchestrator
     expect(skillPerm?.codemap).toBe('allow');
     expect(skillPerm?.codemap).toBe('allow');
     expect(skillPerm?.clonedeps).toBe('allow');
     expect(skillPerm?.clonedeps).toBe('allow');
+    expect(skillPerm?.['release-smoke-test']).toBe('allow');
   });
   });
 
 
   test('fixer does not get codemap skill allowed by default', () => {
   test('fixer does not get codemap skill allowed by default', () => {

+ 7 - 0
src/cli/custom-skills.ts

@@ -67,6 +67,13 @@ export const CUSTOM_SKILLS: CustomSkill[] = [
     allowedAgents: ['orchestrator'],
     allowedAgents: ['orchestrator'],
     sourcePath: 'src/skills/oh-my-opencode-slim',
     sourcePath: 'src/skills/oh-my-opencode-slim',
   },
   },
+  {
+    name: 'release-smoke-test',
+    description:
+      'Validate packed release candidates and bugfixes before public publish',
+    allowedAgents: ['orchestrator'],
+    sourcePath: 'src/skills/release-smoke-test',
+  },
   {
   {
     name: 'worktrees',
     name: 'worktrees',
     description:
     description:

+ 1 - 0
src/cli/skills.test.ts

@@ -25,6 +25,7 @@ describe('skills permissions', () => {
     expect(orchestratorPerms.clonedeps).toBe('allow');
     expect(orchestratorPerms.clonedeps).toBe('allow');
     expect(orchestratorPerms.deepwork).toBe('allow');
     expect(orchestratorPerms.deepwork).toBe('allow');
     expect(orchestratorPerms.reflect).toBe('allow');
     expect(orchestratorPerms.reflect).toBe('allow');
+    expect(orchestratorPerms['release-smoke-test']).toBe('allow');
     expect(orchestratorPerms.worktrees).toBe('allow');
     expect(orchestratorPerms.worktrees).toBe('allow');
     expect(orchestratorPerms['oh-my-opencode-slim']).toBe('allow');
     expect(orchestratorPerms['oh-my-opencode-slim']).toBe('allow');
   });
   });

+ 3 - 3
src/hooks/filter-available-skills/index.ts

@@ -119,9 +119,9 @@ export function createFilterAvailableSkillsHook(
       _input: Record<string, never>,
       _input: Record<string, never>,
       output: { messages?: unknown },
       output: { messages?: unknown },
     ): Promise<void> => {
     ): Promise<void> => {
-      const messages = (Array.isArray(output.messages) ? output.messages : []).filter(
-        isMessageWithParts,
-      );
+      const messages = (
+        Array.isArray(output.messages) ? output.messages : []
+      ).filter(isMessageWithParts);
       if (messages.length === 0) {
       if (messages.length === 0) {
         return;
         return;
       }
       }

+ 1 - 1
src/hooks/phase-reminder/index.ts

@@ -7,7 +7,7 @@
  */
  */
 import { PHASE_REMINDER } from '../../config/constants';
 import { PHASE_REMINDER } from '../../config/constants';
 import { SLIM_INTERNAL_INITIATOR_MARKER } from '../../utils';
 import { SLIM_INTERNAL_INITIATOR_MARKER } from '../../utils';
-import { isUserMessageWithParts, type MessageWithParts } from '../types';
+import { isUserMessageWithParts } from '../types';
 
 
 export { PHASE_REMINDER };
 export { PHASE_REMINDER };
 
 

+ 3 - 1
src/hooks/task-session-manager/index.test.ts

@@ -55,7 +55,9 @@ describe('task-session-manager hook', () => {
     const messages = {
     const messages = {
       messages: [
       messages: [
         {},
         {},
-        { info: { role: 'user', agent: 'orchestrator', sessionID: 'parent-1' } },
+        {
+          info: { role: 'user', agent: 'orchestrator', sessionID: 'parent-1' },
+        },
         { parts: [{ type: 'text', text: 'missing info' }] },
         { parts: [{ type: 'text', text: 'missing info' }] },
         {
         {
           info: { role: 'assistant' },
           info: { role: 'assistant' },

+ 3 - 1
src/hooks/types.ts

@@ -25,7 +25,9 @@ export type MessageWithParts = {
   parts: MessagePart[];
   parts: MessagePart[];
 };
 };
 
 
-export function isMessageWithParts(message: unknown): message is MessageWithParts {
+export function isMessageWithParts(
+  message: unknown,
+): message is MessageWithParts {
   if (!message || typeof message !== 'object') {
   if (!message || typeof message !== 'object') {
     return false;
     return false;
   }
   }

+ 7 - 12
src/index.ts

@@ -32,11 +32,7 @@ import {
   ForegroundFallbackManager,
   ForegroundFallbackManager,
 } from './hooks';
 } from './hooks';
 import { processImageAttachments } from './hooks/image-hook';
 import { processImageAttachments } from './hooks/image-hook';
-import {
-  isMessageWithParts,
-  isUserMessageWithParts,
-  type MessageWithParts,
-} from './hooks/types';
+import { isMessageWithParts, isUserMessageWithParts } from './hooks/types';
 import { createInterviewManager } from './interview';
 import { createInterviewManager } from './interview';
 import { createBuiltinMcps } from './mcp';
 import { createBuiltinMcps } from './mcp';
 import {
 import {
@@ -1037,9 +1033,9 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
       input: Record<string, never>,
       input: Record<string, never>,
       output: { messages?: unknown },
       output: { messages?: unknown },
     ): Promise<void> => {
     ): Promise<void> => {
-      const messages = (Array.isArray(output.messages) ? output.messages : []).filter(
-        isMessageWithParts,
-      );
+      const messages = (
+        Array.isArray(output.messages) ? output.messages : []
+      ).filter(isMessageWithParts);
 
 
       for (const message of messages) {
       for (const message of messages) {
         if (!isUserMessageWithParts(message)) {
         if (!isUserMessageWithParts(message)) {
@@ -1069,10 +1065,9 @@ const OhMyOpenCodeLite: Plugin = async (ctx) => {
         input,
         input,
         { messages },
         { messages },
       );
       );
-      await phaseReminderHook['experimental.chat.messages.transform'](
-        input,
-        { messages },
-      );
+      await phaseReminderHook['experimental.chat.messages.transform'](input, {
+        messages,
+      });
       await filterAvailableSkillsHook['experimental.chat.messages.transform'](
       await filterAvailableSkillsHook['experimental.chat.messages.transform'](
         input,
         input,
         { messages },
         { messages },

+ 3 - 1
src/skills/codemap.md

@@ -22,6 +22,7 @@
   - `src/skills/reflect/` (orchestrator-only workflow for learning from repeated work and suggesting reusable improvements)
   - `src/skills/reflect/` (orchestrator-only workflow for learning from repeated work and suggesting reusable improvements)
   - `src/skills/worktrees/` (orchestrator-only workflow for safe Git worktree lanes)
   - `src/skills/worktrees/` (orchestrator-only workflow for safe Git worktree lanes)
   - `src/skills/oh-my-opencode-slim/` (orchestrator-only plugin configuration and self-improvement guidance)
   - `src/skills/oh-my-opencode-slim/` (orchestrator-only plugin configuration and self-improvement guidance)
+  - `src/skills/release-smoke-test/` (orchestrator-only workflow for packed release-candidate runtime validation)
 - Files are considered static runtime payload. No plugin TS module in `src/` imports these files directly; they
 - Files are considered static runtime payload. No plugin TS module in `src/` imports these files directly; they
   are loaded by OpenCode via filesystem installation.
   are loaded by OpenCode via filesystem installation.
 
 
@@ -44,6 +45,7 @@
   bundled skill payloads such as `src/skills/simplify/SKILL.md`,
   bundled skill payloads such as `src/skills/simplify/SKILL.md`,
   `src/skills/codemap/SKILL.md`, `src/skills/clonedeps/SKILL.md`, and
   `src/skills/codemap/SKILL.md`, `src/skills/clonedeps/SKILL.md`, and
   `src/skills/deepwork/SKILL.md`, `src/skills/reflect/SKILL.md`,
   `src/skills/deepwork/SKILL.md`, `src/skills/reflect/SKILL.md`,
-  `src/skills/worktrees/SKILL.md`, plus `src/skills/oh-my-opencode-slim/SKILL.md`,
+  `src/skills/worktrees/SKILL.md`, `src/skills/release-smoke-test/SKILL.md`,
+  plus `src/skills/oh-my-opencode-slim/SKILL.md`,
   are present in the tarball.
   are present in the tarball.
 - `package.json` scripts (`verify:release`, `build`) rely on these assets to ensure install-time skill availability.
 - `package.json` scripts (`verify:release`, `build`) rely on these assets to ensure install-time skill availability.

+ 160 - 0
src/skills/release-smoke-test/SKILL.md

@@ -0,0 +1,160 @@
+---
+name: release-smoke-test
+description: Test an oh-my-opencode-slim release candidate or bugfix before publishing. Use when validating a packed plugin artifact, release branch, crash fix, OpenCode runtime compatibility, or model-specific smoke test such as OpenCode 1.17.11 message transform regressions.
+---
+
+# Release Smoke Test
+
+Use this skill to validate an `oh-my-opencode-slim` release candidate before
+public npm publish. Test the packed artifact, not `@latest` and not the source
+tree.
+
+## Core Workflow
+
+1. Start from the release-prep branch or commit.
+2. Build and pack the candidate.
+3. Install the tarball into a throwaway app.
+4. Create an isolated OpenCode config pointing at the installed
+   `node_modules/oh-my-opencode-slim/dist/index.js`.
+5. Run `opencode debug config` and verify `plugin_origins` contains only the
+   intended plugin when doing an isolation smoke.
+6. Run non-pure `opencode run --print-logs --log-level DEBUG`.
+7. Search isolated logs for the original crash signature.
+8. Record exact artifact, model, OpenCode version, command shape, result, and
+   limitations on the release issue or PR.
+
+## Pack Candidate
+
+Use a temp directory so release validation never depends on the local package
+cache.
+
+```bash
+SMOKE=/tmp/oh-my-opencode-slim-release-smoke
+rm -rf "$SMOKE"
+mkdir -p "$SMOKE/pkg" "$SMOKE/app" "$SMOKE/home" "$SMOKE/xdg/opencode" "$SMOKE/run"
+
+bun run build
+npm pack --pack-destination "$SMOKE/pkg"
+```
+
+Install the tarball:
+
+```bash
+cd "$SMOKE/app"
+bun init -y
+bun add "$SMOKE/pkg"/oh-my-opencode-slim-*.tgz
+node -p "require('./node_modules/oh-my-opencode-slim/package.json').version"
+```
+
+## Isolated Config
+
+Write the minimal OpenCode config:
+
+```bash
+cat > "$SMOKE/xdg/opencode/opencode.json" <<EOF
+{
+  "model": "opencode/deepseek-v4-flash-free",
+  "plugin": [
+    "file://$SMOKE/app/node_modules/oh-my-opencode-slim/dist/index.js"
+  ],
+  "agent": {
+    "orchestrator": {
+      "model": "opencode/deepseek-v4-flash-free"
+    }
+  }
+}
+EOF
+```
+
+Use `env -i` for the cleanest smoke. This strips host `OPENCODE_*`, `ORCA_*`,
+and project overlay variables that can silently add plugins or provider aliases.
+
+```bash
+env -i PATH="$PATH" HOME="$SMOKE/home" XDG_CONFIG_HOME="$SMOKE/xdg" \
+  opencode debug config
+```
+
+Confirm:
+
+- `plugin_origins` has exactly one entry.
+- That entry points to the temp app's packed `dist/index.js`.
+- The model is the one intended for the smoke.
+
+If OpenCode needs provider aliases from the host environment, run a second
+non-isolated model-specific smoke and clearly label it as weaker isolation.
+
+## Runtime Smoke
+
+Run the actual prompt with timeout:
+
+```bash
+env -i PATH="$PATH" HOME="$SMOKE/home" XDG_CONFIG_HOME="$SMOKE/xdg" \
+  timeout 120 \
+  opencode run --print-logs --log-level DEBUG "Say OK only."
+```
+
+Expected result:
+
+```text
+OK
+```
+
+Search logs for the bug signature. For the OpenCode 1.17.11 malformed-message
+crash, use:
+
+```bash
+rg "message\\.info\\.role|undefined is not an object|Cannot read properties of undefined|TypeError" \
+  "$SMOKE/home/.local/share/opencode/log" -n 2>/dev/null || true
+```
+
+No matches should appear.
+
+## OpenAI / Host-Provider Smoke
+
+If the fully isolated environment cannot resolve OpenAI provider aliases, run a
+separate host-provider smoke while keeping the plugin path pointed at the
+tarball install.
+
+```bash
+mkdir -p "$SMOKE/config"
+cat > "$SMOKE/config/opencode.json" <<EOF
+{
+  "model": "openai/gpt-5.5-fast",
+  "plugin": [
+    "file://$SMOKE/app/node_modules/oh-my-opencode-slim/dist/index.js"
+  ],
+  "agent": {
+    "orchestrator": {
+      "model": "openai/gpt-5.5-fast"
+    }
+  }
+}
+EOF
+
+OPENCODE_CONFIG_DIR="$SMOKE/config" \
+  timeout 120 \
+  opencode run --print-logs --log-level DEBUG "Say OK only."
+```
+
+Report this as a host-provider smoke because existing project, user, or Orca
+OpenCode config may still merge in. Use `opencode debug config` to disclose
+what else loaded.
+
+## Reporting Template
+
+```markdown
+## Release-candidate smoke validation
+
+- Commit under test:
+- Tarball:
+- Installed package version:
+- OpenCode version:
+- Config isolation: sanitized `env -i` / host-provider
+- Plugin origin:
+- Model:
+- Command:
+- Result:
+- Crash signature search:
+- Limitations:
+```
+