opencode-architect 0.2.4 → 0.4.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.
@@ -24,7 +24,7 @@ compatibility: opencode
24
24
  metadata:
25
25
  version: 1.0.0
26
26
  audience: agents
27
- topic: [topic1, topic2]
27
+ topic: "topic1, topic2"
28
28
  ---
29
29
 
30
30
  ## Activation Triggers
@@ -32,9 +32,9 @@ metadata:
32
32
  **USE this skill when user asks about:**
33
33
  - Category: "Example query"
34
34
 
35
- **DO NOT USE for:**
36
- - Non-technical topics
37
- - Known specific repos
35
+ **Route elsewhere when:**
36
+ - The topic is non-technical
37
+ - The repo is well-known and already documented
38
38
 
39
39
  ## Workflow Summary
40
40
 
package/cli.ts ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env bun
2
+ import { parseArgs } from "node:util";
3
+ import { Installer, type Scope } from "./installer";
4
+
5
+ const VERSION = (JSON.parse(await Bun.file(`${import.meta.dirname}/package.json`).text()) as { version: string }).version;
6
+
7
+ async function main(): Promise<void> {
8
+ const { positionals, values } = parseArgs({
9
+ options: {
10
+ scope: { type: "string", short: "s" },
11
+ force: { type: "boolean", short: "f", default: false },
12
+ help: { type: "boolean", short: "h", default: false },
13
+ version: { type: "boolean", short: "v", default: false },
14
+ },
15
+ allowPositionals: true,
16
+ strict: true,
17
+ });
18
+
19
+ if (values.version) {
20
+ console.log(`opencode-architect v${VERSION}`);
21
+ return;
22
+ }
23
+ if (values.help || positionals.length === 0) {
24
+ printHelp();
25
+ return;
26
+ }
27
+
28
+ const command = positionals[0];
29
+ const scopeInput = values.scope;
30
+ if (scopeInput !== undefined && scopeInput !== "local" && scopeInput !== "global") {
31
+ console.error(`Invalid scope: ${scopeInput}. Must be "local" or "global".`);
32
+ process.exit(1);
33
+ }
34
+ const scope: Scope = scopeInput === "global" ? "global" : "local";
35
+ const installer = new Installer();
36
+
37
+ try {
38
+ switch (command) {
39
+ case "install": {
40
+ const outcome = await installer.install(scope, { force: values.force, projectDir: process.cwd() });
41
+ if (outcome.action === "noop") {
42
+ console.log(`Already installed at version ${VERSION}; nothing to do.`);
43
+ } else {
44
+ console.log(`${outcome.action === "installed" ? "Installed" : "Upgraded"} (${scope} scope):`);
45
+ console.log(` Agents: ${outcome.agentsDir}`);
46
+ console.log(` References: ${outcome.referencesDir}`);
47
+ console.log(` Templates: ${outcome.templatesDir}`);
48
+ }
49
+ if (outcome.pluginRemoved) {
50
+ console.log(" Removed the plugin entry from opencode.json (switched to copy install).");
51
+ }
52
+ if (outcome.skipped.length > 0) {
53
+ console.log(" Skipped locally modified files (re-run with --force to overwrite):");
54
+ for (const file of outcome.skipped) console.log(` ${file}`);
55
+ }
56
+ break;
57
+ }
58
+ case "uninstall": {
59
+ const outcome = await installer.uninstall(scope, process.cwd());
60
+ if (outcome.mode === "none") {
61
+ console.log(`Nothing to uninstall for the ${scope} scope.`);
62
+ break;
63
+ }
64
+ if (outcome.mode === "copy") {
65
+ console.log(`Uninstalled (${scope} scope):`);
66
+ for (const file of outcome.removed) console.log(` Removed: ${file}`);
67
+ }
68
+ if (outcome.pluginRemoved) {
69
+ console.log(" Removed the plugin entry from opencode.json.");
70
+ }
71
+ break;
72
+ }
73
+ case "status": {
74
+ const outcome = await installer.status(scope, process.cwd());
75
+ const version = outcome.version ?? "-";
76
+ console.log(`${scope} scope: mode=${outcome.mode} version=${version}`);
77
+ break;
78
+ }
79
+ default:
80
+ console.error(`Unknown command: ${command}`);
81
+ printHelp();
82
+ process.exit(1);
83
+ }
84
+ } catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ console.error(`Error: ${message}`);
87
+ process.exit(1);
88
+ }
89
+ }
90
+
91
+ function printHelp(): void {
92
+ console.log(`
93
+ opencode-architect v${VERSION}
94
+
95
+ Installs the opencode-architect agent suite by copying agents into the scope
96
+ base's agents/, references into opencode-architect/references/, and templates
97
+ into opencode-architect/templates/, rewriting relative reference paths to
98
+ absolute paths at install time. Copy install and plugin install are mutually
99
+ exclusive per scope.
100
+
101
+ Commands:
102
+ install Copy agents, references, and templates into the scope base
103
+ uninstall Remove exactly the files a copy install wrote (or the plugin entry)
104
+ status Show install mode and version for a scope
105
+
106
+ Options:
107
+ -s, --scope <scope> "local" (project .opencode/) or "global" (~/.config/opencode/); default local
108
+ -f, --force install: remove an existing plugin entry and overwrite locally modified files
109
+ -h, --help Show this help message
110
+ -v, --version Show version
111
+
112
+ Examples:
113
+ opencode-architect install
114
+ opencode-architect install --scope global
115
+ opencode-architect uninstall
116
+ opencode-architect status
117
+ `);
118
+ }
119
+
120
+ main();
package/index.ts CHANGED
@@ -1,120 +1,21 @@
1
- import type { Plugin, PluginInput } from "@opencode-ai/plugin";
2
- import { readFile } from "node:fs/promises";
1
+ import type { Plugin } from "@opencode-ai/plugin";
3
2
  import path from "node:path";
4
- import { parse as parseYaml } from "yaml";
5
- import type { AgentConfig } from "@opencode-ai/sdk";
6
- import { command as syncDocsCommand } from "./commands/sync-docs";
7
- import { OpenCodeDocsFetcher } from "./scripts/fetch-opencode-docs";
8
- import { SilentLogger } from "./scripts/logger";
9
- import { createSyncDocsTool } from "./tools/sync-docs";
3
+ import { AgentLoader } from "./agent-loader";
10
4
 
11
5
  const AGENTS_DIR = path.join(import.meta.dirname, "assets", "agents");
12
6
 
13
- const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
14
-
15
- interface AgentFrontmatter {
16
- description: string;
17
- mode: "primary" | "subagent" | "all";
18
- tools?: Record<string, boolean>;
19
- permission?: {
20
- edit?: "ask" | "allow" | "deny";
21
- bash?: ("ask" | "allow" | "deny") | Record<string, "ask" | "allow" | "deny">;
22
- webfetch?: "ask" | "allow" | "deny";
23
- doom_loop?: "ask" | "allow" | "deny";
24
- external_directory?: "ask" | "allow" | "deny";
25
- };
26
- }
27
-
28
- async function parseAgentMarkdown(content: string, agentName: string): Promise<AgentConfig> {
29
- const match = content.match(FRONTMATTER_REGEX);
30
-
31
- if (!match || match.length < 3) {
32
- throw new Error(`Agent ${agentName} must have YAML frontmatter`);
33
- }
34
-
35
- const frontmatterYaml = match[1] as string;
36
- const rawPrompt = match[2] as string;
37
- const frontmatter = parseYaml(frontmatterYaml) as AgentFrontmatter;
38
- const prompt = rawPrompt.replace(/^\r?\n/, "");
39
-
40
- const config: AgentConfig = {
41
- description: frontmatter.description,
42
- mode: frontmatter.mode,
43
- prompt,
44
- };
45
-
46
- if (frontmatter.tools) {
47
- config.tools = frontmatter.tools;
48
- }
49
-
50
- if (frontmatter.permission) {
51
- config.permission = frontmatter.permission;
52
- }
53
-
54
- return config;
55
- }
56
-
57
- const OpencodeArchitect: Plugin = async (input) => {
58
- const syncDocsTool = createSyncDocsTool();
59
-
60
- syncDocsOnStartup(input.client);
61
-
62
- const agents = await loadAgents();
7
+ const OpencodeArchitect: Plugin = async () => {
8
+ const agents = await new AgentLoader(AGENTS_DIR).loadAgents();
63
9
 
64
10
  return {
65
11
  config: async (config) => {
66
12
  config.agent = config.agent || {};
67
- config.command = config.command || {};
68
13
 
69
14
  for (const [name, agentConfig] of Object.entries(agents)) {
70
15
  config.agent[name] = agentConfig;
71
16
  }
72
-
73
- config.command["sync-docs"] = syncDocsCommand;
74
- },
75
- tool: {
76
- "sync-docs": syncDocsTool,
77
17
  },
78
18
  };
79
19
  };
80
20
 
81
21
  export default OpencodeArchitect;
82
-
83
- async function loadAgents(): Promise<Record<string, AgentConfig>> {
84
- const agentFiles = [
85
- "opencode-agent-designer.md",
86
- "opencode-architect.md",
87
- "opencode-command-crafter.md",
88
- "opencode-extension-auditor.md",
89
- "opencode-packager.md",
90
- "opencode-publisher.md",
91
- "opencode-mcp-integrator.md",
92
- "opencode-plugin-engineer.md",
93
- "opencode-skill-creator.md",
94
- "opencode-tool-builder.md",
95
- ];
96
-
97
- const agents: Record<string, AgentConfig> = {};
98
-
99
- for (const filename of agentFiles) {
100
- const agentPath = path.join(AGENTS_DIR, filename);
101
- const agentContent = await readFile(agentPath, "utf-8");
102
- const agentName = path.basename(filename, ".md");
103
- agents[agentName] = await parseAgentMarkdown(agentContent, agentName);
104
- }
105
-
106
- return agents;
107
- }
108
-
109
- function syncDocsOnStartup(client: PluginInput["client"]): void {
110
- const fetcher = new OpenCodeDocsFetcher(new SilentLogger());
111
- fetcher.run().catch((error: unknown) => {
112
- const message = error instanceof Error ? error.message : String(error);
113
- client.tui.showToast({
114
- body: {
115
- message: `Failed to sync OpenCode docs: ${message}`,
116
- variant: "error",
117
- },
118
- });
119
- });
120
- }
package/installer.ts ADDED
@@ -0,0 +1,319 @@
1
+ import { createHash } from "node:crypto";
2
+ import { copyFile, exists, mkdir, readdir, readFile, rm, rmdir, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import path from "node:path";
5
+ import { AGENT_FILENAMES, RELATIVE_REFERENCE_REGEX } from "./agent-loader";
6
+
7
+ export type Scope = "local" | "global";
8
+ export type InstallMode = "none" | "copy" | "plugin";
9
+ export type InstallAction = "installed" | "upgraded" | "noop";
10
+
11
+ export interface ManifestHashEntry {
12
+ path: string;
13
+ hash: string;
14
+ }
15
+
16
+ export interface Manifest {
17
+ version: string;
18
+ agentFiles: string[];
19
+ referencesDir: string;
20
+ templatesDir: string;
21
+ hashes: ManifestHashEntry[];
22
+ }
23
+
24
+ export interface InstallOptions {
25
+ force: boolean;
26
+ projectDir: string;
27
+ }
28
+
29
+ export interface InstallOutcome {
30
+ action: InstallAction;
31
+ scope: Scope;
32
+ agentsDir: string;
33
+ referencesDir: string;
34
+ templatesDir: string;
35
+ manifestPath: string;
36
+ copied: string[];
37
+ skipped: string[];
38
+ overwritten: string[];
39
+ pluginRemoved: boolean;
40
+ }
41
+
42
+ export interface UninstallOutcome {
43
+ scope: Scope;
44
+ mode: InstallMode;
45
+ removed: string[];
46
+ pluginRemoved: boolean;
47
+ }
48
+
49
+ export interface StatusOutcome {
50
+ scope: Scope;
51
+ mode: InstallMode;
52
+ version: string | null;
53
+ }
54
+
55
+ const PACKAGE_NAME = "opencode-architect";
56
+ const MANIFEST_NAME = "opencode-architect.json";
57
+ const ASSETS_AGENTS_DIR = path.join(import.meta.dirname, "assets", "agents");
58
+ const ASSETS_REFERENCES_DIR = path.join(import.meta.dirname, "assets", "references");
59
+ const ASSETS_TEMPLATES_DIR = path.join(import.meta.dirname, "assets", "templates");
60
+
61
+ export class Installer {
62
+ public async install(scope: Scope, options: InstallOptions): Promise<InstallOutcome> {
63
+ const base = this.scopeBase(scope, options.projectDir);
64
+ const configPath = path.join(base, "opencode.json");
65
+ const manifestPath = path.join(base, MANIFEST_NAME);
66
+ const agentsDir = path.join(base, "agents");
67
+ const referencesDir = path.join(base, "opencode-architect", "references");
68
+ const templatesDir = path.join(base, "opencode-architect", "templates");
69
+ const version = await this.getPackageVersion();
70
+
71
+ let pluginRemoved = false;
72
+ if (await this.hasPluginEntry(configPath)) {
73
+ if (!options.force) {
74
+ throw new Error(
75
+ `The "${PACKAGE_NAME}" plugin entry in ${configPath} must be removed before a copy install ` +
76
+ `(copied agents would silently shadow the plugin's agents). Re-run with --force to remove ` +
77
+ `the entry and switch this scope to copy install.`,
78
+ );
79
+ }
80
+ await this.removePluginEntry(configPath);
81
+ pluginRemoved = true;
82
+ }
83
+
84
+ const existingManifest = await this.readManifest(manifestPath);
85
+ if (existingManifest !== null && existingManifest.version === version) {
86
+ return {
87
+ action: "noop",
88
+ scope,
89
+ agentsDir,
90
+ referencesDir,
91
+ templatesDir,
92
+ manifestPath,
93
+ copied: [],
94
+ skipped: [],
95
+ overwritten: [],
96
+ pluginRemoved,
97
+ };
98
+ }
99
+
100
+ const action: InstallAction = existingManifest === null ? "installed" : "upgraded";
101
+ const copied: string[] = [];
102
+ const skipped: string[] = [];
103
+ const overwritten: string[] = [];
104
+ const hashes: ManifestHashEntry[] = [];
105
+
106
+ await mkdir(agentsDir, { recursive: true });
107
+ await mkdir(referencesDir, { recursive: true });
108
+ await mkdir(templatesDir, { recursive: true });
109
+
110
+ for (const filename of AGENT_FILENAMES) {
111
+ const relativePath = path.join("agents", filename);
112
+ const { action: fileAction, priorHash } = await this.disposition(
113
+ existingManifest,
114
+ base,
115
+ relativePath,
116
+ options.force,
117
+ );
118
+ if (fileAction === "skip") {
119
+ skipped.push(relativePath);
120
+ hashes.push({ path: relativePath, hash: priorHash as string });
121
+ continue;
122
+ }
123
+ const source = await readFile(path.join(ASSETS_AGENTS_DIR, filename), "utf-8");
124
+ await writeFile(path.join(base, relativePath), rewriteReferencePaths(source, referencesDir, templatesDir));
125
+ hashes.push({ path: relativePath, hash: await this.sha256File(path.join(base, relativePath)) });
126
+ if (fileAction === "overwrite") overwritten.push(relativePath);
127
+ else copied.push(relativePath);
128
+ }
129
+
130
+ for (const entry of await readdir(ASSETS_REFERENCES_DIR)) {
131
+ const relativePath = path.join("opencode-architect", "references", entry);
132
+ const { action: fileAction, priorHash } = await this.disposition(
133
+ existingManifest,
134
+ base,
135
+ relativePath,
136
+ options.force,
137
+ );
138
+ if (fileAction === "skip") {
139
+ skipped.push(relativePath);
140
+ hashes.push({ path: relativePath, hash: priorHash as string });
141
+ continue;
142
+ }
143
+ await copyFile(path.join(ASSETS_REFERENCES_DIR, entry), path.join(base, relativePath));
144
+ hashes.push({ path: relativePath, hash: await this.sha256File(path.join(base, relativePath)) });
145
+ if (fileAction === "overwrite") overwritten.push(relativePath);
146
+ else copied.push(relativePath);
147
+ }
148
+
149
+ for (const entry of await readdir(ASSETS_TEMPLATES_DIR)) {
150
+ const relativePath = path.join("opencode-architect", "templates", entry);
151
+ const { action: fileAction, priorHash } = await this.disposition(
152
+ existingManifest,
153
+ base,
154
+ relativePath,
155
+ options.force,
156
+ );
157
+ if (fileAction === "skip") {
158
+ skipped.push(relativePath);
159
+ hashes.push({ path: relativePath, hash: priorHash as string });
160
+ continue;
161
+ }
162
+ await copyFile(path.join(ASSETS_TEMPLATES_DIR, entry), path.join(base, relativePath));
163
+ hashes.push({ path: relativePath, hash: await this.sha256File(path.join(base, relativePath)) });
164
+ if (fileAction === "overwrite") overwritten.push(relativePath);
165
+ else copied.push(relativePath);
166
+ }
167
+
168
+ const manifest: Manifest = { version, agentFiles: [...AGENT_FILENAMES], referencesDir, templatesDir, hashes };
169
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
170
+
171
+ return { action, scope, agentsDir, referencesDir, templatesDir, manifestPath, copied, skipped, overwritten, pluginRemoved };
172
+ }
173
+
174
+ public async uninstall(scope: Scope, projectDir: string): Promise<UninstallOutcome> {
175
+ const base = this.scopeBase(scope, projectDir);
176
+ const configPath = path.join(base, "opencode.json");
177
+ const manifestPath = path.join(base, MANIFEST_NAME);
178
+ const manifest = await this.readManifest(manifestPath);
179
+ const hadPluginEntry = await this.hasPluginEntry(configPath);
180
+ const removed: string[] = [];
181
+
182
+ if (manifest !== null) {
183
+ for (const entry of manifest.hashes) {
184
+ const target = path.join(base, entry.path);
185
+ if (await exists(target)) {
186
+ await rm(target);
187
+ removed.push(target);
188
+ }
189
+ }
190
+ await rm(manifestPath);
191
+ removed.push(manifestPath);
192
+ await this.removeIfEmpty(path.join(base, "opencode-architect", "templates"));
193
+ await this.removeIfEmpty(path.join(base, "opencode-architect", "references"));
194
+ await this.removeIfEmpty(path.join(base, "opencode-architect"));
195
+ await this.removeIfEmpty(path.join(base, "agents"));
196
+ }
197
+
198
+ let pluginRemoved = false;
199
+ if (hadPluginEntry) {
200
+ await this.removePluginEntry(configPath);
201
+ pluginRemoved = true;
202
+ }
203
+
204
+ const mode: InstallMode = manifest !== null ? "copy" : hadPluginEntry ? "plugin" : "none";
205
+ return { scope, mode, removed, pluginRemoved };
206
+ }
207
+
208
+ public async status(scope: Scope, projectDir: string): Promise<StatusOutcome> {
209
+ const base = this.scopeBase(scope, projectDir);
210
+ const manifest = await this.readManifest(path.join(base, MANIFEST_NAME));
211
+
212
+ if (manifest !== null) {
213
+ return { scope, mode: "copy", version: manifest.version };
214
+ }
215
+ if (await this.hasPluginEntry(path.join(base, "opencode.json"))) {
216
+ return { scope, mode: "plugin", version: null };
217
+ }
218
+ return { scope, mode: "none", version: null };
219
+ }
220
+
221
+ private async disposition(
222
+ manifest: Manifest | null,
223
+ base: string,
224
+ relativePath: string,
225
+ force: boolean,
226
+ ): Promise<{ action: "copy" | "overwrite" | "skip"; priorHash: string | null }> {
227
+ if (manifest === null) return { action: "copy", priorHash: null };
228
+ const priorHash = manifest.hashes.find((entry) => entry.path === relativePath)?.hash ?? null;
229
+ if (priorHash === null) return { action: "copy", priorHash };
230
+ const installedPath = path.join(base, relativePath);
231
+ if (!(await exists(installedPath))) return { action: "copy", priorHash };
232
+ if ((await this.sha256File(installedPath)) === priorHash) return { action: "copy", priorHash };
233
+ return { action: force ? "overwrite" : "skip", priorHash };
234
+ }
235
+
236
+ private async sha256File(filePath: string): Promise<string> {
237
+ return createHash("sha256").update(await readFile(filePath)).digest("hex");
238
+ }
239
+
240
+ private scopeBase(scope: Scope, projectDir: string): string {
241
+ if (scope === "local") return path.join(projectDir, ".opencode");
242
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME;
243
+ if (xdgConfigHome) return path.join(xdgConfigHome, "opencode");
244
+ return path.join(homedir(), ".config", "opencode");
245
+ }
246
+
247
+ private async readManifest(manifestPath: string): Promise<Manifest | null> {
248
+ try {
249
+ return JSON.parse(await readFile(manifestPath, "utf-8")) as Manifest;
250
+ } catch {
251
+ return null;
252
+ }
253
+ }
254
+
255
+ private async hasPluginEntry(configPath: string): Promise<boolean> {
256
+ const plugins = await this.readPluginArray(configPath);
257
+ return plugins.some((name) => this.isPluginEntry(name));
258
+ }
259
+
260
+ private async removePluginEntry(configPath: string): Promise<void> {
261
+ const plugins = await this.readPluginArray(configPath);
262
+ const remaining = plugins.filter((name) => !this.isPluginEntry(name));
263
+ const config = await this.readConfig(configPath);
264
+ if (remaining.length === 0) delete config.plugin;
265
+ else config.plugin = remaining;
266
+ await mkdir(path.dirname(configPath), { recursive: true });
267
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
268
+ }
269
+
270
+ private isPluginEntry(name: string): boolean {
271
+ return name === PACKAGE_NAME || name.startsWith(`${PACKAGE_NAME}@`);
272
+ }
273
+
274
+ private async readPluginArray(configPath: string): Promise<string[]> {
275
+ const config = await this.readConfig(configPath);
276
+ const plugins = config.plugin;
277
+ return Array.isArray(plugins) ? plugins.filter((name): name is string => typeof name === "string") : [];
278
+ }
279
+
280
+ private async readConfig(configPath: string): Promise<Record<string, unknown>> {
281
+ try {
282
+ return JSON.parse(await readFile(configPath, "utf-8")) as Record<string, unknown>;
283
+ } catch {
284
+ return {};
285
+ }
286
+ }
287
+
288
+ private async getPackageVersion(): Promise<string> {
289
+ const content = await readFile(path.join(import.meta.dirname, "package.json"), "utf-8");
290
+ return (JSON.parse(content) as { version: string }).version;
291
+ }
292
+
293
+ private async removeIfEmpty(directory: string): Promise<void> {
294
+ if (!(await exists(directory))) return;
295
+ const contents = await readdir(directory);
296
+ if (contents.length === 0) await rmdir(directory);
297
+ }
298
+ }
299
+
300
+ export function rewriteReferencePaths(content: string, referencesDir: string, templatesDir: string): string {
301
+ return content.replace(
302
+ RELATIVE_REFERENCE_REGEX,
303
+ (token: string, relativePath: string): string => {
304
+ const normalized = relativePath.replaceAll("\\", "/");
305
+ const packagedPath = path.resolve(ASSETS_AGENTS_DIR, normalized);
306
+ const withinReferences = path.relative(ASSETS_REFERENCES_DIR, packagedPath);
307
+ if (!withinReferences.startsWith("..")) {
308
+ const installedPath = path.resolve(referencesDir, withinReferences).replaceAll("\\", "/");
309
+ return `\`${installedPath}\``;
310
+ }
311
+ const withinTemplates = path.relative(ASSETS_TEMPLATES_DIR, packagedPath);
312
+ if (!withinTemplates.startsWith("..")) {
313
+ const installedPath = path.resolve(templatesDir, withinTemplates).replaceAll("\\", "/");
314
+ return `\`${installedPath}\``;
315
+ }
316
+ return token;
317
+ },
318
+ );
319
+ }
package/package.json CHANGED
@@ -1,28 +1,50 @@
1
1
  {
2
2
  "name": "opencode-architect",
3
- "version": "0.2.4",
3
+ "version": "0.4.0",
4
+ "description": "OpenCode plugin and CLI with ten specialist agents for agent skills, slash commands, custom tools, plugins, and MCP server integration",
5
+ "keywords": [
6
+ "opencode",
7
+ "opencode-plugin",
8
+ "opencode-agents",
9
+ "agent-skills",
10
+ "ai-agents",
11
+ "slash-commands",
12
+ "mcp",
13
+ "mcp-server",
14
+ "coding-agent",
15
+ "bun"
16
+ ],
17
+ "license": "MIT",
4
18
  "private": false,
5
19
  "type": "module",
6
20
  "module": "index.ts",
21
+ "bin": {
22
+ "opencode-architect": "cli.ts"
23
+ },
7
24
  "repository": {
8
25
  "type": "git",
9
- "url": "https://github.com/diegohb/opencode-architect.git"
26
+ "url": "git+https://github.com/Expert-Vision-Software/opencode-architect.git"
10
27
  },
28
+ "homepage": "https://github.com/Expert-Vision-Software/opencode-architect#readme",
11
29
  "publisher": "Expert Vision Software",
12
30
  "author": "Expert Vision Support <support@expertvision.software>",
13
31
  "bugs": {
14
- "url": "https://github.com/diegohb/opencode-architect/issues",
32
+ "url": "https://github.com/Expert-Vision-Software/opencode-architect/issues",
15
33
  "email": "support@expertvision.software"
16
34
  },
17
35
  "scripts": {
18
36
  "check": "tsc --noEmit",
37
+ "test": "bun test",
19
38
  "release": "npm version patch && npm publish && git push --follow-tags"
20
39
  },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
21
43
  "files": [
22
44
  "index.ts",
23
- "tools",
24
- "commands",
25
- "scripts",
45
+ "agent-loader.ts",
46
+ "cli.ts",
47
+ "installer.ts",
26
48
  "assets"
27
49
  ],
28
50
  "dependencies": {
@@ -30,9 +52,9 @@
30
52
  "@opencode-ai/plugin": "*"
31
53
  },
32
54
  "devDependencies": {
33
- "@opencode-ai/plugin": "latest",
34
55
  "@opencode-ai/sdk": "latest",
35
56
  "@types/bun": "latest",
36
- "@types/node": "latest"
57
+ "@types/node": "latest",
58
+ "typescript": "latest"
37
59
  }
38
60
  }