maskweaver 0.8.6 → 0.8.8
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/README.ko.md +217 -217
- package/README.md +235 -235
- package/assets/agents/squad-operator.md +56 -56
- package/assets/commands/weave-approve-plan.md +57 -57
- package/assets/commands/weave-craft.md +43 -43
- package/assets/commands/weave-design.md +64 -64
- package/assets/commands/weave-flow.md +48 -48
- package/assets/commands/weave-help.md +101 -101
- package/assets/commands/weave-init.md +23 -23
- package/assets/commands/weave-plan.md +15 -15
- package/assets/commands/weave-prepare.md +69 -69
- package/assets/commands/weave-refine-plan.md +59 -59
- package/assets/commands/weave-repair.md +70 -70
- package/assets/commands/weave-research.md +51 -51
- package/assets/commands/weave-spec.md +227 -227
- package/assets/commands/weave-status.md +2 -2
- package/assets/commands/weave-verify.md +44 -44
- package/assets/commands/weave-worktree.md +69 -69
- package/dist/cli/doctor.d.ts +16 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +355 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/install.js +9 -0
- package/dist/cli/install.js.map +1 -1
- package/dist/plugin/config/index.d.ts +3 -10
- package/dist/plugin/config/index.d.ts.map +1 -1
- package/dist/plugin/config/index.js +12 -15
- package/dist/plugin/config/index.js.map +1 -1
- package/dist/plugin/index.d.ts +1 -27
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +113 -240
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/tools/slashcommand.js +59 -59
- package/dist/plugin/tools/squad.js +3 -3
- package/dist/plugin/tools/weave.js +111 -111
- package/dist/plugin/types.d.ts +10 -8
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/plugin/types.js +2 -0
- package/dist/plugin/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +4 -2
- package/postinstall.mjs +97 -0
|
@@ -12,6 +12,15 @@ import { parse as parseJsonc } from 'jsonc-parser';
|
|
|
12
12
|
import * as fs from 'node:fs';
|
|
13
13
|
import * as path from 'node:path';
|
|
14
14
|
import * as os from 'node:os';
|
|
15
|
+
function pluginLog(ctx, level, message) {
|
|
16
|
+
ctx.client.app.log({
|
|
17
|
+
body: {
|
|
18
|
+
service: 'maskweaver',
|
|
19
|
+
level,
|
|
20
|
+
message,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
15
24
|
// ============================================================================
|
|
16
25
|
// Configuration Loader
|
|
17
26
|
// ============================================================================
|
|
@@ -66,33 +75,21 @@ export function loadPluginConfig(directory, ctx) {
|
|
|
66
75
|
const config = parseJsoncContent(content);
|
|
67
76
|
// Log successful config load
|
|
68
77
|
if (ctx?.verbose) {
|
|
69
|
-
ctx
|
|
70
|
-
service: 'maskweaver',
|
|
71
|
-
level: 'info',
|
|
72
|
-
message: `Loaded config from: ${location}`,
|
|
73
|
-
});
|
|
78
|
+
pluginLog(ctx, 'info', `Loaded config from: ${location}`);
|
|
74
79
|
}
|
|
75
80
|
return config;
|
|
76
81
|
}
|
|
77
82
|
catch (error) {
|
|
78
83
|
// Log error but continue searching
|
|
79
84
|
if (ctx) {
|
|
80
|
-
ctx
|
|
81
|
-
service: 'maskweaver',
|
|
82
|
-
level: 'warn',
|
|
83
|
-
message: `Failed to load config from ${location}: ${error}`,
|
|
84
|
-
});
|
|
85
|
+
pluginLog(ctx, 'warn', `Failed to load config from ${location}: ${error}`);
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
// No configuration found - return empty config
|
|
90
91
|
if (ctx?.verbose) {
|
|
91
|
-
ctx.
|
|
92
|
-
service: 'maskweaver',
|
|
93
|
-
level: 'info',
|
|
94
|
-
message: 'No maskweaver.json found, using defaults',
|
|
95
|
-
});
|
|
92
|
+
pluginLog(ctx, 'info', 'No maskweaver.json found, using defaults');
|
|
96
93
|
}
|
|
97
94
|
return {};
|
|
98
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugin/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugin/config/index.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -11,33 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Based on oh-my-opencode plugin development patterns.
|
|
13
13
|
*/
|
|
14
|
-
|
|
15
|
-
client: {
|
|
16
|
-
app: {
|
|
17
|
-
log(entry: {
|
|
18
|
-
service: string;
|
|
19
|
-
level: 'debug' | 'info' | 'warn' | 'error';
|
|
20
|
-
message: string;
|
|
21
|
-
}): void;
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
directory: string;
|
|
25
|
-
}
|
|
26
|
-
interface PluginEvent {
|
|
27
|
-
type: string;
|
|
28
|
-
[key: string]: unknown;
|
|
29
|
-
}
|
|
30
|
-
type Plugin = (context: PluginContext) => Promise<{
|
|
31
|
-
agent?: Record<string, unknown>;
|
|
32
|
-
'experimental.chat.system.transform'?: (input: unknown, output: {
|
|
33
|
-
system?: string[];
|
|
34
|
-
}) => Promise<void>;
|
|
35
|
-
tool?: Record<string, unknown>;
|
|
36
|
-
event?: (context: {
|
|
37
|
-
event: PluginEvent;
|
|
38
|
-
}) => Promise<void>;
|
|
39
|
-
config?: (config: unknown) => Promise<void>;
|
|
40
|
-
}>;
|
|
14
|
+
import { type Plugin } from '@opencode-ai/plugin';
|
|
41
15
|
export declare const MaskweaverPlugin: Plugin;
|
|
42
16
|
export default MaskweaverPlugin;
|
|
43
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;EAYE;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;EAYE;AAGF,OAAO,EAAQ,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAu1BxD,eAAO,MAAM,gBAAgB,EAAE,MA0V9B,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
package/dist/plugin/index.js
CHANGED
|
@@ -12,17 +12,13 @@
|
|
|
12
12
|
* Based on oh-my-opencode plugin development patterns.
|
|
13
13
|
*/
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
-
// Inline shim: tool() is just an identity function in @opencode-ai/plugin
|
|
16
|
-
// We inline it to avoid bundler resolution bugs with the upstream package
|
|
17
|
-
const tool = (input) => input;
|
|
18
15
|
import * as fs from 'node:fs';
|
|
19
16
|
import * as path from 'node:path';
|
|
20
17
|
import * as os from 'node:os';
|
|
21
18
|
import { spawnSync } from 'node:child_process';
|
|
22
19
|
import { fileURLToPath } from 'node:url';
|
|
23
|
-
import { parse as parseYaml } from 'yaml';
|
|
24
20
|
import { VERSION } from '../version.js';
|
|
25
|
-
import { loadPluginConfig, isMaskEnabled, isToolEnabled, getDefaultMask, isAutoActivateEnabled,
|
|
21
|
+
import { loadPluginConfig, isMaskEnabled, isToolEnabled, getDefaultMask, isAutoActivateEnabled, isVerboseLoggingEnabled, isCompletionSoundEnabled, validateConfig, } from './config/index.js';
|
|
26
22
|
// New tool imports
|
|
27
23
|
import { createMemorySearchTool } from './tools/memorySearch.js';
|
|
28
24
|
import { createMemoryWriteTool } from './tools/memoryWrite.js';
|
|
@@ -34,6 +30,7 @@ import { createMaskSaveTool } from './tools/maskSave.js';
|
|
|
34
30
|
import { createSquadTool } from './tools/squad.js';
|
|
35
31
|
import { createWeaveTool } from './tools/weave.js';
|
|
36
32
|
import { createSlashcommandTool } from './tools/slashcommand.js';
|
|
33
|
+
import { loadRuntimeConfig, normalizeDummyHumansConfig } from '../shared/config.js';
|
|
37
34
|
const REMOVED_WEAVE_COMMAND_FILES = [
|
|
38
35
|
'weave-task.md',
|
|
39
36
|
'weave-task-auto.md',
|
|
@@ -145,6 +142,70 @@ function installAssets(projectDir) {
|
|
|
145
142
|
return result;
|
|
146
143
|
}
|
|
147
144
|
// ============================================================================
|
|
145
|
+
// Pool Agent Generator
|
|
146
|
+
// ============================================================================
|
|
147
|
+
/**
|
|
148
|
+
* Generate dummy-human agent .md files from maskweaver.config.json's dummyHumans.pool.
|
|
149
|
+
*
|
|
150
|
+
* For each entry in the pool, creates .opencode/agents/dummy-{id}.md with the
|
|
151
|
+
* model field set to the pool entry's model. This ensures the squad system's
|
|
152
|
+
* model pool and the actual agent files stay in sync.
|
|
153
|
+
*
|
|
154
|
+
* Skips existing files to protect user customizations.
|
|
155
|
+
*/
|
|
156
|
+
function generatePoolAgents(projectDir) {
|
|
157
|
+
const generated = [];
|
|
158
|
+
// Read runtime config
|
|
159
|
+
const config = loadRuntimeConfig(projectDir);
|
|
160
|
+
if (!config.dummyHumans) {
|
|
161
|
+
return generated; // No pool configured, nothing to generate
|
|
162
|
+
}
|
|
163
|
+
const pool = normalizeDummyHumansConfig(config.dummyHumans);
|
|
164
|
+
if (pool.length === 0) {
|
|
165
|
+
return generated;
|
|
166
|
+
}
|
|
167
|
+
const agentsDest = path.join(projectDir, '.opencode', 'agents');
|
|
168
|
+
if (!fs.existsSync(agentsDest)) {
|
|
169
|
+
try {
|
|
170
|
+
fs.mkdirSync(agentsDest, { recursive: true });
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return generated; // Cannot create directory, skip silently
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
for (const entry of pool) {
|
|
177
|
+
const agentName = `dummy-${entry.id}`;
|
|
178
|
+
const agentPath = path.join(agentsDest, `${agentName}.md`);
|
|
179
|
+
// Skip if already exists (protect user customizations)
|
|
180
|
+
if (fs.existsSync(agentPath)) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const content = [
|
|
184
|
+
'---',
|
|
185
|
+
`description: "Dummy-Human (${entry.id}) - Auto-generated from maskweaver.config.json pool"`,
|
|
186
|
+
`model: ${entry.model}`,
|
|
187
|
+
'mode: subagent',
|
|
188
|
+
'temperature: 0.2',
|
|
189
|
+
'permission:',
|
|
190
|
+
' edit: allow',
|
|
191
|
+
' bash: allow',
|
|
192
|
+
' webfetch: allow',
|
|
193
|
+
'---',
|
|
194
|
+
'',
|
|
195
|
+
'Faithfully executes instructions from Mask Weaver.',
|
|
196
|
+
'',
|
|
197
|
+
].join('\n');
|
|
198
|
+
try {
|
|
199
|
+
fs.writeFileSync(agentPath, content, 'utf-8');
|
|
200
|
+
generated.push(agentPath);
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
// Skip files that fail to write
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return generated;
|
|
207
|
+
}
|
|
208
|
+
// ============================================================================
|
|
148
209
|
// Simple YAML Parser
|
|
149
210
|
// ============================================================================
|
|
150
211
|
function parseSimpleYaml(content) {
|
|
@@ -425,8 +486,17 @@ function buildRichPrompt(mask) {
|
|
|
425
486
|
}
|
|
426
487
|
return parts.join('\n');
|
|
427
488
|
}
|
|
489
|
+
function pluginLog(client, level, message) {
|
|
490
|
+
client.app.log({
|
|
491
|
+
body: {
|
|
492
|
+
service: 'maskweaver',
|
|
493
|
+
level,
|
|
494
|
+
message,
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
}
|
|
428
498
|
// ============================================================================
|
|
429
|
-
//
|
|
499
|
+
// Helper functions for tool factories
|
|
430
500
|
// ============================================================================
|
|
431
501
|
function createListMasksTool(maskLoader, activeMask) {
|
|
432
502
|
return {
|
|
@@ -602,81 +672,8 @@ function playCompletionSound(config) {
|
|
|
602
672
|
}
|
|
603
673
|
}
|
|
604
674
|
let state = null;
|
|
605
|
-
function parseAgentMarkdown(content) {
|
|
606
|
-
const parts = content.split('---');
|
|
607
|
-
if (parts.length < 3) {
|
|
608
|
-
return { prompt: content.trim() };
|
|
609
|
-
}
|
|
610
|
-
try {
|
|
611
|
-
const frontmatter = parseYaml(parts[1]);
|
|
612
|
-
const prompt = parts.slice(2).join('---').trim();
|
|
613
|
-
return { ...frontmatter, prompt };
|
|
614
|
-
}
|
|
615
|
-
catch (e) {
|
|
616
|
-
return { prompt: content.trim() };
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
// ============================================================================
|
|
620
|
-
// Default Embedded Agents (ensures first-run works without restart)
|
|
621
675
|
// ============================================================================
|
|
622
|
-
const
|
|
623
|
-
'dummy-human': {
|
|
624
|
-
description: 'Dummy-Human - Pure execution agent that performs tasks with masks assigned by Mask Weaver',
|
|
625
|
-
mode: 'subagent',
|
|
626
|
-
temperature: 0.2,
|
|
627
|
-
permission: {
|
|
628
|
-
edit: 'allow',
|
|
629
|
-
bash: 'allow',
|
|
630
|
-
webfetch: 'allow',
|
|
631
|
-
},
|
|
632
|
-
prompt: `# Dummy-Human
|
|
633
|
-
|
|
634
|
-
You are a **Dummy-Human**.
|
|
635
|
-
|
|
636
|
-
## Identity
|
|
637
|
-
|
|
638
|
-
You are a pure execution agent. You accurately perform work instructions received from the Mask Weaver.
|
|
639
|
-
|
|
640
|
-
## Behavior Principles
|
|
641
|
-
|
|
642
|
-
1. If the Mask Weaver provides a **mask (persona)**, become that expert and work accordingly
|
|
643
|
-
2. If no mask is provided, work as a competent software engineer
|
|
644
|
-
3. Complete assigned tasks accurately
|
|
645
|
-
4. Report results clearly
|
|
646
|
-
|
|
647
|
-
## Result Reporting
|
|
648
|
-
|
|
649
|
-
When work is complete:
|
|
650
|
-
- Summary of work performed
|
|
651
|
-
- Generated outputs
|
|
652
|
-
- Additional considerations (if any)`,
|
|
653
|
-
},
|
|
654
|
-
};
|
|
655
|
-
function loadAgentAssets(...assetsDirs) {
|
|
656
|
-
// Start with default embedded agents (always available)
|
|
657
|
-
const agents = { ...DEFAULT_AGENTS };
|
|
658
|
-
// Load from each directory in order (later directories override earlier ones)
|
|
659
|
-
for (const assetsDir of assetsDirs) {
|
|
660
|
-
const agentsDir = path.join(assetsDir, 'agents');
|
|
661
|
-
if (!fs.existsSync(agentsDir))
|
|
662
|
-
continue;
|
|
663
|
-
try {
|
|
664
|
-
const files = fs.readdirSync(agentsDir);
|
|
665
|
-
for (const file of files) {
|
|
666
|
-
if (file.endsWith('.md') && file !== 'dummy-template.md') {
|
|
667
|
-
const agentId = path.basename(file, '.md');
|
|
668
|
-
const content = fs.readFileSync(path.join(agentsDir, file), 'utf-8');
|
|
669
|
-
agents[agentId] = parseAgentMarkdown(content);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
catch (e) {
|
|
674
|
-
// Ignore errors - default agents still available
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
return agents;
|
|
678
|
-
}
|
|
679
|
-
export const MaskweaverPlugin = async ({ client, directory }) => {
|
|
676
|
+
export const MaskweaverPlugin = async ({ client, directory, project, worktree, $, serverUrl }) => {
|
|
680
677
|
// ==========================================================================
|
|
681
678
|
// 1. Load Configuration (oh-my-opencode pattern)
|
|
682
679
|
// ==========================================================================
|
|
@@ -684,11 +681,7 @@ export const MaskweaverPlugin = async ({ client, directory }) => {
|
|
|
684
681
|
// Validate configuration
|
|
685
682
|
const configErrors = validateConfig(pluginConfig);
|
|
686
683
|
if (configErrors.length > 0) {
|
|
687
|
-
client.
|
|
688
|
-
service: 'maskweaver',
|
|
689
|
-
level: 'warn',
|
|
690
|
-
message: `Configuration validation errors: ${configErrors.join(', ')}`,
|
|
691
|
-
});
|
|
684
|
+
pluginLog(client, 'warn', `Configuration validation errors: ${configErrors.join(', ')}`);
|
|
692
685
|
}
|
|
693
686
|
const verbose = isVerboseLoggingEnabled(pluginConfig);
|
|
694
687
|
// ==========================================================================
|
|
@@ -698,24 +691,20 @@ export const MaskweaverPlugin = async ({ client, directory }) => {
|
|
|
698
691
|
// Track if this is a first-time installation
|
|
699
692
|
const isFirstInstall = installResult.installed.length > 0;
|
|
700
693
|
if (isFirstInstall) {
|
|
701
|
-
client.
|
|
702
|
-
service: 'maskweaver',
|
|
703
|
-
level: 'info',
|
|
704
|
-
message: `Installed ${installResult.installed.length} files to .opencode/ (agents, masks)`,
|
|
705
|
-
});
|
|
694
|
+
pluginLog(client, 'info', `Installed ${installResult.installed.length} files to .opencode/ (agents, masks)`);
|
|
706
695
|
// Show prominent restart message for first-time installation
|
|
707
|
-
client
|
|
708
|
-
service: 'maskweaver',
|
|
709
|
-
level: 'warn',
|
|
710
|
-
message: `⚠️ RESTART REQUIRED: Please restart OpenCode to activate all Maskweaver features (agents, masks, commands).`,
|
|
711
|
-
});
|
|
696
|
+
pluginLog(client, 'warn', `⚠️ RESTART REQUIRED: Please restart OpenCode to activate all Maskweaver features (agents, masks, commands).`);
|
|
712
697
|
}
|
|
713
698
|
if (installResult.errors.length > 0) {
|
|
714
|
-
client.
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
699
|
+
pluginLog(client, 'warn', `Asset errors: ${installResult.errors.join(', ')}`);
|
|
700
|
+
}
|
|
701
|
+
// ==========================================================================
|
|
702
|
+
// 2b. Generate pool agents from maskweaver.config.json (dummyHumans.pool)
|
|
703
|
+
// ==========================================================================
|
|
704
|
+
const generatedAgents = generatePoolAgents(directory);
|
|
705
|
+
if (generatedAgents.length > 0) {
|
|
706
|
+
pluginLog(client, 'info', `Generated ${generatedAgents.length} pool agent files from maskweaver.config.json: ${generatedAgents.map(p => path.basename(p)).join(', ')}`);
|
|
707
|
+
pluginLog(client, 'warn', `⚠️ RESTART REQUIRED: Please restart OpenCode to activate the new pool agent files.`);
|
|
719
708
|
}
|
|
720
709
|
// ==========================================================================
|
|
721
710
|
// 3. Initialize masks
|
|
@@ -734,29 +723,17 @@ export const MaskweaverPlugin = async ({ client, directory }) => {
|
|
|
734
723
|
};
|
|
735
724
|
state = pluginState;
|
|
736
725
|
// Log plugin loaded
|
|
737
|
-
client
|
|
738
|
-
service: 'maskweaver',
|
|
739
|
-
level: 'info',
|
|
740
|
-
message: `Maskweaver plugin loaded v${VERSION}`,
|
|
741
|
-
});
|
|
726
|
+
pluginLog(client, 'info', `Maskweaver plugin loaded v${VERSION}`);
|
|
742
727
|
if (fs.existsSync(masksDir)) {
|
|
743
728
|
pluginState.maskLoader = new MaskLoader(masksDir, pluginConfig);
|
|
744
729
|
try {
|
|
745
730
|
await pluginState.maskLoader.loadCatalog();
|
|
746
731
|
if (verbose) {
|
|
747
|
-
client
|
|
748
|
-
service: 'maskweaver',
|
|
749
|
-
level: 'info',
|
|
750
|
-
message: `Masks found at: ${masksDir}`,
|
|
751
|
-
});
|
|
732
|
+
pluginLog(client, 'info', `Masks found at: ${masksDir}`);
|
|
752
733
|
}
|
|
753
734
|
}
|
|
754
735
|
catch (e) {
|
|
755
|
-
client
|
|
756
|
-
service: 'maskweaver',
|
|
757
|
-
level: 'warn',
|
|
758
|
-
message: `Failed to load masks: ${e}`,
|
|
759
|
-
});
|
|
736
|
+
pluginLog(client, 'warn', `Failed to load masks: ${e}`);
|
|
760
737
|
pluginState.maskLoader = null;
|
|
761
738
|
}
|
|
762
739
|
}
|
|
@@ -770,26 +747,14 @@ export const MaskweaverPlugin = async ({ client, directory }) => {
|
|
|
770
747
|
const defaultMask = await pluginState.maskLoader.load(defaultMaskId);
|
|
771
748
|
if (defaultMask) {
|
|
772
749
|
pluginState.activeMask = defaultMask;
|
|
773
|
-
client.
|
|
774
|
-
service: 'maskweaver',
|
|
775
|
-
level: 'info',
|
|
776
|
-
message: `Auto-activated default mask: ${defaultMaskId} (${defaultMask.profile.name})`,
|
|
777
|
-
});
|
|
750
|
+
pluginLog(client, 'info', `Auto-activated default mask: ${defaultMaskId} (${defaultMask.profile.name})`);
|
|
778
751
|
}
|
|
779
752
|
else {
|
|
780
|
-
client
|
|
781
|
-
service: 'maskweaver',
|
|
782
|
-
level: 'warn',
|
|
783
|
-
message: `Default mask "${defaultMaskId}" not found or disabled`,
|
|
784
|
-
});
|
|
753
|
+
pluginLog(client, 'warn', `Default mask "${defaultMaskId}" not found or disabled`);
|
|
785
754
|
}
|
|
786
755
|
}
|
|
787
756
|
catch (e) {
|
|
788
|
-
client
|
|
789
|
-
service: 'maskweaver',
|
|
790
|
-
level: 'warn',
|
|
791
|
-
message: `Failed to auto-activate default mask: ${e}`,
|
|
792
|
-
});
|
|
757
|
+
pluginLog(client, 'warn', `Failed to auto-activate default mask: ${e}`);
|
|
793
758
|
}
|
|
794
759
|
}
|
|
795
760
|
// ==========================================================================
|
|
@@ -805,14 +770,15 @@ export const MaskweaverPlugin = async ({ client, directory }) => {
|
|
|
805
770
|
const isToolActive = (toolName) => isToolEnabled(pluginConfig, toolName);
|
|
806
771
|
// Helper to ensure tool arguments are compatible with opencode's expected format.
|
|
807
772
|
// opencode expects a ZodRawShape (raw object), NOT a ZodObject instance.
|
|
773
|
+
// Zod 4: schema.def.shape, Zod 3: schema._def.shape()
|
|
808
774
|
const wrapSchema = (schema) => {
|
|
809
775
|
if (!schema || typeof schema !== 'object')
|
|
810
776
|
return schema;
|
|
811
|
-
//
|
|
812
|
-
if (schema.def && typeof schema.def === 'object' && schema.type === 'object') {
|
|
777
|
+
// Zod 4 — def.shape is a plain object
|
|
778
|
+
if (schema.def && typeof schema.def === 'object' && schema.type === 'object' && schema.def.shape && typeof schema.def.shape === 'object') {
|
|
813
779
|
return schema.def.shape;
|
|
814
780
|
}
|
|
815
|
-
//
|
|
781
|
+
// Zod 3 — _def.shape() returns a plain object
|
|
816
782
|
if (schema._def && typeof schema._def.shape === 'function') {
|
|
817
783
|
return schema._def.shape();
|
|
818
784
|
}
|
|
@@ -934,104 +900,20 @@ export const MaskweaverPlugin = async ({ client, directory }) => {
|
|
|
934
900
|
};
|
|
935
901
|
}
|
|
936
902
|
// ==========================================================================
|
|
937
|
-
// 8.
|
|
903
|
+
// 8. Agents are loaded from .opencode/agents/*.md files by OpenCode's
|
|
904
|
+
// filesystem-based agent loader (see config/agent.ts:110-140).
|
|
905
|
+
// installAssets() in step 2 copies agent .md files so they are picked up.
|
|
906
|
+
// The 'agent' property in Hooks is NOT consumed by OpenCode — confirmed
|
|
907
|
+
// by source analysis (packages/opencode/src/plugin/index.ts:92-103).
|
|
938
908
|
// ==========================================================================
|
|
939
|
-
const assetsDir = getAssetsDir();
|
|
940
|
-
const projectOpencodeDir = path.join(directory, '.opencode');
|
|
941
|
-
// Load from package assets first, then project .opencode (project overrides package)
|
|
942
|
-
const loadedAgents = loadAgentAssets(assetsDir, projectOpencodeDir);
|
|
943
|
-
// ==========================================================================
|
|
944
|
-
// 8b. Generate agents from model pool (or legacy fallback)
|
|
945
|
-
// ==========================================================================
|
|
946
|
-
{
|
|
947
|
-
const { loadRuntimeConfig, normalizeDummyHumansConfig } = await import('../shared/config.js');
|
|
948
|
-
const { createModelRegistry } = await import('../shared/model-registry.js');
|
|
949
|
-
const runtimeConfig = loadRuntimeConfig(directory);
|
|
950
|
-
if (runtimeConfig.dummyHumans) {
|
|
951
|
-
const pool = normalizeDummyHumansConfig(runtimeConfig.dummyHumans);
|
|
952
|
-
// Initialize the global model registry
|
|
953
|
-
createModelRegistry(pool);
|
|
954
|
-
if (loadedAgents['dummy-human']) {
|
|
955
|
-
// Generate agent variants from pool entries
|
|
956
|
-
for (const entry of pool) {
|
|
957
|
-
const agentName = `dummy-${entry.id}`;
|
|
958
|
-
if (!loadedAgents[agentName]) {
|
|
959
|
-
const tierLabel = entry.tier === 'flash' ? 'Flash' : entry.tier === 'premium' ? 'Premium' : 'Standard';
|
|
960
|
-
const capStr = entry.capabilities.slice(0, 3).join(', ');
|
|
961
|
-
loadedAgents[agentName] = {
|
|
962
|
-
...loadedAgents['dummy-human'],
|
|
963
|
-
description: `Dummy-Human (${entry.id}) - ${tierLabel}. ${entry.description || capStr}. [max ${entry.maxConcurrent} concurrent]`,
|
|
964
|
-
model: entry.model,
|
|
965
|
-
};
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
// Also ensure legacy dummy-flash / dummy-premium aliases exist (for backward compat)
|
|
969
|
-
const flashEntry = pool.find(e => e.tier === 'flash');
|
|
970
|
-
const humanEntry = pool.find(e => e.tier === 'human');
|
|
971
|
-
const premiumEntry = pool.find(e => e.tier === 'premium');
|
|
972
|
-
if (flashEntry && !loadedAgents['dummy-flash']) {
|
|
973
|
-
loadedAgents['dummy-flash'] = loadedAgents[`dummy-${flashEntry.id}`];
|
|
974
|
-
}
|
|
975
|
-
if (humanEntry) {
|
|
976
|
-
// dummy-human already exists as the base agent; just update its model from pool
|
|
977
|
-
loadedAgents['dummy-human'].model = humanEntry.model;
|
|
978
|
-
}
|
|
979
|
-
if (premiumEntry && !loadedAgents['dummy-premium']) {
|
|
980
|
-
loadedAgents['dummy-premium'] = loadedAgents[`dummy-${premiumEntry.id}`];
|
|
981
|
-
}
|
|
982
|
-
// Fallback: if no tier mapping found, use defaults
|
|
983
|
-
if (!loadedAgents['dummy-flash']) {
|
|
984
|
-
loadedAgents['dummy-flash'] = {
|
|
985
|
-
...loadedAgents['dummy-human'],
|
|
986
|
-
description: 'Dummy-Human (Flash) - Fast and cheap',
|
|
987
|
-
model: 'google/gemini-2.0-flash',
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
if (!loadedAgents['dummy-premium']) {
|
|
991
|
-
loadedAgents['dummy-premium'] = {
|
|
992
|
-
...loadedAgents['dummy-human'],
|
|
993
|
-
description: 'Dummy-Human (Premium) - Powerful and reasoning',
|
|
994
|
-
model: 'google/gemini-2.0-pro-exp-02-05',
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
else {
|
|
1000
|
-
// No pool config → legacy hardcoded defaults
|
|
1001
|
-
if (loadedAgents['dummy-human']) {
|
|
1002
|
-
if (!loadedAgents['dummy-flash']) {
|
|
1003
|
-
loadedAgents['dummy-flash'] = {
|
|
1004
|
-
...loadedAgents['dummy-human'],
|
|
1005
|
-
description: 'Dummy-Human (Flash) - Fast and cheap',
|
|
1006
|
-
model: 'google/gemini-2.0-flash',
|
|
1007
|
-
};
|
|
1008
|
-
}
|
|
1009
|
-
if (!loadedAgents['dummy-premium']) {
|
|
1010
|
-
loadedAgents['dummy-premium'] = {
|
|
1011
|
-
...loadedAgents['dummy-human'],
|
|
1012
|
-
description: 'Dummy-Human (Premium) - Powerful and reasoning',
|
|
1013
|
-
model: 'google/gemini-2.0-pro-exp-02-05',
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
// Apply config overrides to agents
|
|
1020
|
-
for (const agentId of Object.keys(loadedAgents)) {
|
|
1021
|
-
const override = getAgentOverride(pluginConfig, agentId);
|
|
1022
|
-
if (override) {
|
|
1023
|
-
if (override.model)
|
|
1024
|
-
loadedAgents[agentId].model = override.model;
|
|
1025
|
-
if (override.systemPrompt)
|
|
1026
|
-
loadedAgents[agentId].prompt = override.systemPrompt;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
909
|
// ==========================================================================
|
|
1030
|
-
// 9. Return plugin hooks
|
|
910
|
+
// 9. Return plugin hooks (official OpenCode Hooks interface only)
|
|
911
|
+
// Note: Agents are registered via .opencode/agents/*.md files (installed by
|
|
912
|
+
// installAssets()), NOT via the plugin return. The Hooks type does not
|
|
913
|
+
// include 'agent' — OpenCode loads agents from the filesystem exclusively.
|
|
1031
914
|
// ==========================================================================
|
|
1032
915
|
return {
|
|
1033
|
-
// Agent registration
|
|
1034
|
-
agent: loadedAgents,
|
|
916
|
+
// Agent registration handled via .opencode/agents/*.md files (see installAssets)
|
|
1035
917
|
// System prompt transform - inject active mask
|
|
1036
918
|
'experimental.chat.system.transform': async (_input, output) => {
|
|
1037
919
|
if (state?.activeMask) {
|
|
@@ -1054,11 +936,7 @@ ${buildRichPrompt(state.activeMask)}
|
|
|
1054
936
|
try {
|
|
1055
937
|
const masks = await pluginState.maskLoader.listAll();
|
|
1056
938
|
const categories = await pluginState.maskLoader.listCategories();
|
|
1057
|
-
client.
|
|
1058
|
-
service: 'maskweaver',
|
|
1059
|
-
level: 'info',
|
|
1060
|
-
message: `Session started - ${masks.length} masks available across ${categories.length} categories`,
|
|
1061
|
-
});
|
|
939
|
+
pluginLog(client, 'info', `Session started - ${masks.length} masks available across ${categories.length} categories`);
|
|
1062
940
|
}
|
|
1063
941
|
catch (_e) {
|
|
1064
942
|
// Ignore errors
|
|
@@ -1085,19 +963,14 @@ ${buildRichPrompt(state.activeMask)}
|
|
|
1085
963
|
const wasActive = pluginState.activeMask !== null;
|
|
1086
964
|
pluginState.activeMask = null;
|
|
1087
965
|
if (wasActive) {
|
|
1088
|
-
client
|
|
1089
|
-
service: 'maskweaver',
|
|
1090
|
-
level: 'info',
|
|
1091
|
-
message: 'Session ended - active mask cleared',
|
|
1092
|
-
});
|
|
966
|
+
pluginLog(client, 'info', 'Session ended - active mask cleared');
|
|
1093
967
|
}
|
|
1094
968
|
}
|
|
1095
969
|
}
|
|
1096
970
|
},
|
|
1097
|
-
// Config hook -
|
|
971
|
+
// Config hook - allows plugins to modify opencode configuration
|
|
1098
972
|
config: async (config) => {
|
|
1099
|
-
//
|
|
1100
|
-
// Agent overrides are currently not supported via this hook in opencode core.
|
|
973
|
+
// Reserved for future configuration injection (model, provider, etc.)
|
|
1101
974
|
return;
|
|
1102
975
|
},
|
|
1103
976
|
};
|