@sage-protocol/cli 0.4.0 → 0.4.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/browser-wallet-integration.js +0 -1
- package/dist/cli/cast-wallet-manager.js +0 -1
- package/dist/cli/commands/interview.js +149 -0
- package/dist/cli/commands/personal.js +138 -79
- package/dist/cli/commands/prompts.js +242 -87
- package/dist/cli/commands/stake-status.js +0 -2
- package/dist/cli/config.js +28 -8
- package/dist/cli/governance-manager.js +28 -19
- package/dist/cli/index.js +32 -8
- package/dist/cli/library-manager.js +16 -6
- package/dist/cli/mcp-server-stdio.js +759 -156
- package/dist/cli/mcp-server.js +4 -30
- package/dist/cli/metamask-integration.js +0 -1
- package/dist/cli/privy-wallet-manager.js +2 -2
- package/dist/cli/prompt-manager.js +0 -1
- package/dist/cli/services/artifact-manager.js +198 -0
- package/dist/cli/services/doctor/fixers.js +1 -1
- package/dist/cli/services/mcp/env-loader.js +2 -0
- package/dist/cli/services/mcp/prompt-result-formatter.js +8 -1
- package/dist/cli/services/mcp/quick-start.js +14 -15
- package/dist/cli/services/mcp/sage-tool-registry.js +322 -0
- package/dist/cli/services/mcp/tool-args-validator.js +43 -0
- package/dist/cli/services/metaprompt/anthropic-client.js +87 -0
- package/dist/cli/services/metaprompt/interview-driver.js +161 -0
- package/dist/cli/services/metaprompt/model-client.js +49 -0
- package/dist/cli/services/metaprompt/openai-client.js +67 -0
- package/dist/cli/services/metaprompt/persistence.js +86 -0
- package/dist/cli/services/metaprompt/prompt-builder.js +186 -0
- package/dist/cli/services/metaprompt/session.js +18 -80
- package/dist/cli/services/metaprompt/slot-planner.js +115 -0
- package/dist/cli/services/metaprompt/templates.json +130 -0
- package/dist/cli/services/project-context.js +98 -0
- package/dist/cli/subdao.js +0 -3
- package/dist/cli/sxxx-manager.js +0 -1
- package/dist/cli/utils/aliases.js +0 -6
- package/dist/cli/utils/tx-wait.js +0 -3
- package/dist/cli/wallet-manager.js +18 -19
- package/dist/cli/walletconnect-integration.js +0 -1
- package/dist/cli/wizard-manager.js +0 -1
- package/package.json +3 -1
- package/dist/cli/commands/prompt-test.js +0 -176
- package/dist/cli/commands/prompt.js +0 -2531
package/dist/cli/mcp-server.js
CHANGED
|
@@ -11,6 +11,7 @@ const { ethers } = require('ethers');
|
|
|
11
11
|
const { execSync } = require('child_process');
|
|
12
12
|
const axios = require('axios');
|
|
13
13
|
const fss = require('fs');
|
|
14
|
+
const { hydrateEnvFromSageConfig } = require('./services/mcp/env-loader');
|
|
14
15
|
const { createLibraryManifestLister } = require('./services/mcp/library-manifests');
|
|
15
16
|
const { createManifestFetcher } = require('./services/mcp/manifest-fetcher');
|
|
16
17
|
const { createManifestWorkflows } = require('./services/mcp/manifest-workflows');
|
|
@@ -57,37 +58,10 @@ class MCPServer extends EventEmitter {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
async initialize() {
|
|
60
|
-
// Hydrate env from .sage/config.json if missing
|
|
61
|
+
// Hydrate env from .sage/config.json if missing (factory, registry, token, subgraph)
|
|
61
62
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
for (let i = 0; i < 6; i++) {
|
|
65
|
-
const cand = path.join(dir, '.sage', 'config.json');
|
|
66
|
-
if (fss.existsSync(cand)) { cfgPath = cand; break; }
|
|
67
|
-
const next = path.dirname(dir);
|
|
68
|
-
if (next === dir) break;
|
|
69
|
-
dir = next;
|
|
70
|
-
}
|
|
71
|
-
if (!cfgPath) {
|
|
72
|
-
const alt = path.join(__dirname, '..', '.sage', 'config.json');
|
|
73
|
-
if (fss.existsSync(alt)) cfgPath = alt;
|
|
74
|
-
}
|
|
75
|
-
if (cfgPath) {
|
|
76
|
-
const raw = fss.readFileSync(cfgPath, 'utf8');
|
|
77
|
-
const j = JSON.parse(raw);
|
|
78
|
-
const active = j.activeProfile || Object.keys(j.profiles || {})[0];
|
|
79
|
-
const a = (j.profiles && j.profiles[active] && j.profiles[active].addresses) || {};
|
|
80
|
-
if (!process.env.SUBDAO_FACTORY_ADDRESS && (a.SUBDAO_FACTORY_ADDRESS || a.SUBDAO_FACTORY)) {
|
|
81
|
-
process.env.SUBDAO_FACTORY_ADDRESS = a.SUBDAO_FACTORY_ADDRESS || a.SUBDAO_FACTORY;
|
|
82
|
-
}
|
|
83
|
-
if (!process.env.LIBRARY_REGISTRY_ADDRESS && (a.LIBRARY_REGISTRY_ADDRESS || a.LIBRARY_REGISTRY)) {
|
|
84
|
-
process.env.LIBRARY_REGISTRY_ADDRESS = a.LIBRARY_REGISTRY_ADDRESS || a.LIBRARY_REGISTRY;
|
|
85
|
-
}
|
|
86
|
-
if (!process.env.SXXX_TOKEN_ADDRESS && (a.SXXX_TOKEN_ADDRESS || a.SXXX)) {
|
|
87
|
-
process.env.SXXX_TOKEN_ADDRESS = a.SXXX_TOKEN_ADDRESS || a.SXXX;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} catch (_) { /* ignore */ }
|
|
63
|
+
hydrateEnvFromSageConfig({ cwd: process.cwd(), fallbackDir: path.join(__dirname, '..') });
|
|
64
|
+
} catch (_) { /* non-fatal */ }
|
|
91
65
|
// Ensure directories exist
|
|
92
66
|
await this.ensureDirectories();
|
|
93
67
|
|
|
@@ -142,7 +142,7 @@ class PrivyWalletManager {
|
|
|
142
142
|
|
|
143
143
|
async initialize() {
|
|
144
144
|
try {
|
|
145
|
-
console.log('✅ Deterministic wallet manager initialized');
|
|
145
|
+
if (process.env.SAGE_VERBOSE === '1') console.log('✅ Deterministic wallet manager initialized');
|
|
146
146
|
return true;
|
|
147
147
|
} catch (error) {
|
|
148
148
|
console.error('❌ Wallet initialization failed:', error.message);
|
|
@@ -208,7 +208,7 @@ class PrivyWalletManager {
|
|
|
208
208
|
async promptEmail() {
|
|
209
209
|
let cached = getCachedPrivyEmail();
|
|
210
210
|
if (cached && cached.includes('@')) {
|
|
211
|
-
if (!process.env.SAGE_QUIET_JSON) console.log(`📧 Using cached Privy email: ${cached}`);
|
|
211
|
+
if (!process.env.SAGE_QUIET_JSON && process.env.SAGE_VERBOSE === '1') console.log(`📧 Using cached Privy email: ${cached}`);
|
|
212
212
|
return cached;
|
|
213
213
|
}
|
|
214
214
|
let email = '';
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
const { readWorkspace, computeChangeSet } = require('./prompts/workspace');
|
|
6
|
+
|
|
7
|
+
class ArtifactManager {
|
|
8
|
+
constructor(cwd = process.cwd()) {
|
|
9
|
+
this.cwd = cwd;
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.promptsDir = path.join(this.cwd, 'prompts');
|
|
12
|
+
this.sageDir = path.join(this.cwd, '.sage');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async initialize() {
|
|
16
|
+
if (!fs.existsSync(this.promptsDir)) fs.mkdirSync(this.promptsDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* List all artifacts, optionally filtered
|
|
21
|
+
* @param {Object} filter { kind, publishable }
|
|
22
|
+
*/
|
|
23
|
+
async listArtifacts(filter = {}) {
|
|
24
|
+
const artifacts = [];
|
|
25
|
+
const ws = readWorkspace();
|
|
26
|
+
const changes = ws ? computeChangeSet(ws) : { added: [], modified: [], removed: [] };
|
|
27
|
+
|
|
28
|
+
if (fs.existsSync(this.promptsDir)) {
|
|
29
|
+
const workspaceFiles = await this._scanDir(this.promptsDir);
|
|
30
|
+
for (const file of workspaceFiles) {
|
|
31
|
+
const artifact = await this._parseArtifact(file, ws, changes);
|
|
32
|
+
if (artifact) artifacts.push(artifact);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return artifacts.filter(a => {
|
|
37
|
+
if (filter.kind && a.kind !== filter.kind) return false;
|
|
38
|
+
if (filter.publishable !== undefined && a.publishable !== filter.publishable) return false;
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async getArtifact(key) {
|
|
44
|
+
// Try to find the file. Key could be "skills/coding-helper" -> prompts/skills/coding-helper.md
|
|
45
|
+
const potentialPath = path.join(this.promptsDir, key + '.md');
|
|
46
|
+
|
|
47
|
+
if (fs.existsSync(potentialPath)) {
|
|
48
|
+
const ws = readWorkspace();
|
|
49
|
+
const changes = ws ? computeChangeSet(ws) : { added: [], modified: [], removed: [] };
|
|
50
|
+
return await this._parseArtifact(potentialPath, ws, changes);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async saveArtifact(artifact) {
|
|
57
|
+
const { key, kind, body, meta, targets, publishable } = artifact;
|
|
58
|
+
|
|
59
|
+
let relPath = key;
|
|
60
|
+
if (!relPath.endsWith('.md')) relPath += '.md';
|
|
61
|
+
|
|
62
|
+
// Enforce directory conventions
|
|
63
|
+
if (kind === 'skill' && !relPath.startsWith('skills/')) {
|
|
64
|
+
relPath = `skills/${relPath}`;
|
|
65
|
+
} else if (kind === 'snippet' && !relPath.startsWith('snippets/')) {
|
|
66
|
+
relPath = `snippets/${relPath}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const filePath = path.join(this.promptsDir, relPath);
|
|
70
|
+
const dir = path.dirname(filePath);
|
|
71
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
72
|
+
|
|
73
|
+
// Update meta
|
|
74
|
+
const frontmatter = {
|
|
75
|
+
...meta,
|
|
76
|
+
kind,
|
|
77
|
+
publishable: publishable !== undefined ? publishable : (kind === 'prompt'),
|
|
78
|
+
targets: targets || meta?.targets || []
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Construct file content
|
|
82
|
+
const yaml = Object.entries(frontmatter)
|
|
83
|
+
.filter(([_, v]) => v !== undefined && v !== null)
|
|
84
|
+
.map(([k, v]) => `${k}: ${JSON.stringify(v)}`)
|
|
85
|
+
.join('\n');
|
|
86
|
+
|
|
87
|
+
const content = `---\n${yaml}\n---\n\n${body}`;
|
|
88
|
+
|
|
89
|
+
fs.writeFileSync(filePath, content);
|
|
90
|
+
|
|
91
|
+
return this._parseArtifact(filePath);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// --- Helpers ---
|
|
95
|
+
|
|
96
|
+
async _scanDir(dir, base = '') {
|
|
97
|
+
let results = [];
|
|
98
|
+
try {
|
|
99
|
+
const list = fs.readdirSync(dir);
|
|
100
|
+
for (const file of list) {
|
|
101
|
+
const fullPath = path.join(dir, file);
|
|
102
|
+
const stat = fs.statSync(fullPath);
|
|
103
|
+
if (stat && stat.isDirectory()) {
|
|
104
|
+
results = results.concat(await this._scanDir(fullPath));
|
|
105
|
+
} else {
|
|
106
|
+
if (file.endsWith('.md')) {
|
|
107
|
+
results.push(fullPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
// ignore if dir doesn't exist
|
|
113
|
+
}
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async _parseArtifact(filePath, ws = null, changes = null) {
|
|
118
|
+
try {
|
|
119
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
120
|
+
const { attributes, body } = this._parseFrontMatter(content);
|
|
121
|
+
|
|
122
|
+
// Infer key from path relative to prompts dir
|
|
123
|
+
const relative = path.relative(this.promptsDir, filePath);
|
|
124
|
+
const key = relative.replace(/\.md$/, '');
|
|
125
|
+
|
|
126
|
+
// Infer kind
|
|
127
|
+
let kind = attributes.kind;
|
|
128
|
+
if (!kind) {
|
|
129
|
+
if (key.startsWith('skills/') || key.includes('/skills/')) kind = 'skill';
|
|
130
|
+
else if (key.startsWith('snippets/') || key.includes('/snippets/')) kind = 'snippet';
|
|
131
|
+
else kind = 'prompt';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Infer publishable
|
|
135
|
+
// Default: true for prompts, false for skills/snippets
|
|
136
|
+
let publishable = attributes.publishable;
|
|
137
|
+
if (publishable === undefined) {
|
|
138
|
+
publishable = (kind === 'prompt');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Derive publishing state
|
|
142
|
+
let status = 'unpublished';
|
|
143
|
+
if (ws && ws.files && ws.files[key]) {
|
|
144
|
+
if (changes && changes.modified.includes(key)) {
|
|
145
|
+
status = 'modified';
|
|
146
|
+
} else {
|
|
147
|
+
status = 'published'; // Assumed synced if in workspace and not modified
|
|
148
|
+
}
|
|
149
|
+
} else if (changes && changes.added.includes(key)) {
|
|
150
|
+
status = 'new';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
key,
|
|
155
|
+
kind,
|
|
156
|
+
publishable,
|
|
157
|
+
body,
|
|
158
|
+
meta: attributes,
|
|
159
|
+
targets: attributes.targets || [],
|
|
160
|
+
publishing: {
|
|
161
|
+
status,
|
|
162
|
+
lastSynced: ws?.files?.[key]?.mtimeMs || null
|
|
163
|
+
},
|
|
164
|
+
filePath
|
|
165
|
+
};
|
|
166
|
+
} catch (e) {
|
|
167
|
+
console.warn(`Failed to parse artifact at ${filePath}: `, e.message);
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
_parseFrontMatter(content) {
|
|
173
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
174
|
+
if (match) {
|
|
175
|
+
const yaml = match[1];
|
|
176
|
+
const body = match[2];
|
|
177
|
+
const attributes = {};
|
|
178
|
+
yaml.split('\n').forEach(line => {
|
|
179
|
+
const parts = line.split(':');
|
|
180
|
+
if (parts.length >= 2) {
|
|
181
|
+
const k = parts[0].trim();
|
|
182
|
+
const v = parts.slice(1).join(':').trim();
|
|
183
|
+
if (k && v) {
|
|
184
|
+
try {
|
|
185
|
+
attributes[k] = JSON.parse(v);
|
|
186
|
+
} catch (e) {
|
|
187
|
+
attributes[k] = v;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
return { attributes, body: body.trim() };
|
|
193
|
+
}
|
|
194
|
+
return { attributes: {}, body: content.trim() };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = ArtifactManager;
|
|
@@ -92,7 +92,7 @@ async function fixIpfsKeys(context = {}) {
|
|
|
92
92
|
if (!envContent.includes('PINATA_SECRET_API_KEY=')) envContent += `\nPINATA_SECRET_API_KEY=${answers.secret}`;
|
|
93
93
|
if (answers.jwt && !envContent.includes('PINATA_JWT=')) envContent += `\nPINATA_JWT=${answers.jwt}`;
|
|
94
94
|
fs.writeFileSync(envPath, envContent);
|
|
95
|
-
try {
|
|
95
|
+
try { config.loadEnv(); } catch(_){}
|
|
96
96
|
console.log('✅ Added Pinata keys to .env');
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -55,6 +55,8 @@ function hydrateEnvFromSageConfig(options = {}) {
|
|
|
55
55
|
SUBDAO_FACTORY_ADDRESS: addresses.SUBDAO_FACTORY_ADDRESS || addresses.SUBDAO_FACTORY,
|
|
56
56
|
LIBRARY_REGISTRY_ADDRESS: addresses.LIBRARY_REGISTRY_ADDRESS || addresses.LIBRARY_REGISTRY,
|
|
57
57
|
SXXX_TOKEN_ADDRESS: addresses.SXXX_TOKEN_ADDRESS || addresses.SXXX,
|
|
58
|
+
SUBGRAPH_URL: addresses.SAGE_SUBGRAPH_URL || addresses.SUBGRAPH_URL,
|
|
59
|
+
SAGE_SUBGRAPH_URL: addresses.SAGE_SUBGRAPH_URL,
|
|
58
60
|
};
|
|
59
61
|
for (const [key, value] of Object.entries(mapping)) {
|
|
60
62
|
if (value && typeof value === 'string' && !process.env[key]) {
|
|
@@ -23,7 +23,14 @@ function formatUnifiedResults(items = [], { total = 0, page = 1, pageSize = 10 }
|
|
|
23
23
|
const libraryName = entry?.library?.name ? ` 🗂️ Library: ${entry.library.name}\n` : '';
|
|
24
24
|
const cidLine = entry?.cid ? ` 🔗 CID: ${entry.cid}\n` : '';
|
|
25
25
|
const origin = entry?.origin || entry?.source || 'unknown';
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
// New unified fields
|
|
28
|
+
const kind = entry?.kind ? ` 🔹 Kind: ${entry.kind}\n` : '';
|
|
29
|
+
const publishable = entry?.publishable !== undefined ? ` 📡 Publishable: ${entry.publishable}\n` : '';
|
|
30
|
+
const status = entry?.publishing?.status ? ` 📊 Status: ${entry.publishing.status}\n` : '';
|
|
31
|
+
const project = entry?.project ? ` 🏗️ Project: ${entry.project}\n` : '';
|
|
32
|
+
|
|
33
|
+
return `${absoluteIndex}. **${promptName}**\n 📜 ${description}\n${kind}${publishable}${status}${project}${libraryName}${cidLine} 🔖 Tags: ${tags}\n 🌐 Source: ${origin}\n`;
|
|
27
34
|
}).join('\n');
|
|
28
35
|
|
|
29
36
|
return head + lines;
|
|
@@ -60,13 +60,12 @@ function createQuickStart({
|
|
|
60
60
|
// Since LibraryManager doesn't expose "addPrompt", we'll implement it here for now
|
|
61
61
|
// In a real refactor, we should move this to LibraryManager
|
|
62
62
|
|
|
63
|
-
const libDir = libraryManager.ensureLibrariesDir();
|
|
64
|
-
const manifestPath = path.join(libDir, `${targetLib.cid}.json`);
|
|
65
|
-
|
|
66
63
|
// Create prompt file
|
|
67
64
|
// We'll store it in a 'prompts' subdirectory next to the manifest if possible,
|
|
68
65
|
// but for local libraries, they are flat in ~/.sage/libraries/
|
|
69
66
|
// Let's create a prompts directory inside ~/.sage/libraries/prompts/
|
|
67
|
+
const libDir = libraryManager.ensureLibrariesDir();
|
|
68
|
+
const manifestPath = libraryManager.getManifestPath(targetLib.cid);
|
|
70
69
|
const promptsDir = path.join(libDir, 'prompts');
|
|
71
70
|
if (!fs.existsSync(promptsDir)) {
|
|
72
71
|
fs.mkdirSync(promptsDir, { recursive: true });
|
|
@@ -114,7 +113,7 @@ function createQuickStart({
|
|
|
114
113
|
cid: '' // Local prompt
|
|
115
114
|
});
|
|
116
115
|
|
|
117
|
-
|
|
116
|
+
libraryManager.writeManifest(targetLib.cid, manifest);
|
|
118
117
|
|
|
119
118
|
return {
|
|
120
119
|
success: true,
|
|
@@ -129,19 +128,18 @@ function createQuickStart({
|
|
|
129
128
|
if (!key) throw new Error('Key is required');
|
|
130
129
|
|
|
131
130
|
// Find the prompt
|
|
132
|
-
|
|
131
|
+
const pinned = libraryManager.listPinned();
|
|
133
132
|
let found = null;
|
|
134
133
|
let foundLib = null;
|
|
135
134
|
|
|
136
135
|
for (const lib of pinned) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
136
|
+
const { manifest } = libraryManager.loadPinned(lib.cid);
|
|
137
|
+
const prompt = manifest.prompts?.find(p => p.key === key);
|
|
138
|
+
if (prompt) {
|
|
139
|
+
found = prompt;
|
|
140
|
+
foundLib = { ...lib, manifest };
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
if (!found) {
|
|
@@ -150,13 +148,13 @@ function createQuickStart({
|
|
|
150
148
|
|
|
151
149
|
// Update content if provided
|
|
152
150
|
if (content) {
|
|
153
|
-
const libDir = libraryManager.ensureLibrariesDir();
|
|
154
151
|
let promptFilePath;
|
|
155
152
|
|
|
156
153
|
if (found.files && found.files.length > 0) {
|
|
157
154
|
// Use existing file
|
|
158
155
|
const relativePath = found.files[0];
|
|
159
156
|
// Handle potential path issues
|
|
157
|
+
const libDir = libraryManager.ensureLibrariesDir();
|
|
160
158
|
promptFilePath = path.join(libDir, relativePath);
|
|
161
159
|
|
|
162
160
|
// Backup existing
|
|
@@ -166,6 +164,7 @@ function createQuickStart({
|
|
|
166
164
|
}
|
|
167
165
|
} else {
|
|
168
166
|
// Create new file if none existed (legacy/imported)
|
|
167
|
+
const libDir = libraryManager.ensureLibrariesDir();
|
|
169
168
|
const promptsDir = path.join(libDir, 'prompts');
|
|
170
169
|
if (!fs.existsSync(promptsDir)) fs.mkdirSync(promptsDir, { recursive: true });
|
|
171
170
|
const promptFileName = `${foundLib.cid}_${key}.md`;
|
|
@@ -210,7 +209,7 @@ function createQuickStart({
|
|
|
210
209
|
}
|
|
211
210
|
|
|
212
211
|
// Save manifest
|
|
213
|
-
|
|
212
|
+
libraryManager.writeManifest(foundLib.cid, foundLib.manifest);
|
|
214
213
|
|
|
215
214
|
return {
|
|
216
215
|
success: true,
|