@kodrunhq/opencode-autopilot 1.9.0 → 1.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kodrunhq/opencode-autopilot",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Curated agents, skills, and commands for the OpenCode AI coding CLI — autonomous orchestrator, multi-agent code review, model fallback, and in-session asset creation tools.",
5
5
  "main": "src/index.ts",
6
6
  "keywords": [
@@ -14,7 +14,7 @@ interface AgentConfig {
14
14
  readonly permission?: Record<string, unknown>;
15
15
  }
16
16
 
17
- const agents = {
17
+ export const agents = {
18
18
  researcher: researcherAgent,
19
19
  metaprompter: metaprompterAgent,
20
20
  documenter: documenterAgent,
@@ -3,7 +3,7 @@ import type { AgentConfig } from "@opencode-ai/sdk";
3
3
  export const metaprompterAgent: Readonly<AgentConfig> = Object.freeze({
4
4
  description:
5
5
  "Crafts high-quality prompts, system instructions, and configurations for OpenCode agents, skills, and commands",
6
- mode: "all",
6
+ mode: "subagent",
7
7
  prompt: `You are a prompt engineering specialist for OpenCode assets. Your job is to craft high-quality system prompts and YAML frontmatter configurations for agents, skills, and commands.
8
8
 
9
9
  ## Instructions
@@ -2,7 +2,7 @@ import type { AgentConfig } from "@opencode-ai/sdk";
2
2
 
3
3
  export const researcherAgent: Readonly<AgentConfig> = Object.freeze({
4
4
  description: "Searches the web about a topic and produces a comprehensive report with sources",
5
- mode: "all",
5
+ mode: "subagent",
6
6
  prompt: `You are a research specialist. Your job is to thoroughly investigate a given topic and produce a clear, well-organized report.
7
7
 
8
8
  ## Instructions
@@ -1,6 +1,9 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { tool } from "@opencode-ai/plugin";
4
+ import { agents as standardAgents } from "../agents/index";
5
+ import { pipelineAgents } from "../agents/pipeline/index";
6
+ import { AGENT_REGISTRY } from "../registry/model-groups";
4
7
  import { lintAgent, lintCommand, lintSkill } from "../skills/linter";
5
8
  import { getAssetsDir, getGlobalConfigDir } from "../utils/paths";
6
9
 
@@ -11,7 +14,11 @@ interface StocktakeArgs {
11
14
  interface AssetEntry {
12
15
  readonly name: string;
13
16
  readonly type: "skill" | "command" | "agent";
14
- readonly origin: "built-in" | "user-created";
17
+ readonly origin: "built-in" | "config-hook" | "user-created";
18
+ readonly mode?: "all" | "primary" | "subagent";
19
+ readonly model?: string;
20
+ readonly group?: string;
21
+ readonly hidden?: boolean;
15
22
  readonly lint?: {
16
23
  readonly valid: boolean;
17
24
  readonly errors: readonly string[];
@@ -19,6 +26,24 @@ interface AssetEntry {
19
26
  };
20
27
  }
21
28
 
29
+ export interface ConfigHookAgent {
30
+ readonly name: string;
31
+ readonly mode?: "all" | "primary" | "subagent";
32
+ readonly hidden?: boolean;
33
+ readonly group?: string;
34
+ }
35
+
36
+ function configHookAgentToEntry(agent: ConfigHookAgent): AssetEntry {
37
+ return {
38
+ name: agent.name,
39
+ type: "agent",
40
+ origin: "config-hook",
41
+ mode: agent.mode,
42
+ group: agent.group,
43
+ hidden: agent.hidden ?? false,
44
+ };
45
+ }
46
+
22
47
  /** Read directory entries safely, returning empty array on ENOENT only. */
23
48
  async function safeReaddir(dirPath: string): Promise<string[]> {
24
49
  try {
@@ -45,11 +70,15 @@ async function isBuiltIn(assetType: string, name: string): Promise<boolean> {
45
70
  return cached.has(name);
46
71
  }
47
72
 
48
- export async function stocktakeCore(args: StocktakeArgs, baseDir: string): Promise<string> {
73
+ export async function stocktakeCore(
74
+ args: StocktakeArgs,
75
+ baseDir: string,
76
+ configHookAgents?: readonly ConfigHookAgent[],
77
+ ): Promise<string> {
49
78
  const shouldLint = args.lint !== false;
50
79
  const skills: AssetEntry[] = [];
51
80
  const commands: AssetEntry[] = [];
52
- const agents: AssetEntry[] = [];
81
+ const agentEntries: AssetEntry[] = [];
53
82
 
54
83
  // Scan skills (each subdirectory is a skill) — filter to directories only
55
84
  const skillEntries = await readdir(join(baseDir, "skills"), { withFileTypes: true }).catch(
@@ -113,22 +142,33 @@ export async function stocktakeCore(args: StocktakeArgs, baseDir: string): Promi
113
142
  try {
114
143
  const content = await readFile(join(baseDir, "agents", file), "utf-8");
115
144
  const lint = lintAgent(content);
116
- agents.push({ ...entry, lint });
145
+ agentEntries.push({ ...entry, lint });
117
146
  } catch {
118
- agents.push({
147
+ agentEntries.push({
119
148
  ...entry,
120
149
  lint: { valid: false, errors: ["Could not read agent file"], warnings: [] },
121
150
  });
122
151
  }
123
152
  } else {
124
- agents.push(entry);
153
+ agentEntries.push(entry);
154
+ }
155
+ }
156
+
157
+ // Add config-hook agents (skip any already found on filesystem to avoid duplicates)
158
+ const filesystemAgentNames = new Set(agentEntries.map((a) => a.name));
159
+ if (configHookAgents) {
160
+ for (const hookAgent of configHookAgents) {
161
+ if (!filesystemAgentNames.has(hookAgent.name)) {
162
+ agentEntries.push(configHookAgentToEntry(hookAgent));
163
+ }
125
164
  }
126
165
  }
127
166
 
128
167
  // Compute summary
129
- const allAssets = [...skills, ...commands, ...agents];
168
+ const allAssets = [...skills, ...commands, ...agentEntries];
130
169
  const builtIn = allAssets.filter((a) => a.origin === "built-in").length;
131
170
  const userCreated = allAssets.filter((a) => a.origin === "user-created").length;
171
+ const configHook = allAssets.filter((a) => a.origin === "config-hook").length;
132
172
  const lintErrors = shouldLint
133
173
  ? allAssets.reduce((sum, a) => sum + (a.lint?.errors.length ?? 0), 0)
134
174
  : 0;
@@ -140,11 +180,12 @@ export async function stocktakeCore(args: StocktakeArgs, baseDir: string): Promi
140
180
  {
141
181
  skills,
142
182
  commands,
143
- agents,
183
+ agents: agentEntries,
144
184
  summary: {
145
185
  total: allAssets.length,
146
186
  builtIn,
147
187
  userCreated,
188
+ configHook,
148
189
  lintErrors,
149
190
  lintWarnings,
150
191
  },
@@ -165,6 +206,20 @@ export const ocStocktake = tool({
165
206
  .describe("Run YAML frontmatter linter on all assets"),
166
207
  },
167
208
  async execute(args) {
168
- return stocktakeCore(args, getGlobalConfigDir());
209
+ const configHookAgentList: ConfigHookAgent[] = [
210
+ ...Object.entries(standardAgents).map(([name, config]) => ({
211
+ name,
212
+ mode: config.mode as ConfigHookAgent["mode"],
213
+ hidden: (config as Record<string, unknown>).hidden === true,
214
+ group: AGENT_REGISTRY[name]?.group,
215
+ })),
216
+ ...Object.entries(pipelineAgents).map(([name, config]) => ({
217
+ name,
218
+ mode: config.mode as ConfigHookAgent["mode"],
219
+ hidden: (config as Record<string, unknown>).hidden === true,
220
+ group: AGENT_REGISTRY[name]?.group,
221
+ })),
222
+ ];
223
+ return stocktakeCore(args, getGlobalConfigDir(), configHookAgentList);
169
224
  },
170
225
  });