patchcord 0.2.5 → 0.2.6
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/bin/patchcord.mjs +128 -0
- package/package.json +1 -1
package/bin/patchcord.mjs
CHANGED
|
@@ -25,6 +25,8 @@ Commands:
|
|
|
25
25
|
patchcord install --full Install + enable full statusline (model, context%, git)
|
|
26
26
|
patchcord agent Set up MCP config for an agent in this project
|
|
27
27
|
patchcord agent --codex Set up Codex skill + MCP config in this project
|
|
28
|
+
patchcord skill apply Fetch and apply custom skill from the web console
|
|
29
|
+
patchcord skill reinstall Full rewrite: default skill + custom skill from server
|
|
28
30
|
|
|
29
31
|
Run "patchcord install" once. Run "patchcord agent" in each project.`);
|
|
30
32
|
process.exit(0);
|
|
@@ -155,5 +157,131 @@ if (cmd === "init") {
|
|
|
155
157
|
process.exit(0);
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
// ── skill: custom skill management ───────────────────────────
|
|
161
|
+
if (cmd === "skill") {
|
|
162
|
+
const sub = process.argv[3];
|
|
163
|
+
const cwd = process.cwd();
|
|
164
|
+
|
|
165
|
+
// Find .mcp.json to get URL and token
|
|
166
|
+
let mcpJson = null;
|
|
167
|
+
let dir = cwd;
|
|
168
|
+
while (dir && dir !== "/") {
|
|
169
|
+
const p = join(dir, ".mcp.json");
|
|
170
|
+
if (existsSync(p)) { mcpJson = p; break; }
|
|
171
|
+
dir = dirname(dir);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!mcpJson) {
|
|
175
|
+
console.error("No .mcp.json found. Run 'patchcord agent' first.");
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const { readFileSync, writeFileSync } = await import("fs");
|
|
180
|
+
const config = JSON.parse(readFileSync(mcpJson, "utf-8"));
|
|
181
|
+
const mcpUrl = config?.mcpServers?.patchcord?.url || "";
|
|
182
|
+
const auth = config?.mcpServers?.patchcord?.headers?.Authorization || "";
|
|
183
|
+
const baseUrl = mcpUrl.replace(/\/mcp(\/bearer)?$/, "");
|
|
184
|
+
const token = auth.replace(/^Bearer\s+/, "");
|
|
185
|
+
|
|
186
|
+
if (!baseUrl || !token) {
|
|
187
|
+
console.error("Cannot read patchcord URL/token from .mcp.json");
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Derive namespace and agent from the token by calling /api/inbox
|
|
192
|
+
let namespace = "", agentId = "";
|
|
193
|
+
try {
|
|
194
|
+
const resp = run(`curl -s -H "Authorization: Bearer ${token}" "${baseUrl}/api/inbox?limit=0"`);
|
|
195
|
+
if (resp) {
|
|
196
|
+
const data = JSON.parse(resp);
|
|
197
|
+
namespace = data.namespace_id || "";
|
|
198
|
+
agentId = data.agent_id || "";
|
|
199
|
+
}
|
|
200
|
+
} catch {}
|
|
201
|
+
|
|
202
|
+
if (!namespace || !agentId) {
|
|
203
|
+
console.error("Cannot determine agent identity. Check your token.");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const START_DELIM = "########## PATCHCORD CUSTOM SKILL ##########";
|
|
208
|
+
const END_DELIM = "########## END CUSTOM SKILL ##########";
|
|
209
|
+
|
|
210
|
+
// Find the skill file
|
|
211
|
+
const skillFile = join(cwd, "PATCHCORD.md");
|
|
212
|
+
const pluginSkill = join(pluginRoot, "skills", "inbox", "SKILL.md");
|
|
213
|
+
|
|
214
|
+
function applyCustomSkill(skillText) {
|
|
215
|
+
let content = "";
|
|
216
|
+
if (existsSync(skillFile)) {
|
|
217
|
+
content = readFileSync(skillFile, "utf-8");
|
|
218
|
+
} else if (existsSync(pluginSkill)) {
|
|
219
|
+
content = readFileSync(pluginSkill, "utf-8");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Remove existing custom skill block
|
|
223
|
+
const startIdx = content.indexOf(START_DELIM);
|
|
224
|
+
const endIdx = content.indexOf(END_DELIM);
|
|
225
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
226
|
+
content = content.substring(0, startIdx).trimEnd();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Append new custom skill
|
|
230
|
+
if (skillText && skillText.trim()) {
|
|
231
|
+
content = content.trimEnd() + "\n\n" + START_DELIM + "\n" + skillText.trim() + "\n" + END_DELIM + "\n";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
writeFileSync(skillFile, content);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (sub === "apply" || !sub) {
|
|
238
|
+
// Fetch and apply custom skill
|
|
239
|
+
console.log(`Fetching custom skill for ${namespace}:${agentId}...`);
|
|
240
|
+
const resp = run(`curl -s -H "Authorization: Bearer ${token}" "${baseUrl}/api/skills/${namespace}/${agentId}"`);
|
|
241
|
+
if (!resp) {
|
|
242
|
+
console.log("No custom skill found or server unreachable.");
|
|
243
|
+
process.exit(0);
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
const data = JSON.parse(resp);
|
|
247
|
+
if (data.skill_text) {
|
|
248
|
+
applyCustomSkill(data.skill_text);
|
|
249
|
+
console.log(`✓ Custom skill applied to ${skillFile}`);
|
|
250
|
+
} else {
|
|
251
|
+
console.log("No custom skill set for this agent.");
|
|
252
|
+
}
|
|
253
|
+
} catch {
|
|
254
|
+
console.error("Failed to parse skill response.");
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
} else if (sub === "reinstall") {
|
|
258
|
+
// Full rewrite: default skill + custom skill
|
|
259
|
+
console.log("Reinstalling patchcord skill...");
|
|
260
|
+
if (existsSync(pluginSkill)) {
|
|
261
|
+
let defaultContent = readFileSync(pluginSkill, "utf-8");
|
|
262
|
+
writeFileSync(skillFile, defaultContent);
|
|
263
|
+
}
|
|
264
|
+
// Then apply custom on top
|
|
265
|
+
const resp = run(`curl -s -H "Authorization: Bearer ${token}" "${baseUrl}/api/skills/${namespace}/${agentId}"`);
|
|
266
|
+
try {
|
|
267
|
+
const data = JSON.parse(resp || "{}");
|
|
268
|
+
if (data.skill_text) {
|
|
269
|
+
applyCustomSkill(data.skill_text);
|
|
270
|
+
console.log(`✓ Skill reinstalled with custom block at ${skillFile}`);
|
|
271
|
+
} else {
|
|
272
|
+
console.log(`✓ Skill reinstalled (no custom block) at ${skillFile}`);
|
|
273
|
+
}
|
|
274
|
+
} catch {
|
|
275
|
+
console.log(`✓ Skill reinstalled (no custom block) at ${skillFile}`);
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
console.log(`Unknown skill subcommand: ${sub}
|
|
279
|
+
Usage:
|
|
280
|
+
patchcord skill apply Fetch and apply custom skill from server
|
|
281
|
+
patchcord skill reinstall Full rewrite: default + custom skill`);
|
|
282
|
+
}
|
|
283
|
+
process.exit(0);
|
|
284
|
+
}
|
|
285
|
+
|
|
158
286
|
console.error(`Unknown command: ${cmd}. Run 'patchcord help' for usage.`);
|
|
159
287
|
process.exit(1);
|