maskweaver 0.9.3 → 0.9.5
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 +279 -325
- package/README.md +109 -113
- package/assets/commands/meta/commands.json +34 -34
- package/assets/commands/weave-agents.md +12 -52
- package/assets/commands/weave-approve.md +12 -51
- package/assets/commands/weave-archive.md +21 -0
- package/assets/commands/weave-build.md +20 -89
- package/assets/commands/weave-craft.md +22 -43
- package/assets/commands/weave-help.md +37 -106
- package/assets/commands/weave-init.md +26 -108
- package/assets/commands/weave-interview.md +13 -111
- package/assets/commands/weave-map.md +13 -99
- package/assets/commands/weave-prepare.md +23 -69
- package/assets/commands/weave-refine-plan.md +26 -59
- package/assets/commands/weave-repair.md +22 -70
- package/assets/commands/weave-status.md +22 -155
- package/assets/commands/weave-troubleshoot.md +11 -47
- package/assets/commands/weave-verify.md +23 -44
- package/assets/commands/weave-worktree.md +27 -69
- package/dist/cli/doctor.js +5 -21
- package/dist/cli/install.d.ts +0 -8
- package/dist/cli/install.js +0 -39
- package/dist/context/config.d.ts +0 -22
- package/dist/context/config.js +0 -28
- package/dist/context/feature.d.ts +0 -39
- package/dist/context/feature.js +0 -77
- package/dist/context/files.d.ts +0 -13
- package/dist/context/files.js +1 -24
- package/dist/context/index.d.ts +0 -7
- package/dist/context/index.js +0 -12
- package/dist/context/project.d.ts +0 -21
- package/dist/context/project.js +0 -30
- package/dist/context/types.d.ts +0 -48
- package/dist/context/types.js +0 -12
- package/dist/context/utils.d.ts +0 -18
- package/dist/context/utils.js +0 -27
- package/dist/core/engine/promptBuilder.d.ts +0 -17
- package/dist/core/engine/promptBuilder.js +0 -28
- package/dist/core/index.d.ts +0 -6
- package/dist/core/index.js +0 -9
- package/dist/core/loader/MaskLoader.d.ts +0 -23
- package/dist/core/loader/MaskLoader.js +0 -29
- package/dist/core/schema/types.d.ts +0 -47
- package/dist/core/schema/types.js +0 -6
- package/dist/core/schema/validator.d.ts +0 -14
- package/dist/core/schema/validator.js +0 -18
- package/dist/i18n/index.d.ts +0 -18
- package/dist/i18n/index.js +4 -23
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -8
- package/dist/lib.d.ts +0 -5
- package/dist/lib.js +0 -12
- package/dist/memory/chunking.d.ts +0 -22
- package/dist/memory/chunking.js +2 -37
- package/dist/memory/core.d.ts +0 -29
- package/dist/memory/core.js +1 -52
- package/dist/memory/index.d.ts +0 -5
- package/dist/memory/index.js +0 -10
- package/dist/memory/indexer.d.ts +0 -21
- package/dist/memory/indexer.js +0 -44
- package/dist/memory/providers/examples.d.ts +0 -5
- package/dist/memory/providers/examples.js +4 -64
- package/dist/memory/providers/factory.d.ts +0 -44
- package/dist/memory/providers/factory.js +0 -46
- package/dist/memory/providers/index.d.ts +0 -26
- package/dist/memory/providers/index.js +0 -28
- package/dist/memory/providers/ollama.d.ts +0 -6
- package/dist/memory/providers/ollama.js +1 -8
- package/dist/memory/providers/openai.d.ts +0 -6
- package/dist/memory/providers/openai.js +1 -8
- package/dist/memory/providers/openrouter.d.ts +0 -6
- package/dist/memory/providers/openrouter.js +0 -8
- package/dist/memory/providers/text-only.d.ts +0 -13
- package/dist/memory/providers/text-only.js +0 -17
- package/dist/memory/providers/types.d.ts +0 -39
- package/dist/memory/providers/types.js +0 -7
- package/dist/memory/providers/voyage.d.ts +0 -22
- package/dist/memory/providers/voyage.js +1 -24
- package/dist/memory/search/hybrid.d.ts +0 -12
- package/dist/memory/search/hybrid.js +1 -22
- package/dist/memory/store/sqlite.d.ts +0 -72
- package/dist/memory/store/sqlite.js +4 -127
- package/dist/plugin/config/index.d.ts +0 -112
- package/dist/plugin/config/index.js +0 -115
- package/dist/plugin/index.d.ts +0 -13
- package/dist/plugin/index.js +1 -124
- package/dist/plugin/tools/command-registry.d.ts +0 -6
- package/dist/plugin/tools/command-registry.js +0 -14
- package/dist/plugin/tools/context.d.ts +0 -12
- package/dist/plugin/tools/context.js +0 -58
- package/dist/plugin/tools/maskSave.d.ts +0 -3
- package/dist/plugin/tools/maskSave.js +0 -3
- package/dist/plugin/tools/memoryGet.d.ts +0 -3
- package/dist/plugin/tools/memoryGet.js +0 -3
- package/dist/plugin/tools/memoryIndexer.d.ts +0 -3
- package/dist/plugin/tools/memoryIndexer.js +0 -10
- package/dist/plugin/tools/memorySearch.d.ts +0 -31
- package/dist/plugin/tools/memorySearch.js +0 -79
- package/dist/plugin/tools/memoryWrite.d.ts +0 -8
- package/dist/plugin/tools/memoryWrite.js +0 -32
- package/dist/plugin/tools/retrospect.d.ts +0 -3
- package/dist/plugin/tools/retrospect.js +0 -3
- package/dist/plugin/tools/slashcommand.d.ts +0 -11
- package/dist/plugin/tools/slashcommand.js +0 -38
- package/dist/plugin/tools/squad.d.ts +0 -12
- package/dist/plugin/tools/squad.js +11 -83
- package/dist/plugin/tools/weave.d.ts +0 -6
- package/dist/plugin/tools/weave.js +0 -78
- package/dist/plugin/types.d.ts +0 -20
- package/dist/plugin/types.js +0 -7
- package/dist/retrospect/index.d.ts +0 -7
- package/dist/retrospect/index.js +0 -9
- package/dist/retrospect/mask-save.d.ts +0 -12
- package/dist/retrospect/mask-save.js +1 -80
- package/dist/retrospect/retrospect.d.ts +0 -18
- package/dist/retrospect/retrospect.js +0 -63
- package/dist/retrospect/strategies/base.d.ts +0 -15
- package/dist/retrospect/strategies/base.js +0 -7
- package/dist/retrospect/strategies/deep.d.ts +0 -12
- package/dist/retrospect/strategies/deep.js +0 -24
- package/dist/retrospect/strategies/index.d.ts +0 -12
- package/dist/retrospect/strategies/index.js +0 -12
- package/dist/retrospect/strategies/quick.d.ts +0 -12
- package/dist/retrospect/strategies/quick.js +0 -19
- package/dist/retrospect/strategies/standard.d.ts +0 -12
- package/dist/retrospect/strategies/standard.js +0 -15
- package/dist/retrospect/types.d.ts +0 -7
- package/dist/retrospect/types.js +0 -7
- package/dist/shared/config.d.ts +0 -105
- package/dist/shared/config.js +0 -33
- package/dist/shared/errors.d.ts +0 -18
- package/dist/shared/errors.js +0 -19
- package/dist/shared/generate-agents.d.ts +0 -69
- package/dist/shared/generate-agents.js +2 -86
- package/dist/shared/image.d.ts +0 -67
- package/dist/shared/image.js +6 -104
- package/dist/shared/index.d.ts +0 -5
- package/dist/shared/index.js +0 -7
- package/dist/shared/model-registry.d.ts +0 -72
- package/dist/shared/model-registry.js +5 -95
- package/dist/shared/types.d.ts +0 -15
- package/dist/shared/types.js +0 -3
- package/dist/shared-context/dag.d.ts +0 -105
- package/dist/shared-context/dag.js +3 -114
- package/dist/shared-context/index.d.ts +0 -5
- package/dist/shared-context/index.js +0 -15
- package/dist/shared-context/logger.d.ts +0 -37
- package/dist/shared-context/logger.js +0 -41
- package/dist/shared-context/parallel-executor.d.ts +0 -54
- package/dist/shared-context/parallel-executor.js +4 -56
- package/dist/shared-context/session.d.ts +0 -56
- package/dist/shared-context/session.js +0 -47
- package/dist/shared-context/squad.d.ts +0 -68
- package/dist/shared-context/squad.js +0 -63
- package/dist/shared-context/storage.d.ts +0 -132
- package/dist/shared-context/storage.js +0 -116
- package/dist/shared-context/task.d.ts +0 -120
- package/dist/shared-context/task.js +0 -152
- package/dist/shared-context/test/dag.test.js +9 -14
- package/dist/shared-context/test/logger.test.d.ts +0 -8
- package/dist/shared-context/test/logger.test.js +0 -52
- package/dist/shared-context/test/session.test.d.ts +0 -7
- package/dist/shared-context/test/session.test.js +0 -63
- package/dist/shared-context/test/squad.test.d.ts +0 -10
- package/dist/shared-context/test/squad.test.js +2 -68
- package/dist/shared-context/test/storage.test.d.ts +0 -8
- package/dist/shared-context/test/storage.test.js +0 -68
- package/dist/shared-context/test/task.test.d.ts +0 -7
- package/dist/shared-context/test/task.test.js +0 -54
- package/dist/shared-context/test/watchdog.test.d.ts +0 -7
- package/dist/shared-context/test/watchdog.test.js +3 -58
- package/dist/shared-context/types.d.ts +0 -215
- package/dist/shared-context/types.js +0 -125
- package/dist/shared-context/watchdog.d.ts +0 -127
- package/dist/shared-context/watchdog.js +0 -148
- package/dist/shared-context/worktree.d.ts +0 -68
- package/dist/shared-context/worktree.js +2 -34
- package/dist/verify/budget.d.ts +0 -29
- package/dist/verify/budget.js +0 -34
- package/dist/verify/critical-files.d.ts +0 -17
- package/dist/verify/critical-files.js +0 -37
- package/dist/verify/escalation.d.ts +0 -20
- package/dist/verify/escalation.js +0 -22
- package/dist/verify/index.d.ts +0 -5
- package/dist/verify/index.js +0 -11
- package/dist/verify/prompts.d.ts +0 -20
- package/dist/verify/prompts.js +0 -20
- package/dist/verify/types.d.ts +0 -26
- package/dist/verify/types.js +1 -12
- package/dist/verify/verifier.d.ts +0 -29
- package/dist/verify/verifier.js +0 -54
- package/dist/version.d.ts +1 -16
- package/dist/version.js +1 -16
- package/dist/weave/bridge.d.ts +0 -35
- package/dist/weave/bridge.js +0 -51
- package/dist/weave/environment/detector.d.ts +0 -6
- package/dist/weave/environment/detector.js +4 -45
- package/dist/weave/environment/index.d.ts +0 -19
- package/dist/weave/environment/index.js +1 -39
- package/dist/weave/environment/issues.d.ts +0 -35
- package/dist/weave/environment/issues.js +0 -59
- package/dist/weave/git.d.ts +0 -8
- package/dist/weave/git.js +0 -8
- package/dist/weave/index.d.ts +0 -13
- package/dist/weave/index.js +2 -28
- package/dist/weave/knowledge/global.d.ts +0 -39
- package/dist/weave/knowledge/global.js +2 -78
- package/dist/weave/loop.js +0 -3
- package/dist/weave/orchestrator.d.ts +0 -69
- package/dist/weave/orchestrator.js +1 -101
- package/dist/weave/phase-manager.d.ts +0 -64
- package/dist/weave/phase-manager.js +0 -89
- package/dist/weave/security/secret-scan.d.ts +0 -14
- package/dist/weave/security/secret-scan.js +0 -19
- package/dist/weave/stages/build.js +0 -15
- package/dist/weave/stages/execute.d.ts +0 -42
- package/dist/weave/stages/execute.js +4 -86
- package/dist/weave/stages/handoff.d.ts +0 -7
- package/dist/weave/stages/handoff.js +0 -43
- package/dist/weave/stages/index.d.ts +0 -3
- package/dist/weave/stages/index.js +0 -3
- package/dist/weave/stages/intake.d.ts +0 -8
- package/dist/weave/stages/intake.js +5 -65
- package/dist/weave/stages/map.d.ts +0 -1
- package/dist/weave/stages/openspec.d.ts +0 -1
- package/dist/weave/stages/plan.d.ts +0 -11
- package/dist/weave/stages/plan.js +1 -53
- package/dist/weave/stages/refine.d.ts +0 -7
- package/dist/weave/stages/refine.js +0 -7
- package/dist/weave/stages/research.d.ts +0 -6
- package/dist/weave/stages/research.js +0 -6
- package/dist/weave/stages/spec.d.ts +0 -12
- package/dist/weave/stages/spec.js +0 -17
- package/dist/weave/types.d.ts +0 -20
- package/dist/weave/types.js +0 -5
- package/dist/weave/verification/commands.d.ts +0 -12
- package/dist/weave/verification/commands.js +0 -19
- package/dist/weave/verification/index.d.ts +0 -6
- package/dist/weave/verification/index.js +1 -19
- package/dist/weave/verification/playwright.d.ts +0 -47
- package/dist/weave/verification/playwright.js +1 -90
- package/dist/weave/worktree.d.ts +0 -16
- package/dist/weave/worktree.js +0 -23
- package/dist/weave/yaml-repair.d.ts +0 -39
- package/dist/weave/yaml-repair.js +13 -116
- package/package.json +1 -1
package/dist/plugin/index.js
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Maskweaver Plugin for opencode
|
|
3
|
-
*
|
|
4
|
-
* Key features:
|
|
5
|
-
* - Configuration-driven tool activation/deactivation
|
|
6
|
-
* - Auto-activation of default masks
|
|
7
|
-
* - Agent configuration overrides
|
|
8
|
-
* - Event-based lifecycle hooks
|
|
9
|
-
* - Memory and context management tools
|
|
10
|
-
* - Clean plugin architecture
|
|
11
|
-
*
|
|
12
|
-
* Based on oh-my-opencode plugin development patterns.
|
|
13
|
-
*/
|
|
14
1
|
import { z } from 'zod';
|
|
15
2
|
import * as fs from 'node:fs';
|
|
16
3
|
import * as path from 'node:path';
|
|
@@ -19,7 +6,6 @@ import { spawnSync } from 'node:child_process';
|
|
|
19
6
|
import { fileURLToPath } from 'node:url';
|
|
20
7
|
import { VERSION } from '../version.js';
|
|
21
8
|
import { loadPluginConfig, isMaskEnabled, isToolEnabled, getDefaultMask, isAutoActivateEnabled, isVerboseLoggingEnabled, isCompletionSoundEnabled, validateConfig, } from './config/index.js';
|
|
22
|
-
// New tool imports
|
|
23
9
|
import { createMemorySearchTool } from './tools/memorySearch.js';
|
|
24
10
|
import { createMemoryWriteTool } from './tools/memoryWrite.js';
|
|
25
11
|
import { createMemoryGetTool } from './tools/memoryGet.js';
|
|
@@ -48,23 +34,19 @@ const REMOVED_WEAVE_COMMAND_FILES = [
|
|
|
48
34
|
'weave-task.md',
|
|
49
35
|
'weave-task-auto.md',
|
|
50
36
|
'wave-task-auto.md',
|
|
51
|
-
'weave-approve.md',
|
|
52
37
|
];
|
|
53
38
|
function getAssetsDir() {
|
|
54
39
|
try {
|
|
55
40
|
const __filename = fileURLToPath(import.meta.url);
|
|
56
41
|
const __dirname = path.dirname(__filename);
|
|
57
|
-
// 1. If in dist/plugin/ (production) -> ../../assets
|
|
58
42
|
const distAssets = path.join(__dirname, '..', '..', 'assets');
|
|
59
43
|
if (fs.existsSync(distAssets)) {
|
|
60
44
|
return distAssets;
|
|
61
45
|
}
|
|
62
|
-
// 2. If in src/plugin/ (development) -> ../../assets
|
|
63
46
|
const srcAssets = path.join(__dirname, '..', '..', 'assets');
|
|
64
47
|
if (fs.existsSync(srcAssets)) {
|
|
65
48
|
return srcAssets;
|
|
66
49
|
}
|
|
67
|
-
// 3. Fallback for npm package structure (node_modules/maskweaver/dist/plugin/index.js)
|
|
68
50
|
return distAssets;
|
|
69
51
|
}
|
|
70
52
|
catch {
|
|
@@ -110,7 +92,6 @@ function installAssets(projectDir) {
|
|
|
110
92
|
const homeDir = os.homedir();
|
|
111
93
|
const globalConfigDir = path.join(homeDir, '.config', 'opencode');
|
|
112
94
|
const projectOpencodeDir = path.join(projectDir, '.opencode');
|
|
113
|
-
// Install to both global and project directories to ensure visibility
|
|
114
95
|
const targetDirs = [projectOpencodeDir];
|
|
115
96
|
if (fs.existsSync(globalConfigDir)) {
|
|
116
97
|
targetDirs.push(globalConfigDir);
|
|
@@ -125,21 +106,17 @@ function installAssets(projectDir) {
|
|
|
125
106
|
continue;
|
|
126
107
|
}
|
|
127
108
|
}
|
|
128
|
-
// Install agents
|
|
129
109
|
const agentsSrc = path.join(assetsDir, 'agents');
|
|
130
110
|
const agentsDest = path.join(targetDir, 'agents');
|
|
131
111
|
copyDirRecursive(agentsSrc, agentsDest, result);
|
|
132
|
-
// Install masks
|
|
133
112
|
const masksSrc = path.join(assetsDir, 'masks');
|
|
134
113
|
const masksDest = path.join(targetDir, 'masks');
|
|
135
114
|
copyDirRecursive(masksSrc, masksDest, result);
|
|
136
|
-
// Install commands (always overwrite to keep commands up-to-date)
|
|
137
115
|
const commandsSrc = path.join(assetsDir, 'commands');
|
|
138
116
|
const commandsDest = path.join(targetDir, 'commands');
|
|
139
117
|
if (fs.existsSync(commandsSrc)) {
|
|
140
118
|
copyDirRecursive(commandsSrc, commandsDest, result, true);
|
|
141
119
|
}
|
|
142
|
-
// Hard-remove deprecated weave commands to keep a craft-centric flow.
|
|
143
120
|
for (const commandFile of REMOVED_WEAVE_COMMAND_FILES) {
|
|
144
121
|
const legacyPath = path.join(commandsDest, commandFile);
|
|
145
122
|
if (!fs.existsSync(legacyPath))
|
|
@@ -154,29 +131,11 @@ function installAssets(projectDir) {
|
|
|
154
131
|
}
|
|
155
132
|
return result;
|
|
156
133
|
}
|
|
157
|
-
// ============================================================================
|
|
158
|
-
// Pool Agent Generator
|
|
159
|
-
// ============================================================================
|
|
160
|
-
/**
|
|
161
|
-
* Generate dummy-human agent .md files from maskweaver.config.json's dummyHumans.pool.
|
|
162
|
-
*
|
|
163
|
-
* For each pool entry with a non-empty model, creates .opencode/agents/dummy-{id}.md.
|
|
164
|
-
* Skips existing files to protect user customizations — use `weave sync-agents`
|
|
165
|
-
* to force overwrite from the config.
|
|
166
|
-
*
|
|
167
|
-
* Also skips entries with empty model names (template placeholders) to prevent
|
|
168
|
-
* generating broken agent files.
|
|
169
|
-
*/
|
|
170
134
|
function generatePoolAgents(projectDir) {
|
|
171
135
|
const agentsDir = path.join(projectDir, '.opencode', 'agents');
|
|
172
|
-
// Use shared utility — force=false means skip existing
|
|
173
136
|
const result = generatePoolAgentFilesFromConfig(projectDir, agentsDir, { force: false });
|
|
174
|
-
// Extract successfully created files (not skipped, not updated)
|
|
175
137
|
return result.created;
|
|
176
138
|
}
|
|
177
|
-
// ============================================================================
|
|
178
|
-
// Simple YAML Parser
|
|
179
|
-
// ============================================================================
|
|
180
139
|
function parseSimpleYaml(content) {
|
|
181
140
|
const lines = content.split('\n');
|
|
182
141
|
const result = {};
|
|
@@ -332,9 +291,6 @@ function parseValue(value) {
|
|
|
332
291
|
return num;
|
|
333
292
|
return value;
|
|
334
293
|
}
|
|
335
|
-
// ============================================================================
|
|
336
|
-
// Mask Loader
|
|
337
|
-
// ============================================================================
|
|
338
294
|
class MaskLoader {
|
|
339
295
|
masksDir;
|
|
340
296
|
catalog = null;
|
|
@@ -355,7 +311,6 @@ class MaskLoader {
|
|
|
355
311
|
return this.catalog;
|
|
356
312
|
}
|
|
357
313
|
async load(maskId) {
|
|
358
|
-
// Check if mask is disabled in configuration
|
|
359
314
|
if (!isMaskEnabled(this.config, maskId)) {
|
|
360
315
|
return null;
|
|
361
316
|
}
|
|
@@ -389,7 +344,6 @@ class MaskLoader {
|
|
|
389
344
|
const result = [];
|
|
390
345
|
for (const [categoryId, category] of Object.entries(catalog.categories)) {
|
|
391
346
|
for (const mask of category.masks) {
|
|
392
|
-
// Filter out disabled masks
|
|
393
347
|
if (isMaskEnabled(this.config, mask.id)) {
|
|
394
348
|
result.push({ ...mask, category: categoryId });
|
|
395
349
|
}
|
|
@@ -400,7 +354,6 @@ class MaskLoader {
|
|
|
400
354
|
async listCategories() {
|
|
401
355
|
const catalog = await this.loadCatalog();
|
|
402
356
|
return Object.entries(catalog.categories).map(([id, cat]) => {
|
|
403
|
-
// Count only enabled masks
|
|
404
357
|
const enabledMasks = cat.masks.filter(m => isMaskEnabled(this.config, m.id));
|
|
405
358
|
return {
|
|
406
359
|
id,
|
|
@@ -411,9 +364,6 @@ class MaskLoader {
|
|
|
411
364
|
});
|
|
412
365
|
}
|
|
413
366
|
}
|
|
414
|
-
// ============================================================================
|
|
415
|
-
// Prompt Builder
|
|
416
|
-
// ============================================================================
|
|
417
367
|
function buildRichPrompt(mask) {
|
|
418
368
|
const parts = [];
|
|
419
369
|
parts.push(`You are ${mask.profile.name}.`);
|
|
@@ -464,9 +414,6 @@ function pluginLog(client, level, message) {
|
|
|
464
414
|
},
|
|
465
415
|
});
|
|
466
416
|
}
|
|
467
|
-
// ============================================================================
|
|
468
|
-
// Helper functions for tool factories
|
|
469
|
-
// ============================================================================
|
|
470
417
|
function createListMasksTool(maskLoader, activeMask) {
|
|
471
418
|
return {
|
|
472
419
|
description: 'List all available expert persona masks.',
|
|
@@ -581,7 +528,7 @@ function createMaskweaverStatusTool(maskLoader, masksDir, activeMask) {
|
|
|
581
528
|
masksCount = masks.length;
|
|
582
529
|
categoriesCount = categories.length;
|
|
583
530
|
}
|
|
584
|
-
catch (_e) {
|
|
531
|
+
catch (_e) { }
|
|
585
532
|
}
|
|
586
533
|
const active = activeMask();
|
|
587
534
|
return `Maskweaver v${VERSION}
|
|
@@ -636,42 +583,27 @@ function playCompletionSound(config) {
|
|
|
636
583
|
process.stdout.write('\u0007');
|
|
637
584
|
}
|
|
638
585
|
catch {
|
|
639
|
-
// Ignore failures - notification sound is best-effort only.
|
|
640
586
|
}
|
|
641
587
|
}
|
|
642
588
|
}
|
|
643
589
|
let state = null;
|
|
644
|
-
// ============================================================================
|
|
645
590
|
export const MaskweaverPlugin = async ({ client, directory, project, worktree, $, serverUrl }) => {
|
|
646
|
-
// ==========================================================================
|
|
647
|
-
// 1. Load Configuration (oh-my-opencode pattern)
|
|
648
|
-
// ==========================================================================
|
|
649
591
|
const pluginConfig = loadPluginConfig(directory, { client, verbose: false });
|
|
650
|
-
// Validate configuration
|
|
651
592
|
const configErrors = validateConfig(pluginConfig);
|
|
652
593
|
if (configErrors.length > 0) {
|
|
653
594
|
pluginLog(client, 'warn', `Configuration validation errors: ${configErrors.join(', ')}`);
|
|
654
595
|
}
|
|
655
596
|
const verbose = isVerboseLoggingEnabled(pluginConfig);
|
|
656
|
-
// ==========================================================================
|
|
657
|
-
// 2. Auto-install assets on first run
|
|
658
|
-
// ==========================================================================
|
|
659
597
|
const installResult = installAssets(directory);
|
|
660
|
-
// Track if this is a first-time installation
|
|
661
598
|
const isFirstInstall = installResult.installed.length > 0;
|
|
662
599
|
if (isFirstInstall) {
|
|
663
600
|
pluginLog(client, 'info', `Installed ${installResult.installed.length} files to .opencode/ (agents, masks)`);
|
|
664
|
-
// Show prominent restart message for first-time installation
|
|
665
601
|
pluginLog(client, 'warn', `⚠️ RESTART REQUIRED: Please restart OpenCode to activate all Maskweaver features (agents, masks, commands).`);
|
|
666
602
|
}
|
|
667
603
|
if (installResult.errors.length > 0) {
|
|
668
604
|
pluginLog(client, 'warn', `Asset errors: ${installResult.errors.join(', ')}`);
|
|
669
605
|
}
|
|
670
|
-
// ==========================================================================
|
|
671
|
-
// 2b. Auto-create/migrate default config files (global first, then project)
|
|
672
|
-
// ==========================================================================
|
|
673
606
|
const globalConfigDir = path.join(os.homedir(), '.config', 'opencode');
|
|
674
|
-
// Migrate/update global config with missing fields
|
|
675
607
|
const globalConfigPath = path.join(globalConfigDir, 'maskweaver.config.json');
|
|
676
608
|
if (fs.existsSync(globalConfigPath)) {
|
|
677
609
|
try {
|
|
@@ -704,25 +636,17 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
704
636
|
if (createdPluginConfig) {
|
|
705
637
|
pluginLog(client, 'info', `Created plugin config: ${path.relative(directory, createdPluginConfig)}`);
|
|
706
638
|
}
|
|
707
|
-
// ==========================================================================
|
|
708
|
-
// 2c. Generate pool agents from maskweaver.config.json (dummyHumans.pool)
|
|
709
|
-
// ==========================================================================
|
|
710
639
|
const generatedAgents = generatePoolAgents(directory);
|
|
711
640
|
if (generatedAgents.length > 0) {
|
|
712
641
|
pluginLog(client, 'info', `Generated ${generatedAgents.length} pool agent files from maskweaver.config.json: ${generatedAgents.map(p => path.basename(p)).join(', ')}`);
|
|
713
642
|
pluginLog(client, 'warn', `⚠️ RESTART REQUIRED: Please restart OpenCode to activate the new pool agent files.`);
|
|
714
643
|
}
|
|
715
|
-
// If project config was just created but pool has no agents, warn user
|
|
716
644
|
if (createdRuntimeConfig && generatedAgents.length === 0) {
|
|
717
645
|
pluginLog(client, 'warn', `⚠️ maskweaver.config.json was created. Edit it to configure your model pool, then restart OpenCode.`);
|
|
718
646
|
}
|
|
719
|
-
// ==========================================================================
|
|
720
|
-
// 3. Initialize masks
|
|
721
|
-
// ==========================================================================
|
|
722
647
|
const homeDir = os.homedir();
|
|
723
648
|
const globalMasksDir = path.join(homeDir, '.config', 'opencode', 'masks');
|
|
724
649
|
const projectMasksDir = path.join(directory, '.opencode', 'masks');
|
|
725
|
-
// Priority: project masks > global masks
|
|
726
650
|
const masksDir = fs.existsSync(projectMasksDir) ? projectMasksDir : globalMasksDir;
|
|
727
651
|
const pluginState = {
|
|
728
652
|
maskLoader: null,
|
|
@@ -732,7 +656,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
732
656
|
currentSessionID: null,
|
|
733
657
|
};
|
|
734
658
|
state = pluginState;
|
|
735
|
-
// Log plugin loaded
|
|
736
659
|
pluginLog(client, 'info', `Maskweaver plugin loaded v${VERSION}`);
|
|
737
660
|
if (fs.existsSync(masksDir)) {
|
|
738
661
|
pluginState.maskLoader = new MaskLoader(masksDir, pluginConfig);
|
|
@@ -747,9 +670,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
747
670
|
pluginState.maskLoader = null;
|
|
748
671
|
}
|
|
749
672
|
}
|
|
750
|
-
// ==========================================================================
|
|
751
|
-
// 4. Auto-activate default mask (oh-my-opencode pattern)
|
|
752
|
-
// ==========================================================================
|
|
753
673
|
const defaultMaskId = getDefaultMask(pluginConfig);
|
|
754
674
|
const autoActivate = isAutoActivateEnabled(pluginConfig);
|
|
755
675
|
if (defaultMaskId && autoActivate && pluginState.maskLoader) {
|
|
@@ -767,28 +687,17 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
767
687
|
pluginLog(client, 'warn', `Failed to auto-activate default mask: ${e}`);
|
|
768
688
|
}
|
|
769
689
|
}
|
|
770
|
-
// ==========================================================================
|
|
771
|
-
// 5. Helper functions for tool factories
|
|
772
|
-
// ==========================================================================
|
|
773
690
|
const getActiveMask = () => pluginState.activeMask;
|
|
774
691
|
const setActiveMask = (mask) => {
|
|
775
692
|
pluginState.activeMask = mask;
|
|
776
693
|
};
|
|
777
|
-
// ==========================================================================
|
|
778
|
-
// 6. Conditional tool registration (oh-my-opencode pattern)
|
|
779
|
-
// ==========================================================================
|
|
780
694
|
const isToolActive = (toolName) => isToolEnabled(pluginConfig, toolName);
|
|
781
|
-
// Helper to ensure tool arguments are compatible with opencode's expected format.
|
|
782
|
-
// opencode expects a ZodRawShape (raw object), NOT a ZodObject instance.
|
|
783
|
-
// Zod 4: schema.def.shape, Zod 3: schema._def.shape()
|
|
784
695
|
const wrapSchema = (schema) => {
|
|
785
696
|
if (!schema || typeof schema !== 'object')
|
|
786
697
|
return schema;
|
|
787
|
-
// Zod 4 — def.shape is a plain object
|
|
788
698
|
if (schema.def && typeof schema.def === 'object' && schema.type === 'object' && schema.def.shape && typeof schema.def.shape === 'object') {
|
|
789
699
|
return schema.def.shape;
|
|
790
700
|
}
|
|
791
|
-
// Zod 3 — _def.shape() returns a plain object
|
|
792
701
|
if (schema._def && typeof schema._def.shape === 'function') {
|
|
793
702
|
return schema._def.shape();
|
|
794
703
|
}
|
|
@@ -822,7 +731,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
822
731
|
tool.args = wrapSchema(tool.args);
|
|
823
732
|
tools.maskweaver_status = tool;
|
|
824
733
|
}
|
|
825
|
-
// Memory tools
|
|
826
734
|
if (isToolActive('memory_search')) {
|
|
827
735
|
const memorySearchTool = createMemorySearchTool();
|
|
828
736
|
tools.memory_search = {
|
|
@@ -855,7 +763,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
855
763
|
execute: (args) => memoryIndexerTool.execute(args, { worktree: directory }),
|
|
856
764
|
};
|
|
857
765
|
}
|
|
858
|
-
// Context tool
|
|
859
766
|
if (isToolActive('context')) {
|
|
860
767
|
const contextTool = createContextTool();
|
|
861
768
|
tools.context = {
|
|
@@ -864,7 +771,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
864
771
|
execute: (args) => contextTool.execute(args, { worktree: directory }),
|
|
865
772
|
};
|
|
866
773
|
}
|
|
867
|
-
// Retrospect tool
|
|
868
774
|
if (isToolActive('retrospect')) {
|
|
869
775
|
const retrospectTool = createRetrospectTool();
|
|
870
776
|
tools.retrospect = {
|
|
@@ -873,7 +779,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
873
779
|
execute: (args) => retrospectTool.execute(args, { worktree: directory }),
|
|
874
780
|
};
|
|
875
781
|
}
|
|
876
|
-
// Mask save tool
|
|
877
782
|
if (isToolActive('mask_save')) {
|
|
878
783
|
const maskSaveTool = createMaskSaveTool();
|
|
879
784
|
tools.mask_save = {
|
|
@@ -882,7 +787,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
882
787
|
execute: (args) => maskSaveTool.execute(args, { worktree: directory }),
|
|
883
788
|
};
|
|
884
789
|
}
|
|
885
|
-
// Squad tool (multi-agent collaboration)
|
|
886
790
|
if (isToolActive('squad')) {
|
|
887
791
|
const squadTool = createSquadTool();
|
|
888
792
|
tools.squad = {
|
|
@@ -891,7 +795,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
891
795
|
execute: (args) => squadTool.execute(args, { worktree: directory }),
|
|
892
796
|
};
|
|
893
797
|
}
|
|
894
|
-
// Weave tool (phase-driven development workflow)
|
|
895
798
|
if (isToolActive('weave')) {
|
|
896
799
|
const weaveTool = createWeaveTool();
|
|
897
800
|
tools.weave = {
|
|
@@ -900,7 +803,6 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
900
803
|
execute: (args) => weaveTool.execute(args, { worktree: directory }),
|
|
901
804
|
};
|
|
902
805
|
}
|
|
903
|
-
// Slashcommand tool (handles /weave etc. on first run without restart)
|
|
904
806
|
if (isToolActive('slashcommand')) {
|
|
905
807
|
const slashcommandTool = createSlashcommandTool(getAssetsDir());
|
|
906
808
|
tools.slashcommand = {
|
|
@@ -909,22 +811,7 @@ export const MaskweaverPlugin = async ({ client, directory, project, worktree, $
|
|
|
909
811
|
execute: (args) => slashcommandTool.execute(args, { worktree: directory }),
|
|
910
812
|
};
|
|
911
813
|
}
|
|
912
|
-
// ==========================================================================
|
|
913
|
-
// 8. Agents are loaded from .opencode/agents/*.md files by OpenCode's
|
|
914
|
-
// filesystem-based agent loader (see config/agent.ts:110-140).
|
|
915
|
-
// installAssets() in step 2 copies agent .md files so they are picked up.
|
|
916
|
-
// The 'agent' property in Hooks is NOT consumed by OpenCode — confirmed
|
|
917
|
-
// by source analysis (packages/opencode/src/plugin/index.ts:92-103).
|
|
918
|
-
// ==========================================================================
|
|
919
|
-
// ==========================================================================
|
|
920
|
-
// 9. Return plugin hooks (official OpenCode Hooks interface only)
|
|
921
|
-
// Note: Agents are registered via .opencode/agents/*.md files (installed by
|
|
922
|
-
// installAssets()), NOT via the plugin return. The Hooks type does not
|
|
923
|
-
// include 'agent' — OpenCode loads agents from the filesystem exclusively.
|
|
924
|
-
// ==========================================================================
|
|
925
814
|
return {
|
|
926
|
-
// Agent registration handled via .opencode/agents/*.md files (see installAssets)
|
|
927
|
-
// System prompt transform - inject active mask
|
|
928
815
|
'experimental.chat.system.transform': async (_input, output) => {
|
|
929
816
|
if (state?.activeMask) {
|
|
930
817
|
const maskPrompt = `<ACTIVE_PERSONA>
|
|
@@ -935,11 +822,8 @@ ${buildRichPrompt(state.activeMask)}
|
|
|
935
822
|
(output.system ||= []).push(maskPrompt);
|
|
936
823
|
}
|
|
937
824
|
},
|
|
938
|
-
// Conditional tools
|
|
939
825
|
tool: tools,
|
|
940
|
-
// Event hooks (oh-my-opencode pattern)
|
|
941
826
|
event: async ({ event }) => {
|
|
942
|
-
// Session created - log available masks
|
|
943
827
|
if (event.type === 'session.created') {
|
|
944
828
|
pluginState.currentSessionID = getSessionId(event);
|
|
945
829
|
if (pluginState.maskLoader && verbose) {
|
|
@@ -949,11 +833,9 @@ ${buildRichPrompt(state.activeMask)}
|
|
|
949
833
|
pluginLog(client, 'info', `Session started - ${masks.length} masks available across ${categories.length} categories`);
|
|
950
834
|
}
|
|
951
835
|
catch (_e) {
|
|
952
|
-
// Ignore errors
|
|
953
836
|
}
|
|
954
837
|
}
|
|
955
838
|
}
|
|
956
|
-
// Session idle - generation completed
|
|
957
839
|
if (event.type === 'session.idle') {
|
|
958
840
|
const idleSessionID = getSessionId(event);
|
|
959
841
|
const isCurrentSession = !idleSessionID ||
|
|
@@ -963,7 +845,6 @@ ${buildRichPrompt(state.activeMask)}
|
|
|
963
845
|
playCompletionSound(pluginState.config);
|
|
964
846
|
}
|
|
965
847
|
}
|
|
966
|
-
// Session deleted - cleanup
|
|
967
848
|
if (event.type === 'session.deleted') {
|
|
968
849
|
const deletedSessionID = getSessionId(event);
|
|
969
850
|
if (!deletedSessionID || deletedSessionID === pluginState.currentSessionID) {
|
|
@@ -978,11 +859,7 @@ ${buildRichPrompt(state.activeMask)}
|
|
|
978
859
|
}
|
|
979
860
|
}
|
|
980
861
|
},
|
|
981
|
-
// Config hook - allows plugins to modify opencode configuration
|
|
982
862
|
config: async (config) => {
|
|
983
|
-
// opencode discovers slash commands from config.command and command files.
|
|
984
|
-
// installAssets() writes command files for subsequent starts, while this
|
|
985
|
-
// hook makes the direct /build command visible on the current plugin load.
|
|
986
863
|
config.command ||= {};
|
|
987
864
|
config.command.build ||= {
|
|
988
865
|
description: 'Run or manage the Maskweaver autonomous build loop',
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command Registry for Weave Tool
|
|
3
|
-
*
|
|
4
|
-
* Loads commands.json and provides alias resolution, deprecation warnings,
|
|
5
|
-
* and dynamic description/help generation.
|
|
6
|
-
*/
|
|
7
1
|
export interface CommandArg {
|
|
8
2
|
name: string;
|
|
9
3
|
type: 'string' | 'boolean' | 'number' | 'enum';
|
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command Registry for Weave Tool
|
|
3
|
-
*
|
|
4
|
-
* Loads commands.json and provides alias resolution, deprecation warnings,
|
|
5
|
-
* and dynamic description/help generation.
|
|
6
|
-
*/
|
|
7
1
|
import * as fs from 'node:fs';
|
|
8
2
|
import * as path from 'node:path';
|
|
9
|
-
// ============================================================================
|
|
10
|
-
// Inline Default (fail-safe fallback when commands.json cannot be loaded)
|
|
11
|
-
// ============================================================================
|
|
12
3
|
const INLINE_DEFAULT = {
|
|
13
4
|
schemaVersion: '1.0',
|
|
14
5
|
lastUpdated: '2026-04-29',
|
|
@@ -246,13 +237,9 @@ const INLINE_DEFAULT = {
|
|
|
246
237
|
},
|
|
247
238
|
],
|
|
248
239
|
};
|
|
249
|
-
// ============================================================================
|
|
250
|
-
// Resolution
|
|
251
|
-
// ============================================================================
|
|
252
240
|
let _cached = null;
|
|
253
241
|
let _cachePath = null;
|
|
254
242
|
function resolveCommandsJsonPath() {
|
|
255
|
-
// Try relative to dist first, then src (for dev)
|
|
256
243
|
const candidates = [
|
|
257
244
|
path.join(process.cwd(), 'assets', 'commands', 'meta', 'commands.json'),
|
|
258
245
|
path.join(process.cwd(), 'dist', 'assets', 'commands', 'meta', 'commands.json'),
|
|
@@ -279,7 +266,6 @@ export function loadCommandsJson() {
|
|
|
279
266
|
return parsed;
|
|
280
267
|
}
|
|
281
268
|
catch {
|
|
282
|
-
// fall through to inline default
|
|
283
269
|
}
|
|
284
270
|
}
|
|
285
271
|
_cached = INLINE_DEFAULT;
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Context Management Tool
|
|
3
|
-
*
|
|
4
|
-
* 피처 기반 작업 컨텍스트 관리 도구
|
|
5
|
-
*
|
|
6
|
-
* 설계 원칙:
|
|
7
|
-
* - 의도를 드러내는 코드 (Intention-Revealing)
|
|
8
|
-
* - 명확한 에러 메시지
|
|
9
|
-
* - 일관된 JSON 응답 형식
|
|
10
|
-
*
|
|
11
|
-
* @author Martin Fowler's Dummy Human
|
|
12
|
-
*/
|
|
13
1
|
import { z } from "zod";
|
|
14
2
|
import type { ToolFactory } from '../types.js';
|
|
15
3
|
export declare const contextSchema: z.ZodObject<{
|
|
@@ -1,20 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Context Management Tool
|
|
3
|
-
*
|
|
4
|
-
* 피처 기반 작업 컨텍스트 관리 도구
|
|
5
|
-
*
|
|
6
|
-
* 설계 원칙:
|
|
7
|
-
* - 의도를 드러내는 코드 (Intention-Revealing)
|
|
8
|
-
* - 명확한 에러 메시지
|
|
9
|
-
* - 일관된 JSON 응답 형식
|
|
10
|
-
*
|
|
11
|
-
* @author Martin Fowler's Dummy Human
|
|
12
|
-
*/
|
|
13
1
|
import { z } from "zod";
|
|
14
2
|
import * as core from '../../context/index.js';
|
|
15
|
-
// ============================================================================
|
|
16
|
-
// Schema 정의
|
|
17
|
-
// ============================================================================
|
|
18
3
|
export const contextSchema = z.object({
|
|
19
4
|
action: z.enum([
|
|
20
5
|
"start", "switch", "status", "done",
|
|
@@ -38,9 +23,6 @@ function successResponse(action, message, data) {
|
|
|
38
23
|
function errorResponse(action, message) {
|
|
39
24
|
return createResponse(false, action, message);
|
|
40
25
|
}
|
|
41
|
-
// ============================================================================
|
|
42
|
-
// Tool Factory
|
|
43
|
-
// ============================================================================
|
|
44
26
|
export function createContextTool() {
|
|
45
27
|
return {
|
|
46
28
|
description: `작업 컨텍스트를 관리합니다.
|
|
@@ -91,14 +73,6 @@ export function createContextTool() {
|
|
|
91
73
|
}
|
|
92
74
|
};
|
|
93
75
|
}
|
|
94
|
-
// ============================================================================
|
|
95
|
-
// 액션 핸들러
|
|
96
|
-
// ============================================================================
|
|
97
|
-
/**
|
|
98
|
-
* 새 피처 시작
|
|
99
|
-
*
|
|
100
|
-
* 피처를 생성하고 즉시 활성화합니다.
|
|
101
|
-
*/
|
|
102
76
|
async function handleStart(basePath, name, goal) {
|
|
103
77
|
if (!name || name.trim().length === 0) {
|
|
104
78
|
return errorResponse("start", "피처 이름(name)을 입력해주세요. 예: name=\"login-oauth\"");
|
|
@@ -106,12 +80,10 @@ async function handleStart(basePath, name, goal) {
|
|
|
106
80
|
if (!goal || goal.trim().length === 0) {
|
|
107
81
|
return errorResponse("start", "피처 목표(goal)를 입력해주세요. 예: goal=\"OAuth 로그인 구현\"");
|
|
108
82
|
}
|
|
109
|
-
// 피처 생성
|
|
110
83
|
const createResult = await core.createFeature(basePath, name, goal);
|
|
111
84
|
if (!createResult.success || !createResult.data) {
|
|
112
85
|
return errorResponse("start", createResult.error || "피처 생성에 실패했습니다");
|
|
113
86
|
}
|
|
114
|
-
// 생성된 피처를 활성화
|
|
115
87
|
const switchResult = await core.switchFeature(basePath, createResult.data.id);
|
|
116
88
|
if (!switchResult.success) {
|
|
117
89
|
return errorResponse("start", switchResult.error || "피처 활성화에 실패했습니다");
|
|
@@ -123,16 +95,10 @@ async function handleStart(basePath, name, goal) {
|
|
|
123
95
|
status: "active"
|
|
124
96
|
});
|
|
125
97
|
}
|
|
126
|
-
/**
|
|
127
|
-
* 피처 전환
|
|
128
|
-
*
|
|
129
|
-
* ID 또는 이름으로 피처를 찾아 전환합니다.
|
|
130
|
-
*/
|
|
131
98
|
async function handleSwitch(basePath, id, name) {
|
|
132
99
|
if (!id && !name) {
|
|
133
100
|
return errorResponse("switch", "피처 ID(id) 또는 이름(name)을 입력해주세요");
|
|
134
101
|
}
|
|
135
|
-
// ID가 없으면 이름으로 피처 찾기
|
|
136
102
|
let featureId = id;
|
|
137
103
|
if (!featureId && name) {
|
|
138
104
|
const listResult = await core.listFeatures(basePath);
|
|
@@ -158,9 +124,6 @@ async function handleSwitch(basePath, id, name) {
|
|
|
158
124
|
files: result.data.files
|
|
159
125
|
});
|
|
160
126
|
}
|
|
161
|
-
/**
|
|
162
|
-
* 현재 상태 표시
|
|
163
|
-
*/
|
|
164
127
|
async function handleStatus(basePath) {
|
|
165
128
|
const activeResult = await core.getActiveFeature(basePath);
|
|
166
129
|
if (!activeResult.success) {
|
|
@@ -184,12 +147,8 @@ async function handleStatus(basePath) {
|
|
|
184
147
|
blockers: feature.blockers
|
|
185
148
|
});
|
|
186
149
|
}
|
|
187
|
-
/**
|
|
188
|
-
* 피처 완료
|
|
189
|
-
*/
|
|
190
150
|
async function handleDone(basePath, id) {
|
|
191
151
|
let featureId = id;
|
|
192
|
-
// ID가 없으면 현재 활성 피처 사용
|
|
193
152
|
if (!featureId) {
|
|
194
153
|
const activeResult = await core.getActiveFeature(basePath);
|
|
195
154
|
if (!activeResult.success) {
|
|
@@ -212,14 +171,10 @@ async function handleDone(basePath, id) {
|
|
|
212
171
|
totalFiles: result.data.files.length
|
|
213
172
|
});
|
|
214
173
|
}
|
|
215
|
-
/**
|
|
216
|
-
* 파일 추가
|
|
217
|
-
*/
|
|
218
174
|
async function handleAdd(basePath, file) {
|
|
219
175
|
if (!file || file.trim().length === 0) {
|
|
220
176
|
return errorResponse("add", "추가할 파일 경로(file)를 입력해주세요. 예: file=\"src/auth/oauth.ts\"");
|
|
221
177
|
}
|
|
222
|
-
// 활성 피처 확인
|
|
223
178
|
const activeResult = await core.getActiveFeature(basePath);
|
|
224
179
|
if (!activeResult.success) {
|
|
225
180
|
return errorResponse("add", activeResult.error || "활성 피처 조회에 실패했습니다");
|
|
@@ -237,14 +192,10 @@ async function handleAdd(basePath, file) {
|
|
|
237
192
|
fileCount: result.data.files.length
|
|
238
193
|
});
|
|
239
194
|
}
|
|
240
|
-
/**
|
|
241
|
-
* 파일 제거
|
|
242
|
-
*/
|
|
243
195
|
async function handleDrop(basePath, file) {
|
|
244
196
|
if (!file || file.trim().length === 0) {
|
|
245
197
|
return errorResponse("drop", "제거할 파일 경로(file)를 입력해주세요. 예: file=\"src/old-file.ts\"");
|
|
246
198
|
}
|
|
247
|
-
// 활성 피처 확인
|
|
248
199
|
const activeResult = await core.getActiveFeature(basePath);
|
|
249
200
|
if (!activeResult.success) {
|
|
250
201
|
return errorResponse("drop", activeResult.error || "활성 피처 조회에 실패했습니다");
|
|
@@ -262,14 +213,10 @@ async function handleDrop(basePath, file) {
|
|
|
262
213
|
fileCount: result.data.files.length
|
|
263
214
|
});
|
|
264
215
|
}
|
|
265
|
-
/**
|
|
266
|
-
* 목표 변경
|
|
267
|
-
*/
|
|
268
216
|
async function handleGoal(basePath, goal) {
|
|
269
217
|
if (!goal || goal.trim().length === 0) {
|
|
270
218
|
return errorResponse("goal", "새 목표(goal)를 입력해주세요. 예: goal=\"OAuth 로그인 + 소셜 연동\"");
|
|
271
219
|
}
|
|
272
|
-
// 활성 피처 확인
|
|
273
220
|
const activeResult = await core.getActiveFeature(basePath);
|
|
274
221
|
if (!activeResult.success) {
|
|
275
222
|
return errorResponse("goal", activeResult.error || "활성 피처 조회에 실패했습니다");
|
|
@@ -287,9 +234,6 @@ async function handleGoal(basePath, goal) {
|
|
|
287
234
|
newGoal: result.data.goal
|
|
288
235
|
});
|
|
289
236
|
}
|
|
290
|
-
/**
|
|
291
|
-
* 모든 피처 목록
|
|
292
|
-
*/
|
|
293
237
|
async function handleList(basePath) {
|
|
294
238
|
const listResult = await core.listFeatures(basePath);
|
|
295
239
|
if (!listResult.success || !listResult.data) {
|
|
@@ -302,7 +246,6 @@ async function handleList(basePath) {
|
|
|
302
246
|
features: []
|
|
303
247
|
});
|
|
304
248
|
}
|
|
305
|
-
// 활성 피처 확인
|
|
306
249
|
const projectResult = await core.getProjectContext(basePath);
|
|
307
250
|
const activeId = projectResult.success && projectResult.data
|
|
308
251
|
? projectResult.data.activeFeatureId
|
|
@@ -316,7 +259,6 @@ async function handleList(basePath) {
|
|
|
316
259
|
fileCount: f.files.length,
|
|
317
260
|
updatedAt: f.updatedAt
|
|
318
261
|
}));
|
|
319
|
-
// 상태별 카운트
|
|
320
262
|
const counts = {
|
|
321
263
|
active: features.filter(f => f.status === "active").length,
|
|
322
264
|
paused: features.filter(f => f.status === "paused").length,
|