@keighleykodric/weeve 0.1.2 → 0.1.3
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/weeve.js +180 -87
- package/package.json +1 -1
package/bin/weeve.js
CHANGED
|
@@ -64,6 +64,7 @@ const FIRST_STEP_ORDER = [
|
|
|
64
64
|
const cmd = process.argv[2];
|
|
65
65
|
const args = process.argv.slice(3);
|
|
66
66
|
|
|
67
|
+
// Run a command in the current working directory
|
|
67
68
|
function run(command) {
|
|
68
69
|
try {
|
|
69
70
|
execSync(command, { stdio: "inherit" });
|
|
@@ -72,6 +73,45 @@ function run(command) {
|
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
// Run a command globally (always from HOME) so skills install to ~/.agents/, not cwd
|
|
77
|
+
function runGlobal(command) {
|
|
78
|
+
try {
|
|
79
|
+
execSync(command, { stdio: "inherit", cwd: os.homedir() });
|
|
80
|
+
} catch {
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Lock file written by the skills CLI — lives in ~/.claude/ regardless of cwd
|
|
86
|
+
const LOCK_PATH = path.join(os.homedir(), ".claude", "skills-lock.json");
|
|
87
|
+
|
|
88
|
+
function readLock() {
|
|
89
|
+
if (!fs.existsSync(LOCK_PATH)) return null;
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(fs.readFileSync(LOCK_PATH, "utf8"));
|
|
92
|
+
} catch {
|
|
93
|
+
console.error(`Could not parse ${LOCK_PATH}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Return installed weeve lanes from the lock file { lane: { count, source } }
|
|
99
|
+
function installedLanes() {
|
|
100
|
+
const lock = readLock();
|
|
101
|
+
if (!lock) return {};
|
|
102
|
+
const laneMap = {};
|
|
103
|
+
for (const [skillName, skillData] of Object.entries(lock.skills || {})) {
|
|
104
|
+
const source = skillData.source || "";
|
|
105
|
+
const match = source.match(/^keighleykodric\/weeve-(.+)$/);
|
|
106
|
+
if (match) {
|
|
107
|
+
const lane = match[1];
|
|
108
|
+
if (!laneMap[lane]) laneMap[lane] = { count: 0, source };
|
|
109
|
+
laneMap[lane].count++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return laneMap;
|
|
113
|
+
}
|
|
114
|
+
|
|
75
115
|
function installLanes(lanes) {
|
|
76
116
|
const invalid = lanes.filter((l) => !LANES[l]);
|
|
77
117
|
if (invalid.length) {
|
|
@@ -84,10 +124,10 @@ function installLanes(lanes) {
|
|
|
84
124
|
for (const lane of lanes) {
|
|
85
125
|
const repo = `${ORG}/weeve-${lane}`;
|
|
86
126
|
console.log(` + ${lane} — ${LANES[lane]}`);
|
|
87
|
-
|
|
127
|
+
runGlobal(`npx skills add ${repo}`);
|
|
88
128
|
}
|
|
89
129
|
// Always install framework skills from the weeve repo itself
|
|
90
|
-
|
|
130
|
+
runGlobal(`npx skills add ${ORG}/weeve`);
|
|
91
131
|
// Show the right first step based on what was installed
|
|
92
132
|
const firstLane = FIRST_STEP_ORDER.find((l) => lanes.includes(l));
|
|
93
133
|
if (firstLane && firstLane !== "pilot") {
|
|
@@ -109,7 +149,6 @@ switch (cmd) {
|
|
|
109
149
|
process.exit(1);
|
|
110
150
|
}
|
|
111
151
|
|
|
112
|
-
// Check if first arg is a preset
|
|
113
152
|
if (args.length === 1 && PRESETS[args[0]]) {
|
|
114
153
|
const preset = args[0];
|
|
115
154
|
console.log(`Preset: ${preset} (${PRESETS[preset].join(", ")})`);
|
|
@@ -120,10 +159,121 @@ switch (cmd) {
|
|
|
120
159
|
break;
|
|
121
160
|
}
|
|
122
161
|
|
|
162
|
+
case "update": {
|
|
163
|
+
// Re-install all currently installed lanes to pull the latest skill versions
|
|
164
|
+
const laneMap = installedLanes();
|
|
165
|
+
const installed = Object.keys(laneMap).filter((l) => LANES[l]);
|
|
166
|
+
|
|
167
|
+
if (!installed.length) {
|
|
168
|
+
console.log("\nNo weeve lanes installed. Run 'weeve add <lane>' first.\n");
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(`\nUpdating ${installed.length} lane(s)...\n`);
|
|
173
|
+
for (const lane of installed) {
|
|
174
|
+
const repo = `${ORG}/weeve-${lane}`;
|
|
175
|
+
console.log(` ↑ ${lane}`);
|
|
176
|
+
runGlobal(`npx skills add ${repo}`);
|
|
177
|
+
}
|
|
178
|
+
runGlobal(`npx skills add ${ORG}/weeve`);
|
|
179
|
+
console.log(`\nDone. All lanes updated to latest.\n`);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
case "remove":
|
|
184
|
+
case "uninstall": {
|
|
185
|
+
if (!args.length) {
|
|
186
|
+
console.error("Usage: weeve remove <lane|preset> [lane2...]");
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let lanes;
|
|
191
|
+
if (args.length === 1 && PRESETS[args[0]]) {
|
|
192
|
+
const preset = args[0];
|
|
193
|
+
console.log(`Removing preset: ${preset} (${PRESETS[preset].join(", ")})`);
|
|
194
|
+
lanes = PRESETS[preset];
|
|
195
|
+
} else {
|
|
196
|
+
const invalid = args.filter((l) => !LANES[l]);
|
|
197
|
+
if (invalid.length) {
|
|
198
|
+
console.error(`Unknown lane(s): ${invalid.join(", ")}`);
|
|
199
|
+
console.error(`Run 'weeve list' to see available lanes.`);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
lanes = args;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(`\nRemoving ${lanes.length} lane(s)...\n`);
|
|
206
|
+
for (const lane of lanes) {
|
|
207
|
+
const repo = `${ORG}/weeve-${lane}`;
|
|
208
|
+
console.log(` - ${lane}`);
|
|
209
|
+
runGlobal(`npx skills remove ${repo}`);
|
|
210
|
+
}
|
|
211
|
+
console.log(`\nDone. Removed: ${lanes.join(", ")}\n`);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
case "setup": {
|
|
216
|
+
const configDir = path.join(os.homedir(), ".config", "weeve");
|
|
217
|
+
const configPath = path.join(configDir, "config.yaml");
|
|
218
|
+
|
|
219
|
+
if (fs.existsSync(configPath)) {
|
|
220
|
+
console.log(`\nConfig already exists at ${configPath}\n`);
|
|
221
|
+
console.log(fs.readFileSync(configPath, "utf8"));
|
|
222
|
+
console.log("Delete it and re-run 'weeve setup' to reconfigure.\n");
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Auto-detect common vault locations
|
|
227
|
+
const vaultCandidates = [
|
|
228
|
+
path.join(os.homedir(), "Library/Mobile Documents/iCloud~md~obsidian/Documents"),
|
|
229
|
+
path.join(os.homedir(), "Documents/Obsidian"),
|
|
230
|
+
path.join(os.homedir(), "Obsidian"),
|
|
231
|
+
path.join(os.homedir(), "vault"),
|
|
232
|
+
];
|
|
233
|
+
const detectedVault = vaultCandidates.find((p) => fs.existsSync(p)) || "";
|
|
234
|
+
|
|
235
|
+
// Auto-detect common repos root
|
|
236
|
+
const reposCandidates = [
|
|
237
|
+
path.join(os.homedir(), "GitHub"),
|
|
238
|
+
path.join(os.homedir(), "code"),
|
|
239
|
+
path.join(os.homedir(), "projects"),
|
|
240
|
+
path.join(os.homedir(), "dev"),
|
|
241
|
+
];
|
|
242
|
+
const detectedRepos = reposCandidates.find((p) => fs.existsSync(p)) || path.join(os.homedir(), "GitHub");
|
|
243
|
+
|
|
244
|
+
const readline = require("readline");
|
|
245
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
246
|
+
|
|
247
|
+
const vaultPrompt = detectedVault
|
|
248
|
+
? `Vault path [${detectedVault}]: `
|
|
249
|
+
: `Vault path (Obsidian vault root): `;
|
|
250
|
+
|
|
251
|
+
rl.question(vaultPrompt, (vaultAnswer) => {
|
|
252
|
+
const vaultPath = vaultAnswer.trim() || detectedVault;
|
|
253
|
+
if (!vaultPath) {
|
|
254
|
+
console.error("Vault path is required.");
|
|
255
|
+
rl.close();
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
rl.question(`Repos root [${detectedRepos}]: `, (reposAnswer) => {
|
|
260
|
+
const reposRoot = reposAnswer.trim() || detectedRepos;
|
|
261
|
+
rl.close();
|
|
262
|
+
|
|
263
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
264
|
+
fs.writeFileSync(configPath, `vault_path: "${vaultPath}"\nrepos_root: "${reposRoot}"\n`);
|
|
265
|
+
console.log(`\nConfig written to ${configPath}`);
|
|
266
|
+
console.log(` vault_path ${vaultPath}`);
|
|
267
|
+
console.log(` repos_root ${reposRoot}`);
|
|
268
|
+
console.log(`\nRun 'weeve add all' to install lanes.\n`);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
|
|
123
274
|
case "init": {
|
|
124
275
|
const presetArg = args[0];
|
|
125
276
|
|
|
126
|
-
// Resolve lanes to scaffold
|
|
127
277
|
let lanes;
|
|
128
278
|
if (!presetArg) {
|
|
129
279
|
lanes = Object.keys(LANES);
|
|
@@ -140,7 +290,6 @@ switch (cmd) {
|
|
|
140
290
|
const cwd = process.cwd();
|
|
141
291
|
const yamlPath = path.join(cwd, "weeve.yaml");
|
|
142
292
|
|
|
143
|
-
// Bail if weeve.yaml already exists
|
|
144
293
|
if (fs.existsSync(yamlPath)) {
|
|
145
294
|
console.error(`Warning: weeve.yaml already exists in ${cwd}. Exiting without changes.`);
|
|
146
295
|
process.exit(1);
|
|
@@ -154,7 +303,6 @@ switch (cmd) {
|
|
|
154
303
|
}
|
|
155
304
|
|
|
156
305
|
// 1b. Create loom/ content-type folders based on installed lanes, plus shared/ and archive/
|
|
157
|
-
// Loom is organised by content type, not lane — multiple lanes share the same folder.
|
|
158
306
|
const loomFolders = new Set(["shared"]);
|
|
159
307
|
const LOOM_MAP = {
|
|
160
308
|
strategy: ["compass"],
|
|
@@ -301,39 +449,10 @@ switch (cmd) {
|
|
|
301
449
|
}
|
|
302
450
|
|
|
303
451
|
case "status": {
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
if (!fs.existsSync(lockPath)) {
|
|
307
|
-
console.log("No weeve lanes installed. Run 'weeve add <lane>' to get started.");
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
let lock;
|
|
312
|
-
try {
|
|
313
|
-
lock = JSON.parse(fs.readFileSync(lockPath, "utf8"));
|
|
314
|
-
} catch {
|
|
315
|
-
console.error(`Could not parse ${lockPath}`);
|
|
316
|
-
process.exit(1);
|
|
317
|
-
}
|
|
452
|
+
const laneMap = installedLanes();
|
|
318
453
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
// Group skills by weeve lane (source matches keighleykodric/weeve-<lane>)
|
|
322
|
-
const laneMap = {};
|
|
323
|
-
for (const [skillName, skillData] of Object.entries(skills)) {
|
|
324
|
-
const source = skillData.source || "";
|
|
325
|
-
const match = source.match(/^keighleykodric\/weeve-(.+)$/);
|
|
326
|
-
if (match) {
|
|
327
|
-
const lane = match[1];
|
|
328
|
-
if (!laneMap[lane]) {
|
|
329
|
-
laneMap[lane] = { count: 0, source };
|
|
330
|
-
}
|
|
331
|
-
laneMap[lane].count++;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (Object.keys(laneMap).length === 0) {
|
|
336
|
-
console.log("No weeve lanes installed. Run 'weeve add <lane>' to get started.");
|
|
454
|
+
if (!Object.keys(laneMap).length) {
|
|
455
|
+
console.log("\nNo weeve lanes installed. Run 'weeve add <lane>' to get started.\n");
|
|
337
456
|
break;
|
|
338
457
|
}
|
|
339
458
|
|
|
@@ -356,39 +475,6 @@ switch (cmd) {
|
|
|
356
475
|
break;
|
|
357
476
|
}
|
|
358
477
|
|
|
359
|
-
case "remove":
|
|
360
|
-
case "uninstall": {
|
|
361
|
-
if (!args.length) {
|
|
362
|
-
console.error("Usage: weeve remove <lane|preset> [lane2...]");
|
|
363
|
-
process.exit(1);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Expand preset or validate individual lanes
|
|
367
|
-
let lanes;
|
|
368
|
-
if (args.length === 1 && PRESETS[args[0]]) {
|
|
369
|
-
const preset = args[0];
|
|
370
|
-
console.log(`Removing preset: ${preset} (${PRESETS[preset].join(", ")})`);
|
|
371
|
-
lanes = PRESETS[preset];
|
|
372
|
-
} else {
|
|
373
|
-
const invalid = args.filter((l) => !LANES[l]);
|
|
374
|
-
if (invalid.length) {
|
|
375
|
-
console.error(`Unknown lane(s): ${invalid.join(", ")}`);
|
|
376
|
-
console.error(`Run 'weeve list' to see available lanes.`);
|
|
377
|
-
process.exit(1);
|
|
378
|
-
}
|
|
379
|
-
lanes = args;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
console.log(`\nRemoving ${lanes.length} lane(s)...\n`);
|
|
383
|
-
for (const lane of lanes) {
|
|
384
|
-
const repo = `${ORG}/weeve-${lane}`;
|
|
385
|
-
console.log(` - ${lane}`);
|
|
386
|
-
run(`npx skills remove ${repo}`);
|
|
387
|
-
}
|
|
388
|
-
console.log(`\nDone. Removed: ${lanes.join(", ")}\n`);
|
|
389
|
-
break;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
478
|
case "demo": {
|
|
393
479
|
const presetArg = args[0];
|
|
394
480
|
|
|
@@ -430,7 +516,6 @@ switch (cmd) {
|
|
|
430
516
|
process.exit(1);
|
|
431
517
|
}
|
|
432
518
|
|
|
433
|
-
// Recursively copy demo folder to cwd
|
|
434
519
|
function copyDir(src, dest) {
|
|
435
520
|
fs.mkdirSync(dest, { recursive: true });
|
|
436
521
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
@@ -452,7 +537,7 @@ switch (cmd) {
|
|
|
452
537
|
const firstCmd = firstLane && firstLane !== "pilot" ? `/${firstLane}-intro` : `/pilot-intro`;
|
|
453
538
|
|
|
454
539
|
console.log(`\nDemo ready: ${demoName} (${presetArg} preset)\n`);
|
|
455
|
-
console.log(` weeve.yaml project config
|
|
540
|
+
console.log(` weeve.yaml project config`);
|
|
456
541
|
console.log(` docs/shared/ weeve-project.md, weeve-guardrails.md, weeve-intake.md\n`);
|
|
457
542
|
console.log(`Next steps:`);
|
|
458
543
|
console.log(` 1. weeve add ${presetArg} — install the lanes`);
|
|
@@ -464,7 +549,6 @@ switch (cmd) {
|
|
|
464
549
|
const subcmd = args[0];
|
|
465
550
|
|
|
466
551
|
if (!subcmd || subcmd === "show") {
|
|
467
|
-
// Walk up from cwd to find weeve.yaml
|
|
468
552
|
let dir = process.cwd();
|
|
469
553
|
let found = null;
|
|
470
554
|
for (let i = 0; i < 4; i++) {
|
|
@@ -487,7 +571,6 @@ switch (cmd) {
|
|
|
487
571
|
const raw = fs.readFileSync(found, "utf8");
|
|
488
572
|
console.log(`\nProject config: ${found}\n`);
|
|
489
573
|
|
|
490
|
-
// Parse and display key fields
|
|
491
574
|
const nameMatch = raw.match(/^\s{2}name:\s*(.+)$/m);
|
|
492
575
|
const tagMatch = raw.match(/^\s{2}tag:\s*(.+)$/m);
|
|
493
576
|
const docsMatch = raw.match(/^\s{2}docs_root:\s*(.+)$/m);
|
|
@@ -495,22 +578,35 @@ switch (cmd) {
|
|
|
495
578
|
|
|
496
579
|
const name = nameMatch ? nameMatch[1].trim() : "(not set)";
|
|
497
580
|
const tag = tagMatch ? tagMatch[1].trim() : "(not set)";
|
|
498
|
-
const
|
|
499
|
-
|
|
581
|
+
const schemaVersion = schemaMatch ? schemaMatch[1].trim() : "(not set)";
|
|
582
|
+
|
|
583
|
+
// Resolve docs_root: explicit in weeve.yaml > weeve config > default
|
|
584
|
+
let docsRoot;
|
|
585
|
+
if (docsMatch) {
|
|
586
|
+
docsRoot = docsMatch[1].trim();
|
|
587
|
+
} else {
|
|
588
|
+
const weeveConfig = path.join(os.homedir(), ".config", "weeve", "config.yaml");
|
|
589
|
+
if (fs.existsSync(weeveConfig)) {
|
|
590
|
+
const cfgRaw = fs.readFileSync(weeveConfig, "utf8");
|
|
591
|
+
const vaultMatch = cfgRaw.match(/^vault_path:\s*"?(.+?)"?\s*$/m);
|
|
592
|
+
const vault = vaultMatch ? vaultMatch[1] : null;
|
|
593
|
+
docsRoot = vault ? `${vault}/Projects/${tag}/weeve` : `<vault>/Projects/${tag}/weeve`;
|
|
594
|
+
} else {
|
|
595
|
+
docsRoot = `<vault>/Projects/${tag}/weeve (run 'weeve setup' to configure)`;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
500
598
|
|
|
501
599
|
const maxLabel = 14;
|
|
502
600
|
console.log(` ${"name".padEnd(maxLabel)}${name}`);
|
|
503
601
|
console.log(` ${"tag".padEnd(maxLabel)}${tag}`);
|
|
504
602
|
console.log(` ${"docs_root".padEnd(maxLabel)}${docsRoot}`);
|
|
505
|
-
console.log(` ${"schema_version".padEnd(maxLabel)}${
|
|
603
|
+
console.log(` ${"schema_version".padEnd(maxLabel)}${schemaVersion}`);
|
|
506
604
|
|
|
507
|
-
// Check for repos
|
|
508
605
|
const reposBlock = raw.match(/^\s{2}repos:\n((?:\s{4}.+\n?)*)/m);
|
|
509
606
|
if (reposBlock) {
|
|
510
607
|
console.log(` ${"repos".padEnd(maxLabel)}${reposBlock[1].trim().split("\n").length} registered`);
|
|
511
608
|
}
|
|
512
609
|
|
|
513
|
-
// Check for depends_on
|
|
514
610
|
const depsBlock = raw.match(/^\s{2}depends_on:\n((?:\s{4}.+\n?)*)/m);
|
|
515
611
|
if (depsBlock) {
|
|
516
612
|
const depTags = [...depsBlock[1].matchAll(/tag:\s*(\S+)/g)].map((m) => m[1]);
|
|
@@ -519,13 +615,6 @@ switch (cmd) {
|
|
|
519
615
|
console.log(` ${"depends_on".padEnd(maxLabel)}${depList}`);
|
|
520
616
|
}
|
|
521
617
|
|
|
522
|
-
// Check for shared docs
|
|
523
|
-
const sharedDir = path.join(path.dirname(found), "docs", "shared");
|
|
524
|
-
if (fs.existsSync(sharedDir)) {
|
|
525
|
-
const sharedFiles = fs.readdirSync(sharedDir).filter((f) => f.endsWith(".md"));
|
|
526
|
-
console.log(` ${"shared/".padEnd(maxLabel)}${sharedFiles.join(", ")}`);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
618
|
console.log();
|
|
530
619
|
break;
|
|
531
620
|
}
|
|
@@ -570,8 +659,10 @@ switch (cmd) {
|
|
|
570
659
|
Cross-functional contract framework
|
|
571
660
|
|
|
572
661
|
Commands:
|
|
662
|
+
weeve setup Configure weeve (vault path, repos root)
|
|
573
663
|
weeve add <lane> [lane2...] Install one or more lanes
|
|
574
664
|
weeve add <preset> Install a preset group of lanes
|
|
665
|
+
weeve update Update all installed lanes to latest
|
|
575
666
|
weeve remove <lane> [lane2…] Remove one or more lanes
|
|
576
667
|
weeve remove <preset> Remove a preset group of lanes
|
|
577
668
|
weeve init [preset] Scaffold weeve structure in current directory
|
|
@@ -582,9 +673,11 @@ Commands:
|
|
|
582
673
|
weeve presets Show preset groups
|
|
583
674
|
|
|
584
675
|
Examples:
|
|
676
|
+
weeve setup First-time setup — configure vault and repos root
|
|
585
677
|
weeve add compass ally pilot Install three lanes
|
|
586
678
|
weeve add core Install core preset (compass, layers-plus, ally, rubric, pilot)
|
|
587
679
|
weeve add all Install everything
|
|
680
|
+
weeve update Pull latest versions of all installed lanes
|
|
588
681
|
weeve init core Scaffold folder structure for the core preset
|
|
589
682
|
weeve demo core Copy the Prism demo project (ready to run immediately)
|
|
590
683
|
weeve status See which lanes are installed
|