@soleri/forge 5.2.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scaffolder.js +166 -3
- package/dist/scaffolder.js.map +1 -1
- package/dist/skills/brain-debrief.md +186 -0
- package/dist/skills/brainstorming.md +170 -0
- package/dist/skills/code-patrol.md +176 -0
- package/dist/skills/context-resume.md +143 -0
- package/dist/skills/executing-plans.md +201 -0
- package/dist/skills/fix-and-learn.md +164 -0
- package/dist/skills/health-check.md +225 -0
- package/dist/skills/knowledge-harvest.md +178 -0
- package/dist/skills/onboard-me.md +197 -0
- package/dist/skills/retrospective.md +189 -0
- package/dist/skills/second-opinion.md +142 -0
- package/dist/skills/systematic-debugging.md +230 -0
- package/dist/skills/test-driven-development.md +266 -0
- package/dist/skills/vault-capture.md +154 -0
- package/dist/skills/vault-navigator.md +129 -0
- package/dist/skills/verification-before-completion.md +170 -0
- package/dist/skills/writing-plans.md +207 -0
- package/dist/templates/claude-md-template.js +90 -1
- package/dist/templates/claude-md-template.js.map +1 -1
- package/dist/templates/domain-facade.d.ts +4 -0
- package/dist/templates/domain-facade.js +4 -0
- package/dist/templates/domain-facade.js.map +1 -1
- package/dist/templates/entry-point.js +32 -0
- package/dist/templates/entry-point.js.map +1 -1
- package/dist/templates/readme.js +38 -0
- package/dist/templates/readme.js.map +1 -1
- package/dist/templates/setup-script.js +52 -1
- package/dist/templates/setup-script.js.map +1 -1
- package/dist/templates/skills.d.ts +16 -0
- package/dist/templates/skills.js +73 -0
- package/dist/templates/skills.js.map +1 -0
- package/dist/templates/test-facades.js +173 -3
- package/dist/templates/test-facades.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/scaffolder.test.ts +115 -2
- package/src/scaffolder.ts +171 -3
- package/src/skills/brain-debrief.md +186 -0
- package/src/skills/brainstorming.md +170 -0
- package/src/skills/code-patrol.md +176 -0
- package/src/skills/context-resume.md +143 -0
- package/src/skills/executing-plans.md +201 -0
- package/src/skills/fix-and-learn.md +164 -0
- package/src/skills/health-check.md +225 -0
- package/src/skills/knowledge-harvest.md +178 -0
- package/src/skills/onboard-me.md +197 -0
- package/src/skills/retrospective.md +189 -0
- package/src/skills/second-opinion.md +142 -0
- package/src/skills/systematic-debugging.md +230 -0
- package/src/skills/test-driven-development.md +266 -0
- package/src/skills/vault-capture.md +154 -0
- package/src/skills/vault-navigator.md +129 -0
- package/src/skills/verification-before-completion.md +170 -0
- package/src/skills/writing-plans.md +207 -0
- package/src/templates/claude-md-template.ts +181 -0
- package/src/templates/domain-facade.ts +4 -0
- package/src/templates/entry-point.ts +32 -0
- package/src/templates/readme.ts +38 -0
- package/src/templates/setup-script.ts +54 -1
- package/src/templates/skills.ts +82 -0
- package/src/templates/test-facades.ts +173 -3
|
@@ -101,7 +101,60 @@ else
|
|
|
101
101
|
fi
|
|
102
102
|
fi
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
# Install skills to ~/.claude/commands/
|
|
105
|
+
SKILLS_DIR="$AGENT_DIR/skills"
|
|
106
|
+
COMMANDS_DIR="$HOME/.claude/commands"
|
|
107
|
+
|
|
108
|
+
if [ -d "$SKILLS_DIR" ]; then
|
|
109
|
+
echo ""
|
|
110
|
+
echo "Installing skills..."
|
|
111
|
+
mkdir -p "$COMMANDS_DIR"
|
|
112
|
+
skill_installed=0
|
|
113
|
+
skill_skipped=0
|
|
114
|
+
for skill_dir in "$SKILLS_DIR"/*/; do
|
|
115
|
+
[ -d "$skill_dir" ] || continue
|
|
116
|
+
skill_file="$skill_dir/SKILL.md"
|
|
117
|
+
[ -f "$skill_file" ] || continue
|
|
118
|
+
skill_name="$(basename "$skill_dir")"
|
|
119
|
+
dest="$COMMANDS_DIR/$skill_name.md"
|
|
120
|
+
if [ -f "$dest" ]; then
|
|
121
|
+
skill_skipped=$((skill_skipped + 1))
|
|
122
|
+
else
|
|
123
|
+
cp "$skill_file" "$dest"
|
|
124
|
+
skill_installed=$((skill_installed + 1))
|
|
125
|
+
fi
|
|
126
|
+
done
|
|
127
|
+
echo "[ok] Skills: $skill_installed installed, $skill_skipped already present"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
${
|
|
131
|
+
config.hookPacks?.length
|
|
132
|
+
? `# Install hook packs to global ~/.claude/
|
|
133
|
+
AGENT_CLAUDE_DIR="$AGENT_DIR/.claude"
|
|
134
|
+
GLOBAL_CLAUDE_DIR="$HOME/.claude"
|
|
135
|
+
|
|
136
|
+
if [ -d "$AGENT_CLAUDE_DIR" ]; then
|
|
137
|
+
echo ""
|
|
138
|
+
echo "Installing hook packs..."
|
|
139
|
+
mkdir -p "$GLOBAL_CLAUDE_DIR"
|
|
140
|
+
installed=0
|
|
141
|
+
skipped=0
|
|
142
|
+
for hook_file in "$AGENT_CLAUDE_DIR"/hookify.*.local.md; do
|
|
143
|
+
[ -f "$hook_file" ] || continue
|
|
144
|
+
dest="$GLOBAL_CLAUDE_DIR/$(basename "$hook_file")"
|
|
145
|
+
if [ -f "$dest" ]; then
|
|
146
|
+
skipped=$((skipped + 1))
|
|
147
|
+
else
|
|
148
|
+
cp "$hook_file" "$dest"
|
|
149
|
+
installed=$((installed + 1))
|
|
150
|
+
fi
|
|
151
|
+
done
|
|
152
|
+
echo "[ok] Hooks: $installed installed, $skipped already present"
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
`
|
|
156
|
+
: ''
|
|
157
|
+
}echo ""
|
|
105
158
|
echo "=== Setup Complete ==="
|
|
106
159
|
echo ""
|
|
107
160
|
echo "Next:"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import type { AgentConfig } from '../types.js';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const SKILLS_DIR = join(__dirname, '..', 'skills');
|
|
8
|
+
|
|
9
|
+
/** Skills that use YOUR_AGENT_core placeholder and need agent-specific substitution. */
|
|
10
|
+
const AGENT_SPECIFIC_SKILLS = new Set([
|
|
11
|
+
'brain-debrief',
|
|
12
|
+
'brainstorming',
|
|
13
|
+
'code-patrol',
|
|
14
|
+
'context-resume',
|
|
15
|
+
'executing-plans',
|
|
16
|
+
'fix-and-learn',
|
|
17
|
+
'health-check',
|
|
18
|
+
'knowledge-harvest',
|
|
19
|
+
'onboard-me',
|
|
20
|
+
'retrospective',
|
|
21
|
+
'second-opinion',
|
|
22
|
+
'systematic-debugging',
|
|
23
|
+
'test-driven-development',
|
|
24
|
+
'vault-capture',
|
|
25
|
+
'vault-navigator',
|
|
26
|
+
'verification-before-completion',
|
|
27
|
+
'writing-plans',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generate skill files for the scaffolded agent.
|
|
32
|
+
* Returns [relativePath, content] tuples for each skill.
|
|
33
|
+
*
|
|
34
|
+
* - Superpowers-adapted skills (MIT): copied as-is
|
|
35
|
+
* - Engine-adapted skills: YOUR_AGENT_core → {config.id}_core
|
|
36
|
+
*/
|
|
37
|
+
export function generateSkills(config: AgentConfig): Array<[string, string]> {
|
|
38
|
+
const files: Array<[string, string]> = [];
|
|
39
|
+
let skillFiles: string[];
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
skillFiles = readdirSync(SKILLS_DIR).filter((f) => f.endsWith('.md'));
|
|
43
|
+
} catch {
|
|
44
|
+
return files;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const file of skillFiles) {
|
|
48
|
+
const skillName = file.replace('.md', '');
|
|
49
|
+
let content = readFileSync(join(SKILLS_DIR, file), 'utf-8');
|
|
50
|
+
|
|
51
|
+
if (AGENT_SPECIFIC_SKILLS.has(skillName)) {
|
|
52
|
+
content = content.replace(/YOUR_AGENT_core/g, `${config.id}_core`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
files.push([`skills/${skillName}/SKILL.md`, content]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return files;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* List all bundled skill names with their descriptions (from YAML frontmatter).
|
|
63
|
+
*/
|
|
64
|
+
export function listSkillDescriptions(): Array<{ name: string; description: string }> {
|
|
65
|
+
let skillFiles: string[];
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
skillFiles = readdirSync(SKILLS_DIR).filter((f) => f.endsWith('.md'));
|
|
69
|
+
} catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return skillFiles.map((file) => {
|
|
74
|
+
const content = readFileSync(join(SKILLS_DIR, file), 'utf-8');
|
|
75
|
+
const nameMatch = content.match(/^name:\s*(.+)$/m);
|
|
76
|
+
const descMatch = content.match(/^description:\s*"?(.+?)"?\s*$/m);
|
|
77
|
+
return {
|
|
78
|
+
name: nameMatch?.[1]?.trim() ?? file.replace('.md', ''),
|
|
79
|
+
description: descMatch?.[1]?.trim() ?? '',
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
@@ -126,6 +126,29 @@ ${domainDescribes}
|
|
|
126
126
|
if (stats.totalEntries === 0) {
|
|
127
127
|
recommendations.push('Vault is empty');
|
|
128
128
|
}
|
|
129
|
+
// Check hook status
|
|
130
|
+
const { readdirSync } = await import('node:fs');
|
|
131
|
+
const agentClaudeDir = joinPath(__dirname, '..', '.claude');
|
|
132
|
+
const globalClaudeDir = joinPath(homedir(), '.claude');
|
|
133
|
+
const hookStatus = { agent: [] as string[], global: [] as string[], missing: [] as string[] };
|
|
134
|
+
if (exists(agentClaudeDir)) {
|
|
135
|
+
try {
|
|
136
|
+
const agentHooks = readdirSync(agentClaudeDir)
|
|
137
|
+
.filter((f: string) => f.startsWith('hookify.') && f.endsWith('.local.md'))
|
|
138
|
+
.map((f: string) => f.replace('hookify.', '').replace('.local.md', ''));
|
|
139
|
+
hookStatus.agent = agentHooks;
|
|
140
|
+
for (const hook of agentHooks) {
|
|
141
|
+
if (exists(joinPath(globalClaudeDir, \`hookify.\${hook}.local.md\`))) {
|
|
142
|
+
hookStatus.global.push(hook);
|
|
143
|
+
} else {
|
|
144
|
+
hookStatus.missing.push(hook);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch { /* ignore */ }
|
|
148
|
+
}
|
|
149
|
+
if (hookStatus.missing.length > 0) {
|
|
150
|
+
recommendations.push(\`\${hookStatus.missing.length} hook(s) not installed globally — run scripts/setup.sh\`);
|
|
151
|
+
}
|
|
129
152
|
if (recommendations.length === 0) {
|
|
130
153
|
recommendations.push('${config.name} is fully set up and ready!');
|
|
131
154
|
}
|
|
@@ -136,6 +159,7 @@ ${domainDescribes}
|
|
|
136
159
|
global: { exists: exists(globalClaudeMd), has_agent_section: hasAgentMarker(globalClaudeMd) },
|
|
137
160
|
},
|
|
138
161
|
vault: { entries: stats.totalEntries, domains: Object.keys(stats.byDomain) },
|
|
162
|
+
hooks: hookStatus,
|
|
139
163
|
recommendations,
|
|
140
164
|
};
|
|
141
165
|
},
|
|
@@ -172,6 +196,10 @@ ${domainDescribes}
|
|
|
172
196
|
expect(opNames).toContain('brain_archive_sessions');
|
|
173
197
|
expect(opNames).toContain('brain_promote_proposals');
|
|
174
198
|
expect(opNames).toContain('brain_lifecycle');
|
|
199
|
+
// Enhanced brain ops (3)
|
|
200
|
+
expect(opNames).toContain('brain_feedback');
|
|
201
|
+
expect(opNames).toContain('brain_feedback_stats');
|
|
202
|
+
expect(opNames).toContain('brain_reset_extracted');
|
|
175
203
|
// Agent-specific ops (5)
|
|
176
204
|
expect(opNames).toContain('health');
|
|
177
205
|
expect(opNames).toContain('identity');
|
|
@@ -187,8 +215,122 @@ ${domainDescribes}
|
|
|
187
215
|
expect(opNames).toContain('route_intent');
|
|
188
216
|
expect(opNames).toContain('morph');
|
|
189
217
|
expect(opNames).toContain('get_behavior_rules');
|
|
190
|
-
//
|
|
191
|
-
expect(
|
|
218
|
+
// Cognee ops (5)
|
|
219
|
+
expect(opNames).toContain('cognee_status');
|
|
220
|
+
expect(opNames).toContain('cognee_search');
|
|
221
|
+
expect(opNames).toContain('cognee_add');
|
|
222
|
+
expect(opNames).toContain('cognee_cognify');
|
|
223
|
+
expect(opNames).toContain('cognee_config');
|
|
224
|
+
// LLM ops (2)
|
|
225
|
+
expect(opNames).toContain('llm_rotate');
|
|
226
|
+
expect(opNames).toContain('llm_call');
|
|
227
|
+
// Governance ops (5)
|
|
228
|
+
expect(opNames).toContain('governance_policy');
|
|
229
|
+
expect(opNames).toContain('governance_proposals');
|
|
230
|
+
expect(opNames).toContain('governance_stats');
|
|
231
|
+
expect(opNames).toContain('governance_expire');
|
|
232
|
+
expect(opNames).toContain('governance_dashboard');
|
|
233
|
+
// Planning Extra ops (9)
|
|
234
|
+
expect(opNames).toContain('plan_iterate');
|
|
235
|
+
expect(opNames).toContain('plan_split');
|
|
236
|
+
expect(opNames).toContain('plan_reconcile');
|
|
237
|
+
expect(opNames).toContain('plan_complete_lifecycle');
|
|
238
|
+
expect(opNames).toContain('plan_dispatch');
|
|
239
|
+
expect(opNames).toContain('plan_review');
|
|
240
|
+
expect(opNames).toContain('plan_archive');
|
|
241
|
+
expect(opNames).toContain('plan_list_tasks');
|
|
242
|
+
expect(opNames).toContain('plan_stats');
|
|
243
|
+
// Memory Extra ops (8)
|
|
244
|
+
expect(opNames).toContain('memory_delete');
|
|
245
|
+
expect(opNames).toContain('memory_stats');
|
|
246
|
+
expect(opNames).toContain('memory_export');
|
|
247
|
+
expect(opNames).toContain('memory_import');
|
|
248
|
+
expect(opNames).toContain('memory_prune');
|
|
249
|
+
expect(opNames).toContain('memory_deduplicate');
|
|
250
|
+
expect(opNames).toContain('memory_topics');
|
|
251
|
+
expect(opNames).toContain('memory_by_project');
|
|
252
|
+
// Vault Extra ops (12)
|
|
253
|
+
expect(opNames).toContain('vault_get');
|
|
254
|
+
expect(opNames).toContain('vault_update');
|
|
255
|
+
expect(opNames).toContain('vault_remove');
|
|
256
|
+
expect(opNames).toContain('vault_bulk_add');
|
|
257
|
+
expect(opNames).toContain('vault_bulk_remove');
|
|
258
|
+
expect(opNames).toContain('vault_tags');
|
|
259
|
+
expect(opNames).toContain('vault_domains');
|
|
260
|
+
expect(opNames).toContain('vault_recent');
|
|
261
|
+
expect(opNames).toContain('vault_import');
|
|
262
|
+
expect(opNames).toContain('vault_seed');
|
|
263
|
+
expect(opNames).toContain('vault_backup');
|
|
264
|
+
expect(opNames).toContain('vault_age_report');
|
|
265
|
+
// Admin ops (8)
|
|
266
|
+
expect(opNames).toContain('admin_health');
|
|
267
|
+
expect(opNames).toContain('admin_tool_list');
|
|
268
|
+
expect(opNames).toContain('admin_config');
|
|
269
|
+
expect(opNames).toContain('admin_vault_size');
|
|
270
|
+
expect(opNames).toContain('admin_uptime');
|
|
271
|
+
expect(opNames).toContain('admin_version');
|
|
272
|
+
expect(opNames).toContain('admin_reset_cache');
|
|
273
|
+
expect(opNames).toContain('admin_diagnostic');
|
|
274
|
+
// Loop ops (7)
|
|
275
|
+
expect(opNames).toContain('loop_start');
|
|
276
|
+
expect(opNames).toContain('loop_iterate');
|
|
277
|
+
expect(opNames).toContain('loop_status');
|
|
278
|
+
expect(opNames).toContain('loop_cancel');
|
|
279
|
+
expect(opNames).toContain('loop_history');
|
|
280
|
+
expect(opNames).toContain('loop_is_active');
|
|
281
|
+
expect(opNames).toContain('loop_complete');
|
|
282
|
+
// Orchestrate ops (5)
|
|
283
|
+
expect(opNames).toContain('orchestrate_plan');
|
|
284
|
+
expect(opNames).toContain('orchestrate_execute');
|
|
285
|
+
expect(opNames).toContain('orchestrate_complete');
|
|
286
|
+
expect(opNames).toContain('orchestrate_status');
|
|
287
|
+
expect(opNames).toContain('orchestrate_quick_capture');
|
|
288
|
+
// Capture ops (4)
|
|
289
|
+
expect(opNames).toContain('capture_knowledge');
|
|
290
|
+
expect(opNames).toContain('capture_quick');
|
|
291
|
+
expect(opNames).toContain('search_intelligent');
|
|
292
|
+
expect(opNames).toContain('search_feedback');
|
|
293
|
+
// Grading ops (5)
|
|
294
|
+
expect(opNames).toContain('plan_grade');
|
|
295
|
+
expect(opNames).toContain('plan_check_history');
|
|
296
|
+
expect(opNames).toContain('plan_latest_check');
|
|
297
|
+
expect(opNames).toContain('plan_meets_grade');
|
|
298
|
+
expect(opNames).toContain('plan_auto_improve');
|
|
299
|
+
// Admin Extra ops (10)
|
|
300
|
+
expect(opNames).toContain('admin_telemetry');
|
|
301
|
+
expect(opNames).toContain('admin_telemetry_recent');
|
|
302
|
+
expect(opNames).toContain('admin_telemetry_reset');
|
|
303
|
+
expect(opNames).toContain('admin_permissions');
|
|
304
|
+
expect(opNames).toContain('admin_vault_analytics');
|
|
305
|
+
expect(opNames).toContain('admin_search_insights');
|
|
306
|
+
expect(opNames).toContain('admin_module_status');
|
|
307
|
+
expect(opNames).toContain('admin_env');
|
|
308
|
+
expect(opNames).toContain('admin_gc');
|
|
309
|
+
expect(opNames).toContain('admin_export_config');
|
|
310
|
+
// Curator Extra ops (4)
|
|
311
|
+
expect(opNames).toContain('curator_entry_history');
|
|
312
|
+
expect(opNames).toContain('curator_record_snapshot');
|
|
313
|
+
expect(opNames).toContain('curator_queue_stats');
|
|
314
|
+
expect(opNames).toContain('curator_enrich');
|
|
315
|
+
// Project ops (12)
|
|
316
|
+
expect(opNames).toContain('project_get');
|
|
317
|
+
expect(opNames).toContain('project_list');
|
|
318
|
+
expect(opNames).toContain('project_unregister');
|
|
319
|
+
expect(opNames).toContain('project_get_rules');
|
|
320
|
+
expect(opNames).toContain('project_list_rules');
|
|
321
|
+
expect(opNames).toContain('project_add_rule');
|
|
322
|
+
expect(opNames).toContain('project_remove_rule');
|
|
323
|
+
expect(opNames).toContain('project_link');
|
|
324
|
+
expect(opNames).toContain('project_unlink');
|
|
325
|
+
expect(opNames).toContain('project_get_links');
|
|
326
|
+
expect(opNames).toContain('project_linked_projects');
|
|
327
|
+
expect(opNames).toContain('project_touch');
|
|
328
|
+
// Cross-project memory ops (3)
|
|
329
|
+
expect(opNames).toContain('memory_promote_to_global');
|
|
330
|
+
expect(opNames).toContain('memory_configure');
|
|
331
|
+
expect(opNames).toContain('memory_cross_project_search');
|
|
332
|
+
// Total: 152 (147 core + 5 agent-specific)
|
|
333
|
+
expect(facade.ops.length).toBe(152);
|
|
192
334
|
});
|
|
193
335
|
|
|
194
336
|
it('search should query across all domains with ranked results', async () => {
|
|
@@ -280,7 +422,9 @@ ${domainDescribes}
|
|
|
280
422
|
const setupOp = facade.ops.find((o) => o.name === 'setup')!;
|
|
281
423
|
const result = (await setupOp.handler({ projectPath: '/tmp/nonexistent-test' })) as {
|
|
282
424
|
agent: { name: string };
|
|
425
|
+
claude_md: { project: { exists: boolean; has_agent_section: boolean }; global: { exists: boolean; has_agent_section: boolean } };
|
|
283
426
|
vault: { entries: number };
|
|
427
|
+
hooks: { agent: string[]; global: string[]; missing: string[] };
|
|
284
428
|
recommendations: string[];
|
|
285
429
|
};
|
|
286
430
|
expect(result.agent.name).toBe('${escapeQuotes(config.name)}');
|
|
@@ -330,6 +474,32 @@ ${domainDescribes}
|
|
|
330
474
|
const result = (await healthOp.handler({})) as { score: number };
|
|
331
475
|
expect(result.score).toBeGreaterThan(0);
|
|
332
476
|
});
|
|
477
|
+
|
|
478
|
+
it('governance_policy get should return default policy', async () => {
|
|
479
|
+
const facade = buildCoreFacade();
|
|
480
|
+
const policyOp = facade.ops.find((o) => o.name === 'governance_policy')!;
|
|
481
|
+
const result = (await policyOp.handler({ action: 'get', projectPath: '/test' })) as {
|
|
482
|
+
projectPath: string;
|
|
483
|
+
quotas: { maxEntriesTotal: number };
|
|
484
|
+
autoCapture: { enabled: boolean };
|
|
485
|
+
};
|
|
486
|
+
expect(result.projectPath).toBe('/test');
|
|
487
|
+
expect(result.quotas.maxEntriesTotal).toBe(500);
|
|
488
|
+
expect(result.autoCapture.enabled).toBe(true);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('governance_dashboard should return combined view', async () => {
|
|
492
|
+
const facade = buildCoreFacade();
|
|
493
|
+
const dashOp = facade.ops.find((o) => o.name === 'governance_dashboard')!;
|
|
494
|
+
const result = (await dashOp.handler({ projectPath: '/test' })) as {
|
|
495
|
+
vaultSize: number;
|
|
496
|
+
quotaPercent: number;
|
|
497
|
+
pendingProposals: number;
|
|
498
|
+
};
|
|
499
|
+
expect(typeof result.vaultSize).toBe('number');
|
|
500
|
+
expect(typeof result.quotaPercent).toBe('number');
|
|
501
|
+
expect(result.pendingProposals).toBe(0);
|
|
502
|
+
});
|
|
333
503
|
});
|
|
334
504
|
});
|
|
335
505
|
`;
|
|
@@ -387,7 +557,7 @@ function generateDomainDescribe(agentId: string, domain: string): string {
|
|
|
387
557
|
severity: 'warning',
|
|
388
558
|
description: 'A captured pattern.',
|
|
389
559
|
tags: ['captured'],
|
|
390
|
-
})) as { captured: boolean };
|
|
560
|
+
})) as { captured: boolean; governance?: { action: string } };
|
|
391
561
|
expect(result.captured).toBe(true);
|
|
392
562
|
const entry = runtime.vault.get('${domain}-cap1');
|
|
393
563
|
expect(entry).not.toBeNull();
|