oh-my-opencode-slim 2.0.4 → 2.1.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.
Files changed (61) hide show
  1. package/README.ja-JP.md +61 -21
  2. package/README.ko-KR.md +59 -19
  3. package/README.md +73 -20
  4. package/README.zh-CN.md +62 -24
  5. package/dist/agents/council.d.ts +1 -1
  6. package/dist/agents/index.d.ts +7 -2
  7. package/dist/agents/orchestrator.d.ts +1 -1
  8. package/dist/agents/permissions.d.ts +10 -0
  9. package/dist/cli/custom-skills-registry.d.ts +18 -0
  10. package/dist/cli/custom-skills.d.ts +3 -19
  11. package/dist/cli/index.js +1093 -186
  12. package/dist/cli/providers.d.ts +5 -9
  13. package/dist/cli/skills.d.ts +3 -3
  14. package/dist/companion/manager.d.ts +7 -0
  15. package/dist/config/constants.d.ts +3 -2
  16. package/dist/config/loader.d.ts +5 -2
  17. package/dist/config/schema.d.ts +7 -0
  18. package/dist/council/council-manager.d.ts +1 -1
  19. package/dist/hooks/auto-update-checker/skill-sync.d.ts +59 -1
  20. package/dist/hooks/filter-available-skills/index.d.ts +1 -2
  21. package/dist/hooks/foreground-fallback/index.d.ts +5 -1
  22. package/dist/hooks/image-hook.d.ts +1 -1
  23. package/dist/hooks/index.d.ts +1 -0
  24. package/dist/hooks/loop-command/index.d.ts +13 -0
  25. package/dist/hooks/phase-reminder/index.d.ts +1 -2
  26. package/dist/hooks/task-session-manager/index.d.ts +1 -2
  27. package/dist/hooks/task-session-manager/pending-call-tracker.d.ts +13 -0
  28. package/dist/hooks/task-session-manager/task-context-tracker.d.ts +14 -0
  29. package/dist/hooks/types.d.ts +3 -1
  30. package/dist/index.js +3491 -876
  31. package/dist/interview/dashboard-manager.d.ts +21 -0
  32. package/dist/interview/dashboard.d.ts +5 -0
  33. package/dist/interview/document.d.ts +4 -1
  34. package/dist/interview/manager.d.ts +0 -14
  35. package/dist/interview/server.d.ts +2 -0
  36. package/dist/interview/service.d.ts +9 -0
  37. package/dist/interview/session-server.d.ts +21 -0
  38. package/dist/interview/types.d.ts +16 -1
  39. package/dist/loop/loop-session.d.ts +64 -0
  40. package/dist/multiplexer/factory.d.ts +5 -5
  41. package/dist/multiplexer/herdr/index.d.ts +31 -0
  42. package/dist/multiplexer/index.d.ts +1 -0
  43. package/dist/multiplexer/session-manager.d.ts +3 -0
  44. package/dist/multiplexer/types.d.ts +4 -4
  45. package/dist/tools/acp-run.d.ts +1 -1
  46. package/dist/tui-state.d.ts +3 -0
  47. package/dist/tui.d.ts +4 -1
  48. package/dist/tui.js +142 -55
  49. package/dist/utils/background-job-board.d.ts +20 -0
  50. package/dist/utils/env.d.ts +3 -0
  51. package/dist/utils/session.d.ts +1 -1
  52. package/oh-my-opencode-slim.schema.json +24 -4
  53. package/package.json +1 -1
  54. package/src/skills/clonedeps/SKILL.md +2 -2
  55. package/src/skills/clonedeps/codemap.md +23 -32
  56. package/src/skills/codemap/SKILL.md +2 -2
  57. package/src/skills/codemap.md +63 -36
  58. package/src/skills/loop-engineering/SKILL.md +30 -0
  59. package/src/skills/reflect/SKILL.md +133 -0
  60. package/src/skills/release-smoke-test/SKILL.md +159 -0
  61. package/src/skills/simplify/SKILL.md +6 -6
@@ -104,24 +104,20 @@ export declare const MODEL_MAPPINGS: {
104
104
  };
105
105
  readonly 'opencode-go': {
106
106
  readonly orchestrator: {
107
- readonly model: "opencode-go/glm-5.1";
107
+ readonly model: "opencode-go/glm-5.2";
108
108
  };
109
109
  readonly oracle: {
110
- readonly model: "opencode-go/deepseek-v4-pro";
110
+ readonly model: "opencode-go/qwen3.7-max";
111
111
  readonly variant: "max";
112
112
  };
113
- readonly council: {
114
- readonly model: "opencode-go/deepseek-v4-pro";
115
- readonly variant: "high";
116
- };
117
113
  readonly librarian: {
118
- readonly model: "opencode-go/minimax-m2.7";
114
+ readonly model: "opencode-go/deepseek-v4-flash";
119
115
  };
120
116
  readonly explorer: {
121
- readonly model: "opencode-go/minimax-m2.7";
117
+ readonly model: "opencode-go/deepseek-v4-flash";
122
118
  };
123
119
  readonly designer: {
124
- readonly model: "opencode-go/kimi-k2.6";
120
+ readonly model: "opencode-go/kimi-k2.7-code";
125
121
  readonly variant: "medium";
126
122
  };
127
123
  readonly fixer: {
@@ -3,7 +3,7 @@
3
3
  * permission grants but is NOT installed by this plugin's CLI.
4
4
  */
5
5
  export interface PermissionOnlySkill {
6
- /** Skill name must match the name OpenCode uses for permission checks */
6
+ /** Skill name - must match the name OpenCode uses for permission checks */
7
7
  name: string;
8
8
  /** List of agents that should auto-allow this skill */
9
9
  allowedAgents: string[];
@@ -12,7 +12,7 @@ export interface PermissionOnlySkill {
12
12
  }
13
13
  /**
14
14
  * Skills managed externally (not installed by this plugin's CLI).
15
- * Entries here only affect agent permission grants nothing is installed.
15
+ * Entries here only affect agent permission grants - nothing is installed.
16
16
  */
17
17
  export declare const PERMISSION_ONLY_SKILLS: PermissionOnlySkill[];
18
18
  /**
@@ -21,4 +21,4 @@ export declare const PERMISSION_ONLY_SKILLS: PermissionOnlySkill[];
21
21
  * @param skillList - Optional explicit list of skills to allow (overrides defaults)
22
22
  * @returns Permission rules for the skill permission type
23
23
  */
24
- export declare function getSkillPermissionsForAgent(agentName: string, skillList?: string[]): Record<string, 'allow' | 'ask' | 'deny'>;
24
+ export declare function getSkillPermissionsForAgent(agentName: string, skillList?: string[], disabledSkillNames?: string[]): Record<string, 'allow' | 'ask' | 'deny'>;
@@ -19,6 +19,13 @@ export declare class CompanionManager {
19
19
  private companionProcess;
20
20
  constructor(sessionId: string, cwd: string, config?: CompanionConfig);
21
21
  onLoad(): void;
22
+ /**
23
+ * Register this manager behind a single process `exit` listener. Re-inits for
24
+ * the same OpenCode session dispose the superseded manager immediately so its
25
+ * detached child does not survive until process exit.
26
+ */
27
+ private registerActiveManager;
28
+ private static disposeActiveManagers;
22
29
  /**
23
30
  * Feed every session.status event here, with the agent name resolved
24
31
  * from sessionAgentMap. Orchestrator sessions drive overall status;
@@ -5,8 +5,9 @@ export type AgentName = (typeof ALL_AGENT_NAMES)[number];
5
5
  /** Agents that cannot be disabled even if listed in disabled_agents config. */
6
6
  export declare const PROTECTED_AGENTS: Set<string>;
7
7
  /**
8
- * Get the list of orchestratable agents, excluding any disabled agents.
9
- * This is used for delegation validation at runtime.
8
+ * Default models for each agent.
9
+ * All set to undefined so agents follow the global/session model.
10
+ * Users can override per-agent via oh-my-opencode-slim.json agents.<name>.model.
10
11
  */
11
12
  export declare const DEFAULT_MODELS: Record<AgentName, string | undefined>;
12
13
  export declare const POLL_INTERVAL_MS = 500;
@@ -76,10 +76,13 @@ export declare function loadPluginConfig(directory: string, options?: LoadPlugin
76
76
  * then falls back to the root prompts directory.
77
77
  *
78
78
  * @param agentName - Name of the agent (e.g., "orchestrator", "explorer")
79
- * @param preset - Optional preset name for preset-scoped prompt lookup
79
+ * @param optionsOrPreset - Optional preset name or options configuration
80
80
  * @returns Object with prompt and/or appendPrompt if files exist
81
81
  */
82
- export declare function loadAgentPrompt(agentName: string, preset?: string): {
82
+ export declare function loadAgentPrompt(agentName: string, optionsOrPreset?: string | {
83
+ preset?: string;
84
+ projectDirectory?: string;
85
+ }): {
83
86
  prompt?: string;
84
87
  appendPrompt?: string;
85
88
  };
@@ -66,6 +66,7 @@ export declare const MultiplexerTypeSchema: z.ZodEnum<{
66
66
  auto: "auto";
67
67
  tmux: "tmux";
68
68
  zellij: "zellij";
69
+ herdr: "herdr";
69
70
  none: "none";
70
71
  }>;
71
72
  export type MultiplexerType = z.infer<typeof MultiplexerTypeSchema>;
@@ -95,6 +96,7 @@ export declare const MultiplexerConfigSchema: z.ZodObject<{
95
96
  auto: "auto";
96
97
  tmux: "tmux";
97
98
  zellij: "zellij";
99
+ herdr: "herdr";
98
100
  none: "none";
99
101
  }>>;
100
102
  layout: z.ZodDefault<z.ZodEnum<{
@@ -208,6 +210,7 @@ export declare const AcpAgentPermissionModeSchema: z.ZodEnum<{
208
210
  ask: "ask";
209
211
  reject: "reject";
210
212
  }>;
213
+ export declare const MAX_ACP_TIMEOUT_MS = 2147483647;
211
214
  export declare const AcpAgentConfigSchema: z.ZodObject<{
212
215
  command: z.ZodString;
213
216
  args: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -246,6 +249,7 @@ export type AcpAgentsConfig = z.infer<typeof AcpAgentsConfigSchema>;
246
249
  export declare const PluginConfigSchema: z.ZodObject<{
247
250
  preset: z.ZodOptional<z.ZodString>;
248
251
  setDefaultAgent: z.ZodOptional<z.ZodBoolean>;
252
+ compactSidebar: z.ZodOptional<z.ZodBoolean>;
249
253
  autoUpdate: z.ZodOptional<z.ZodBoolean>;
250
254
  presets: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
251
255
  model: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
@@ -277,11 +281,14 @@ export declare const PluginConfigSchema: z.ZodObject<{
277
281
  }, z.core.$strict>>>;
278
282
  disabled_agents: z.ZodOptional<z.ZodArray<z.ZodString>>;
279
283
  disabled_mcps: z.ZodOptional<z.ZodArray<z.ZodString>>;
284
+ disabled_tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
285
+ disabled_skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
280
286
  multiplexer: z.ZodOptional<z.ZodObject<{
281
287
  type: z.ZodDefault<z.ZodEnum<{
282
288
  auto: "auto";
283
289
  tmux: "tmux";
284
290
  zellij: "zellij";
291
+ herdr: "herdr";
285
292
  none: "none";
286
293
  }>>;
287
294
  layout: z.ZodDefault<z.ZodEnum<{
@@ -42,7 +42,7 @@ export declare class CouncilManager {
42
42
  private runCouncillors;
43
43
  /**
44
44
  * Run a single councillor with retry logic for empty responses.
45
- * Only retries on "Empty response from provider" errors timeouts
45
+ * Only retries on "Empty response from provider" errors - timeouts
46
46
  * and other failures are returned immediately.
47
47
  */
48
48
  private runCouncillorWithRetry;
@@ -2,8 +2,66 @@ export interface SkillSyncResult {
2
2
  installed: string[];
3
3
  skippedExisting: string[];
4
4
  failed: string[];
5
+ staged: string[];
6
+ adopted: string[];
7
+ customized: string[];
5
8
  }
9
+ export interface SkillManifestEntry {
10
+ status: 'managed' | 'customized' | 'deleted' | 'conflict';
11
+ packageVersion: string;
12
+ sourceHash: string;
13
+ lastManagedHash: string;
14
+ lastSeenHash: string;
15
+ stagedPath?: string;
16
+ updatedAt: string;
17
+ }
18
+ export interface SkillsManifest {
19
+ schemaVersion: number;
20
+ updatedAt: string;
21
+ skills: Record<string, SkillManifestEntry>;
22
+ }
23
+ interface ManagedSkillSource {
24
+ name: string;
25
+ sourcePath: string;
26
+ }
27
+ interface SkillSyncOptions {
28
+ skills?: ManagedSkillSource[];
29
+ }
30
+ /**
31
+ * Hashes of historically managed versions of skills.
32
+ * When a release changes skill content, this table must be populated
33
+ * from the published npm package tarballs to allow upgrading existing users.
34
+ *
35
+ * How to populate:
36
+ * 1. Download previous releases of the npm package: `npm pack oh-my-opencode-slim@<version>`
37
+ * 2. Compute directory hash for each legacy skill directory inside the unpacked tarball:
38
+ * `import { computeDirectoryHash } from './skill-sync';`
39
+ * `const hash = computeDirectoryHash('path/to/extracted/package/src/skills/<skill-name>');`
40
+ * 3. Append the hash to the skill's string array below:
41
+ * ```typescript
42
+ * export const LEGACY_MANAGED_SKILL_HASHES: Record<string, string[]> = {
43
+ * 'simplify': ['hash1', 'hash2'],
44
+ * 'codemap': ['hash3']
45
+ * };
46
+ * ```
47
+ */
48
+ export declare const LEGACY_MANAGED_SKILL_HASHES: Record<string, string[]>;
49
+ /**
50
+ * Computes a deterministic SHA-256 hash of a directory's files.
51
+ */
52
+ export declare function computeDirectoryHash(dirPath: string): string;
53
+ /**
54
+ * Acquires a simple lock under .oh-my-opencode-slim.
55
+ * Avoids stealing active locks purely by time; writes owner metadata
56
+ * and only steals dead same-host pid if detectable.
57
+ */
58
+ export declare function acquireLock(lockDir: string): boolean;
59
+ /**
60
+ * Releases the lock.
61
+ */
62
+ export declare function releaseLock(lockDir: string): void;
6
63
  /**
7
64
  * Synchronizes bundled skills from the newly installed package root to OpenCode config skills directory.
8
65
  */
9
- export declare function syncBundledSkillsFromPackage(packageRoot: string): SkillSyncResult;
66
+ export declare function syncBundledSkillsFromPackage(packageRoot: string, options?: SkillSyncOptions): SkillSyncResult;
67
+ export {};
@@ -5,7 +5,6 @@
5
5
  */
6
6
  import type { PluginInput } from '@opencode-ai/plugin';
7
7
  import { type PluginConfig } from '../../config';
8
- import type { MessageWithParts } from '../types';
9
8
  type SkillRule = 'allow' | 'ask' | 'deny';
10
9
  declare function filterAvailableSkillsText(text: string, permissionRules: Record<string, SkillRule>): string;
11
10
  /**
@@ -14,7 +13,7 @@ declare function filterAvailableSkillsText(text: string, permissionRules: Record
14
13
  */
15
14
  export declare function createFilterAvailableSkillsHook(_ctx: PluginInput, config: PluginConfig): {
16
15
  'experimental.chat.messages.transform': (_input: Record<string, never>, output: {
17
- messages: MessageWithParts[];
16
+ messages?: unknown;
18
17
  }) => Promise<void>;
19
18
  };
20
19
  export { filterAvailableSkillsText };
@@ -6,7 +6,7 @@
6
6
  * 1. Looks up the next untried model in the agent's configured chain
7
7
  * 2. Aborts the rate-limited prompt via client.session.abort()
8
8
  * 3. Re-queues the last user message via client.session.promptAsync()
9
- * with the new model promptAsync returns immediately so we never
9
+ * with the new model - promptAsync returns immediately so we never
10
10
  * block the event handler waiting for a full LLM response.
11
11
  *
12
12
  * This mirrors the same fallback loop used for delegated sessions, but operates
@@ -41,6 +41,10 @@ export declare class ForegroundFallbackManager {
41
41
  private readonly inProgress;
42
42
  /** sessionID → timestamp of last trigger (for deduplication) */
43
43
  private readonly lastTrigger;
44
+ /** sessionID → model in use when lastTrigger was set; dedup is bypassed
45
+ * when the model has changed, allowing the cascade to continue when a
46
+ * new fallback model also fails within the dedup window. */
47
+ private readonly lastTriggerModel;
44
48
  constructor(client: OpencodeClient,
45
49
  /**
46
50
  * Ordered fallback chains per agent.
@@ -1,4 +1,4 @@
1
- import type { MessageWithParts } from './types';
1
+ import { type MessageWithParts } from './types';
2
2
  export declare function processImageAttachments(args: {
3
3
  messages: MessageWithParts[];
4
4
  workDir: string;
@@ -8,6 +8,7 @@ export { createFilterAvailableSkillsHook } from './filter-available-skills';
8
8
  export { ForegroundFallbackManager, isRateLimitError, } from './foreground-fallback';
9
9
  export { processImageAttachments } from './image-hook';
10
10
  export { createJsonErrorRecoveryHook } from './json-error-recovery/hook';
11
+ export { createLoopCommandHook } from './loop-command';
11
12
  export { createPhaseReminderHook } from './phase-reminder';
12
13
  export { createPostFileToolNudgeHook } from './post-file-tool-nudge';
13
14
  export { createReflectCommandHook } from './reflect';
@@ -0,0 +1,13 @@
1
+ export declare function createLoopCommandHook(): {
2
+ registerCommand: (config: Record<string, unknown>) => void;
3
+ handleCommandExecuteBefore: (input: {
4
+ command: string;
5
+ sessionID: string;
6
+ arguments: string;
7
+ }, output: {
8
+ parts: Array<{
9
+ type: string;
10
+ text?: string;
11
+ }>;
12
+ }) => Promise<void>;
13
+ };
@@ -6,7 +6,6 @@
6
6
  * of the user's actual turn.
7
7
  */
8
8
  import { PHASE_REMINDER } from '../../config/constants';
9
- import type { MessageWithParts } from '../types';
10
9
  export { PHASE_REMINDER };
11
10
  /**
12
11
  * Creates the experimental.chat.messages.transform hook for phase reminder injection.
@@ -15,6 +14,6 @@ export { PHASE_REMINDER };
15
14
  */
16
15
  export declare function createPhaseReminderHook(): {
17
16
  'experimental.chat.messages.transform': (_input: Record<string, never>, output: {
18
- messages: MessageWithParts[];
17
+ messages?: unknown;
19
18
  }) => Promise<void>;
20
19
  };
@@ -1,6 +1,5 @@
1
1
  import type { PluginInput } from '@opencode-ai/plugin';
2
2
  import { BackgroundJobBoard } from '../../utils';
3
- import type { MessageWithParts } from '../types';
4
3
  export declare function createTaskSessionManagerHook(_ctx: PluginInput, options: {
5
4
  maxSessionsPerAgent: number;
6
5
  readContextMinLines?: number;
@@ -24,7 +23,7 @@ export declare function createTaskSessionManagerHook(_ctx: PluginInput, options:
24
23
  metadata?: unknown;
25
24
  }) => Promise<void>;
26
25
  'experimental.chat.messages.transform': (_input: Record<string, never>, output: {
27
- messages: MessageWithParts[];
26
+ messages?: unknown;
28
27
  }) => Promise<void>;
29
28
  event: (input: {
30
29
  event: {
@@ -0,0 +1,13 @@
1
+ export interface PendingTaskCall {
2
+ callId: string;
3
+ parentSessionId: string;
4
+ agentType: string;
5
+ label: string;
6
+ resumedTaskId?: string;
7
+ }
8
+ export declare function createPendingCallTracker(): {
9
+ add(call: PendingTaskCall): void;
10
+ take(callId?: string, parentSessionId?: string): PendingTaskCall | undefined;
11
+ clearSession(sessionId: string): void;
12
+ pendingCallId(sessionID?: string, callID?: string): string;
13
+ };
@@ -0,0 +1,14 @@
1
+ import type { ContextFile } from '../../utils';
2
+ export declare function createTaskContextTracker(): {
3
+ pendingManagedTaskIds: Set<string>;
4
+ addContext(taskId: string, files: ContextFile[]): void;
5
+ prune(backgroundJobBoard: {
6
+ taskIDs(): Set<string>;
7
+ }): void;
8
+ clearSession(sessionId: string): void;
9
+ contextFilesForPrompt(taskId: string): ContextFile[];
10
+ };
11
+ export declare function extractReadFiles(root: string, output: {
12
+ output: unknown;
13
+ metadata?: unknown;
14
+ }): ContextFile[];
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * These types describe the structure of chat messages passed through
5
5
  * `experimental.chat.messages.transform` and related hooks. All fields
6
- * are unioned across the files that previously defined them privately
6
+ * are unioned across the files that previously defined them privately -
7
7
  * optional extras are harmless under structural typing.
8
8
  */
9
9
  export type MessageInfo = {
@@ -21,3 +21,5 @@ export type MessageWithParts = {
21
21
  info: MessageInfo;
22
22
  parts: MessagePart[];
23
23
  };
24
+ export declare function isMessageWithParts(message: unknown): message is MessageWithParts;
25
+ export declare function isUserMessageWithParts(message: unknown): message is MessageWithParts;