@sage-protocol/cli 0.2.9 → 0.3.2
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/cli/commands/config.js +28 -0
- package/dist/cli/commands/doctor.js +87 -8
- package/dist/cli/commands/gov-config.js +81 -0
- package/dist/cli/commands/governance.js +152 -72
- package/dist/cli/commands/library.js +9 -0
- package/dist/cli/commands/proposals.js +187 -17
- package/dist/cli/commands/skills.js +737 -0
- package/dist/cli/commands/subdao.js +96 -132
- package/dist/cli/config/playbooks.json +62 -0
- package/dist/cli/config.js +15 -0
- package/dist/cli/governance-manager.js +25 -4
- package/dist/cli/index.js +6 -7
- package/dist/cli/library-manager.js +79 -0
- package/dist/cli/mcp-server-stdio.js +1387 -166
- package/dist/cli/schemas/manifest.schema.json +55 -0
- package/dist/cli/services/doctor/fixers.js +134 -0
- package/dist/cli/services/governance/doctor.js +140 -0
- package/dist/cli/services/governance/playbooks.js +97 -0
- package/dist/cli/services/mcp/bulk-operations.js +272 -0
- package/dist/cli/services/mcp/dependency-analyzer.js +202 -0
- package/dist/cli/services/mcp/library-listing.js +2 -2
- package/dist/cli/services/mcp/local-prompt-collector.js +1 -0
- package/dist/cli/services/mcp/manifest-downloader.js +5 -3
- package/dist/cli/services/mcp/manifest-fetcher.js +17 -1
- package/dist/cli/services/mcp/manifest-workflows.js +127 -15
- package/dist/cli/services/mcp/quick-start.js +287 -0
- package/dist/cli/services/mcp/stdio-runner.js +30 -5
- package/dist/cli/services/mcp/template-manager.js +156 -0
- package/dist/cli/services/mcp/templates/default-templates.json +84 -0
- package/dist/cli/services/mcp/tool-args-validator.js +56 -0
- package/dist/cli/services/mcp/trending-formatter.js +1 -1
- package/dist/cli/services/mcp/unified-prompt-search.js +2 -2
- package/dist/cli/services/metaprompt/designer.js +12 -5
- package/dist/cli/services/skills/discovery.js +99 -0
- package/dist/cli/services/subdao/applier.js +229 -0
- package/dist/cli/services/subdao/planner.js +142 -0
- package/dist/cli/subdao-manager.js +14 -0
- package/dist/cli/utils/aliases.js +28 -6
- package/dist/cli/utils/contract-error-decoder.js +61 -0
- package/dist/cli/utils/suggestions.js +25 -13
- package/package.json +3 -2
- package/src/schemas/manifest.schema.json +55 -0
|
@@ -41,7 +41,8 @@ function register(program) {
|
|
|
41
41
|
try {
|
|
42
42
|
const HelperABI = ['event ProposalTupleIndexed(uint256 indexed proposalId,address[] targets,uint256[] values,bytes[] calldatas,bytes32 descriptionHash)'];
|
|
43
43
|
const helper = new ethers.Contract(helperAddr, HelperABI, provider);
|
|
44
|
-
|
|
44
|
+
// Ethers v6 compatibility: compute topic hash directly from signature
|
|
45
|
+
const topic = (ethers.id || ((s)=>require('ethers').id(s)))('ProposalTupleIndexed(uint256,address[],uint256[],bytes[],bytes32)');
|
|
45
46
|
const address = helperAddr;
|
|
46
47
|
const filter = { address, topics: [topic] };
|
|
47
48
|
// Respect from-block and max-range by chunking if needed
|
|
@@ -152,18 +153,37 @@ function register(program) {
|
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
if (!emitted) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
156
|
+
// Fallback: use SDK getProposalDetails
|
|
157
|
+
try {
|
|
158
|
+
const { getProposalDetails } = require('@sage-protocol/sdk');
|
|
159
|
+
const details = await getProposalDetails({
|
|
160
|
+
governorAddress: ctx.governor,
|
|
161
|
+
proposalId: pid,
|
|
162
|
+
provider,
|
|
163
|
+
helperAddress: helperAddr
|
|
164
|
+
});
|
|
165
|
+
const summary = {
|
|
166
|
+
id: pid.toString(),
|
|
167
|
+
source: 'sdk',
|
|
168
|
+
actionCount: details.targets?.length || 0,
|
|
169
|
+
targets: details.targets || [],
|
|
170
|
+
values: (details.values || []).map(v => v.toString()),
|
|
171
|
+
selectors: (details.calldatas || []).map(d => (typeof d === 'string' ? d.slice(0, 10) : '0x')),
|
|
172
|
+
description: details.description || ''
|
|
173
|
+
};
|
|
174
|
+
if (opts.json) console.log(JSON.stringify(withJsonVersion(summary), null, 2));
|
|
175
|
+
else {
|
|
176
|
+
console.log(`Proposal ${summary.id} (SDK)`);
|
|
177
|
+
console.log(` Actions: ${summary.actionCount}`);
|
|
178
|
+
if (summary.targets.length) console.log(` First target: ${summary.targets[0]}`);
|
|
179
|
+
if (summary.selectors.length) console.log(` First selector: ${summary.selectors[0]}`);
|
|
180
|
+
if (summary.description) console.log(` Description: ${summary.description.slice(0, 100)}...`);
|
|
166
181
|
}
|
|
182
|
+
} catch (e) {
|
|
183
|
+
// Final fallback: minimal info
|
|
184
|
+
const summary = { id: pid.toString(), description: '(unable to fetch proposal details)' };
|
|
185
|
+
if (opts.json) console.log(JSON.stringify(withJsonVersion(summary), null, 2));
|
|
186
|
+
else console.log(`Proposal ${summary.id} - ${summary.description}`);
|
|
167
187
|
}
|
|
168
188
|
}
|
|
169
189
|
} catch (e) {
|
|
@@ -252,9 +272,17 @@ function register(program) {
|
|
|
252
272
|
const ctx = await resolveGovContext({ govOpt: opts.gov, subdaoOpt: opts.subdao, provider });
|
|
253
273
|
try { const net = await provider.getNetwork(); ctx.chainId = Number(net.chainId || 0); } catch (_) {}
|
|
254
274
|
printContextBanner(ctx, opts);
|
|
275
|
+
// Initialize wallet first, then GovernanceManager
|
|
276
|
+
const WalletManager = require('../wallet-manager');
|
|
277
|
+
const wm = new WalletManager();
|
|
278
|
+
await wm.connect();
|
|
279
|
+
const signer = wm.getSigner();
|
|
280
|
+
const walletProvider = wm.getProvider();
|
|
281
|
+
|
|
255
282
|
const GovernanceManager = require('../governance-manager');
|
|
256
283
|
const gm = new GovernanceManager();
|
|
257
|
-
|
|
284
|
+
// Initialize with wallet connection
|
|
285
|
+
await gm.initialize(ctx.subdao || null, { governorOverride: ctx.governor });
|
|
258
286
|
await gm.vote(id, v);
|
|
259
287
|
console.log('✅ Vote submitted');
|
|
260
288
|
} catch (e) {
|
|
@@ -269,6 +297,7 @@ function register(program) {
|
|
|
269
297
|
.argument('<id>', 'Proposal ID')
|
|
270
298
|
.option('--gov <address>', 'Governor address (override)')
|
|
271
299
|
.option('--subdao <address>', 'SubDAO address (derive governor)')
|
|
300
|
+
.option('--simulate', 'Simulate execution without broadcasting a transaction', false)
|
|
272
301
|
.action(async (id, opts) => {
|
|
273
302
|
try {
|
|
274
303
|
enterJsonMode(opts);
|
|
@@ -277,15 +306,31 @@ function register(program) {
|
|
|
277
306
|
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL || 'https://base-sepolia.publicnode.com');
|
|
278
307
|
const ctx = await resolveGovContext({ govOpt: opts.gov, subdaoOpt: opts.subdao, provider });
|
|
279
308
|
try { const net = await provider.getNetwork(); ctx.chainId = Number(net.chainId || 0); } catch (_) {}
|
|
280
|
-
printContextBanner(ctx, opts);
|
|
309
|
+
if (!opts.simulate) printContextBanner(ctx, opts);
|
|
310
|
+
|
|
311
|
+
// Initialize wallet first, then GovernanceManager
|
|
312
|
+
const WalletManager = require('../wallet-manager');
|
|
313
|
+
const wm = new WalletManager();
|
|
314
|
+
if (!opts.simulate) {
|
|
315
|
+
await wm.connect();
|
|
316
|
+
}
|
|
317
|
+
|
|
281
318
|
const GovernanceManager = require('../governance-manager');
|
|
282
319
|
const gm = new GovernanceManager();
|
|
283
|
-
|
|
320
|
+
// Initialize with wallet connection (or just provider for simulation)
|
|
321
|
+
await gm.initialize(ctx.subdao || null, { governorOverride: ctx.governor, providerOverride: provider, signerOverride: opts.simulate ? null : undefined });
|
|
322
|
+
|
|
323
|
+
// If simulate, pass flag
|
|
324
|
+
if (opts.simulate) {
|
|
325
|
+
await gm.executeProposal(id, { simulate: true });
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
284
329
|
// Delegate to existing library-manager execute flow if it is a library update; otherwise, try queue/execute via gm
|
|
285
330
|
try {
|
|
286
|
-
await gm.
|
|
331
|
+
await gm.queueProposal(id);
|
|
287
332
|
} catch (_) {}
|
|
288
|
-
await gm.
|
|
333
|
+
await gm.executeProposal(id);
|
|
289
334
|
console.log('✅ Execute complete');
|
|
290
335
|
} catch (e) {
|
|
291
336
|
const { handleCLIError } = require('../utils/error-handler');
|
|
@@ -293,6 +338,131 @@ function register(program) {
|
|
|
293
338
|
}
|
|
294
339
|
});
|
|
295
340
|
|
|
341
|
+
cmd
|
|
342
|
+
.command('status')
|
|
343
|
+
.description('Show inbox + next recommended action for proposals')
|
|
344
|
+
.option('--gov <address>', 'Governor address (override)')
|
|
345
|
+
.option('--subdao <address>', 'SubDAO address (derive governor)')
|
|
346
|
+
.option('--json', 'Emit JSON output', false)
|
|
347
|
+
.action(async (opts) => {
|
|
348
|
+
try {
|
|
349
|
+
enterJsonMode(opts);
|
|
350
|
+
const { ethers } = require('ethers');
|
|
351
|
+
const { resolveGovContext } = require('../utils/gov-context');
|
|
352
|
+
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL || 'https://base-sepolia.publicnode.com');
|
|
353
|
+
const ctx = await resolveGovContext({ govOpt: opts.gov, subdaoOpt: opts.subdao, provider });
|
|
354
|
+
|
|
355
|
+
// Get inbox
|
|
356
|
+
const inboxCmd = cmd.commands.find(c => c.name() === 'inbox');
|
|
357
|
+
if (!inboxCmd) {
|
|
358
|
+
throw new Error('inbox command not found');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Call inbox logic inline
|
|
362
|
+
const latest = await provider.getBlockNumber();
|
|
363
|
+
const windowDefault = Number(process.env.SAGE_INBOX_BLOCK_WINDOW || 50000);
|
|
364
|
+
const fromBlock = Math.max(0, Number(latest) - windowDefault);
|
|
365
|
+
const toBlock = 'latest';
|
|
366
|
+
|
|
367
|
+
const out = [];
|
|
368
|
+
const helperAddr = opts.helper || process.env.GOVERNANCE_HELPER_ADDRESS || process.env.SAGE_GOVERNANCE_HELPER || null;
|
|
369
|
+
if (helperAddr && /^0x[0-9a-fA-F]{40}$/.test(helperAddr)) {
|
|
370
|
+
try {
|
|
371
|
+
const HelperABI = ['event ProposalTupleIndexed(uint256 indexed proposalId,address[] targets,uint256[] values,bytes[] calldatas,bytes32 descriptionHash)'];
|
|
372
|
+
const helper = new ethers.Contract(helperAddr, HelperABI, provider);
|
|
373
|
+
const topic = (ethers.id || ((s)=>require('ethers').id(s)))('ProposalTupleIndexed(uint256,address[],uint256[],bytes[],bytes32)');
|
|
374
|
+
const filter = { address: helperAddr, topics: [topic] };
|
|
375
|
+
const logs = await provider.getLogs({ ...filter, fromBlock, toBlock });
|
|
376
|
+
for (const log of logs) {
|
|
377
|
+
try {
|
|
378
|
+
const parsed = helper.interface.parseLog(log);
|
|
379
|
+
const id = parsed?.args?.proposalId?.toString?.() || String(parsed?.args?.[0] || '');
|
|
380
|
+
const GovABI = require('../utils/artifacts').resolveArtifact('contracts/cloneable/PromptGovernorCloneable.sol/PromptGovernorCloneable.json').abi;
|
|
381
|
+
const gov = new ethers.Contract(ctx.governor, GovABI, provider);
|
|
382
|
+
let state = 'unknown';
|
|
383
|
+
try { state = String(await gov.state(BigInt(id))); } catch (_) {}
|
|
384
|
+
out.push({ id, state, title: '(indexed)', description: '' });
|
|
385
|
+
} catch (_) {}
|
|
386
|
+
}
|
|
387
|
+
} catch (e) {
|
|
388
|
+
console.log(`⚠️ Helper scan failed: ${e.message}. Falling back to Governor logs.`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (!out.length) {
|
|
392
|
+
const GovABI = require('../utils/artifacts').resolveArtifact('contracts/cloneable/PromptGovernorCloneable.sol/PromptGovernorCloneable.json').abi;
|
|
393
|
+
const gov = new ethers.Contract(ctx.governor, GovABI, provider);
|
|
394
|
+
const filter = gov.filters?.ProposalCreated ? gov.filters.ProposalCreated() : null;
|
|
395
|
+
if (filter) {
|
|
396
|
+
const events = await gov.queryFilter(filter, fromBlock, toBlock);
|
|
397
|
+
for (const ev of events) {
|
|
398
|
+
try {
|
|
399
|
+
const parsed = ev.args || {};
|
|
400
|
+
const id = parsed.proposalId?.toString?.() || parsed[0]?.toString?.() || String(ev.topics?.[1] || '');
|
|
401
|
+
const desc = parsed.description || '';
|
|
402
|
+
const title = (desc.split('\n')[0] || '').slice(0, 80);
|
|
403
|
+
let state = 'unknown';
|
|
404
|
+
try { state = String(await gov.state(BigInt(id))); } catch (_) {}
|
|
405
|
+
out.push({ id, state, title, description: desc });
|
|
406
|
+
} catch (_) {}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (opts.json) {
|
|
412
|
+
console.log(JSON.stringify({ version: '1.0', results: out, nextAction: out.length ? 'vote' : 'none' }, null, 2));
|
|
413
|
+
} else {
|
|
414
|
+
if (!out.length) {
|
|
415
|
+
console.log('No active proposals found.');
|
|
416
|
+
console.log('\n💡 Next: Create a proposal with:');
|
|
417
|
+
console.log(' sage library push <manifest.json> --subdao <subdao-address>');
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
console.log(`Found ${out.length} proposal(s):`);
|
|
421
|
+
out.forEach((r, idx) => {
|
|
422
|
+
const stateName = { '0': 'Pending', '1': 'Active', '2': 'Canceled', '3': 'Defeated', '4': 'Succeeded', '5': 'Queued', '6': 'Expired', '7': 'Executed' }[r.state] || r.state;
|
|
423
|
+
console.log(`\n${idx + 1}. #${r.id} [${stateName}] ${r.title || '(no title)'}`);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Recommend next action
|
|
427
|
+
const active = out.filter(r => r.state === '1');
|
|
428
|
+
const succeeded = out.filter(r => r.state === '4');
|
|
429
|
+
const queued = out.filter(r => r.state === '5');
|
|
430
|
+
|
|
431
|
+
console.log('\n📋 Recommended next actions:');
|
|
432
|
+
if (active.length) {
|
|
433
|
+
const first = active[0];
|
|
434
|
+
const subdao = ctx.subdao || opts.subdao;
|
|
435
|
+
if (subdao) {
|
|
436
|
+
console.log(` sage proposals vote ${first.id} for --subdao ${subdao}`);
|
|
437
|
+
} else {
|
|
438
|
+
console.log(` sage proposals vote ${first.id} for`);
|
|
439
|
+
}
|
|
440
|
+
} else if (succeeded.length) {
|
|
441
|
+
const first = succeeded[0];
|
|
442
|
+
const subdao = ctx.subdao || opts.subdao;
|
|
443
|
+
if (subdao) {
|
|
444
|
+
console.log(` sage proposals execute ${first.id} --subdao ${subdao}`);
|
|
445
|
+
} else {
|
|
446
|
+
console.log(` sage proposals execute ${first.id}`);
|
|
447
|
+
}
|
|
448
|
+
} else if (queued.length) {
|
|
449
|
+
const first = queued[0];
|
|
450
|
+
const subdao = ctx.subdao || opts.subdao;
|
|
451
|
+
if (subdao) {
|
|
452
|
+
console.log(` sage proposals execute ${first.id} --subdao ${subdao}`);
|
|
453
|
+
} else {
|
|
454
|
+
console.log(` sage proposals execute ${first.id}`);
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
console.log(' No actionable proposals at this time.');
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
} catch (e) {
|
|
461
|
+
const { handleCLIError } = require('../utils/error-handler');
|
|
462
|
+
handleCLIError('proposals:status', e, { context: { gov: opts.gov, subdao: opts.subdao } });
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
|
|
296
466
|
program.addCommand(cmd);
|
|
297
467
|
}
|
|
298
468
|
|