@keighleykodric/weeve 0.1.2 → 0.1.4
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 +169 -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,34 @@ 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
|
+
const SKILLS_DIR = path.join(os.homedir(), ".agents", "skills");
|
|
86
|
+
|
|
87
|
+
// Return installed weeve lanes by scanning ~/.agents/skills/ for known lane dirs
|
|
88
|
+
function installedLanes() {
|
|
89
|
+
if (!fs.existsSync(SKILLS_DIR)) return {};
|
|
90
|
+
const entries = new Set(fs.readdirSync(SKILLS_DIR));
|
|
91
|
+
const laneMap = {};
|
|
92
|
+
for (const lane of Object.keys(LANES)) {
|
|
93
|
+
if (entries.has(lane)) {
|
|
94
|
+
// Count all skill dirs that start with this lane name
|
|
95
|
+
const count = [...entries].filter(
|
|
96
|
+
(e) => e === lane || e.startsWith(lane + "-")
|
|
97
|
+
).length;
|
|
98
|
+
laneMap[lane] = { count, source: `keighleykodric/weeve-${lane}` };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return laneMap;
|
|
102
|
+
}
|
|
103
|
+
|
|
75
104
|
function installLanes(lanes) {
|
|
76
105
|
const invalid = lanes.filter((l) => !LANES[l]);
|
|
77
106
|
if (invalid.length) {
|
|
@@ -84,10 +113,10 @@ function installLanes(lanes) {
|
|
|
84
113
|
for (const lane of lanes) {
|
|
85
114
|
const repo = `${ORG}/weeve-${lane}`;
|
|
86
115
|
console.log(` + ${lane} — ${LANES[lane]}`);
|
|
87
|
-
|
|
116
|
+
runGlobal(`npx skills add ${repo}`);
|
|
88
117
|
}
|
|
89
118
|
// Always install framework skills from the weeve repo itself
|
|
90
|
-
|
|
119
|
+
runGlobal(`npx skills add ${ORG}/weeve`);
|
|
91
120
|
// Show the right first step based on what was installed
|
|
92
121
|
const firstLane = FIRST_STEP_ORDER.find((l) => lanes.includes(l));
|
|
93
122
|
if (firstLane && firstLane !== "pilot") {
|
|
@@ -109,7 +138,6 @@ switch (cmd) {
|
|
|
109
138
|
process.exit(1);
|
|
110
139
|
}
|
|
111
140
|
|
|
112
|
-
// Check if first arg is a preset
|
|
113
141
|
if (args.length === 1 && PRESETS[args[0]]) {
|
|
114
142
|
const preset = args[0];
|
|
115
143
|
console.log(`Preset: ${preset} (${PRESETS[preset].join(", ")})`);
|
|
@@ -120,10 +148,121 @@ switch (cmd) {
|
|
|
120
148
|
break;
|
|
121
149
|
}
|
|
122
150
|
|
|
151
|
+
case "update": {
|
|
152
|
+
// Re-install all currently installed lanes to pull the latest skill versions
|
|
153
|
+
const laneMap = installedLanes();
|
|
154
|
+
const installed = Object.keys(laneMap).filter((l) => LANES[l]);
|
|
155
|
+
|
|
156
|
+
if (!installed.length) {
|
|
157
|
+
console.log("\nNo weeve lanes installed. Run 'weeve add <lane>' first.\n");
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
console.log(`\nUpdating ${installed.length} lane(s)...\n`);
|
|
162
|
+
for (const lane of installed) {
|
|
163
|
+
const repo = `${ORG}/weeve-${lane}`;
|
|
164
|
+
console.log(` ↑ ${lane}`);
|
|
165
|
+
runGlobal(`npx skills add ${repo}`);
|
|
166
|
+
}
|
|
167
|
+
runGlobal(`npx skills add ${ORG}/weeve`);
|
|
168
|
+
console.log(`\nDone. All lanes updated to latest.\n`);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
case "remove":
|
|
173
|
+
case "uninstall": {
|
|
174
|
+
if (!args.length) {
|
|
175
|
+
console.error("Usage: weeve remove <lane|preset> [lane2...]");
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let lanes;
|
|
180
|
+
if (args.length === 1 && PRESETS[args[0]]) {
|
|
181
|
+
const preset = args[0];
|
|
182
|
+
console.log(`Removing preset: ${preset} (${PRESETS[preset].join(", ")})`);
|
|
183
|
+
lanes = PRESETS[preset];
|
|
184
|
+
} else {
|
|
185
|
+
const invalid = args.filter((l) => !LANES[l]);
|
|
186
|
+
if (invalid.length) {
|
|
187
|
+
console.error(`Unknown lane(s): ${invalid.join(", ")}`);
|
|
188
|
+
console.error(`Run 'weeve list' to see available lanes.`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
lanes = args;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(`\nRemoving ${lanes.length} lane(s)...\n`);
|
|
195
|
+
for (const lane of lanes) {
|
|
196
|
+
const repo = `${ORG}/weeve-${lane}`;
|
|
197
|
+
console.log(` - ${lane}`);
|
|
198
|
+
runGlobal(`npx skills remove ${repo}`);
|
|
199
|
+
}
|
|
200
|
+
console.log(`\nDone. Removed: ${lanes.join(", ")}\n`);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
case "setup": {
|
|
205
|
+
const configDir = path.join(os.homedir(), ".config", "weeve");
|
|
206
|
+
const configPath = path.join(configDir, "config.yaml");
|
|
207
|
+
|
|
208
|
+
if (fs.existsSync(configPath)) {
|
|
209
|
+
console.log(`\nConfig already exists at ${configPath}\n`);
|
|
210
|
+
console.log(fs.readFileSync(configPath, "utf8"));
|
|
211
|
+
console.log("Delete it and re-run 'weeve setup' to reconfigure.\n");
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Auto-detect common vault locations
|
|
216
|
+
const vaultCandidates = [
|
|
217
|
+
path.join(os.homedir(), "Library/Mobile Documents/iCloud~md~obsidian/Documents"),
|
|
218
|
+
path.join(os.homedir(), "Documents/Obsidian"),
|
|
219
|
+
path.join(os.homedir(), "Obsidian"),
|
|
220
|
+
path.join(os.homedir(), "vault"),
|
|
221
|
+
];
|
|
222
|
+
const detectedVault = vaultCandidates.find((p) => fs.existsSync(p)) || "";
|
|
223
|
+
|
|
224
|
+
// Auto-detect common repos root
|
|
225
|
+
const reposCandidates = [
|
|
226
|
+
path.join(os.homedir(), "GitHub"),
|
|
227
|
+
path.join(os.homedir(), "code"),
|
|
228
|
+
path.join(os.homedir(), "projects"),
|
|
229
|
+
path.join(os.homedir(), "dev"),
|
|
230
|
+
];
|
|
231
|
+
const detectedRepos = reposCandidates.find((p) => fs.existsSync(p)) || path.join(os.homedir(), "GitHub");
|
|
232
|
+
|
|
233
|
+
const readline = require("readline");
|
|
234
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
235
|
+
|
|
236
|
+
const vaultPrompt = detectedVault
|
|
237
|
+
? `Vault path [${detectedVault}]: `
|
|
238
|
+
: `Vault path (Obsidian vault root): `;
|
|
239
|
+
|
|
240
|
+
rl.question(vaultPrompt, (vaultAnswer) => {
|
|
241
|
+
const vaultPath = vaultAnswer.trim() || detectedVault;
|
|
242
|
+
if (!vaultPath) {
|
|
243
|
+
console.error("Vault path is required.");
|
|
244
|
+
rl.close();
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
rl.question(`Repos root [${detectedRepos}]: `, (reposAnswer) => {
|
|
249
|
+
const reposRoot = reposAnswer.trim() || detectedRepos;
|
|
250
|
+
rl.close();
|
|
251
|
+
|
|
252
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
253
|
+
fs.writeFileSync(configPath, `vault_path: "${vaultPath}"\nrepos_root: "${reposRoot}"\n`);
|
|
254
|
+
console.log(`\nConfig written to ${configPath}`);
|
|
255
|
+
console.log(` vault_path ${vaultPath}`);
|
|
256
|
+
console.log(` repos_root ${reposRoot}`);
|
|
257
|
+
console.log(`\nRun 'weeve add all' to install lanes.\n`);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
|
|
123
263
|
case "init": {
|
|
124
264
|
const presetArg = args[0];
|
|
125
265
|
|
|
126
|
-
// Resolve lanes to scaffold
|
|
127
266
|
let lanes;
|
|
128
267
|
if (!presetArg) {
|
|
129
268
|
lanes = Object.keys(LANES);
|
|
@@ -140,7 +279,6 @@ switch (cmd) {
|
|
|
140
279
|
const cwd = process.cwd();
|
|
141
280
|
const yamlPath = path.join(cwd, "weeve.yaml");
|
|
142
281
|
|
|
143
|
-
// Bail if weeve.yaml already exists
|
|
144
282
|
if (fs.existsSync(yamlPath)) {
|
|
145
283
|
console.error(`Warning: weeve.yaml already exists in ${cwd}. Exiting without changes.`);
|
|
146
284
|
process.exit(1);
|
|
@@ -154,7 +292,6 @@ switch (cmd) {
|
|
|
154
292
|
}
|
|
155
293
|
|
|
156
294
|
// 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
295
|
const loomFolders = new Set(["shared"]);
|
|
159
296
|
const LOOM_MAP = {
|
|
160
297
|
strategy: ["compass"],
|
|
@@ -301,39 +438,10 @@ switch (cmd) {
|
|
|
301
438
|
}
|
|
302
439
|
|
|
303
440
|
case "status": {
|
|
304
|
-
const
|
|
441
|
+
const laneMap = installedLanes();
|
|
305
442
|
|
|
306
|
-
if (!
|
|
307
|
-
console.log("
|
|
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
|
-
}
|
|
318
|
-
|
|
319
|
-
const skills = lock.skills || {};
|
|
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.");
|
|
443
|
+
if (!Object.keys(laneMap).length) {
|
|
444
|
+
console.log("\nNo weeve lanes installed. Run 'weeve add <lane>' to get started.\n");
|
|
337
445
|
break;
|
|
338
446
|
}
|
|
339
447
|
|
|
@@ -356,39 +464,6 @@ switch (cmd) {
|
|
|
356
464
|
break;
|
|
357
465
|
}
|
|
358
466
|
|
|
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
467
|
case "demo": {
|
|
393
468
|
const presetArg = args[0];
|
|
394
469
|
|
|
@@ -430,7 +505,6 @@ switch (cmd) {
|
|
|
430
505
|
process.exit(1);
|
|
431
506
|
}
|
|
432
507
|
|
|
433
|
-
// Recursively copy demo folder to cwd
|
|
434
508
|
function copyDir(src, dest) {
|
|
435
509
|
fs.mkdirSync(dest, { recursive: true });
|
|
436
510
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
@@ -452,7 +526,7 @@ switch (cmd) {
|
|
|
452
526
|
const firstCmd = firstLane && firstLane !== "pilot" ? `/${firstLane}-intro` : `/pilot-intro`;
|
|
453
527
|
|
|
454
528
|
console.log(`\nDemo ready: ${demoName} (${presetArg} preset)\n`);
|
|
455
|
-
console.log(` weeve.yaml project config
|
|
529
|
+
console.log(` weeve.yaml project config`);
|
|
456
530
|
console.log(` docs/shared/ weeve-project.md, weeve-guardrails.md, weeve-intake.md\n`);
|
|
457
531
|
console.log(`Next steps:`);
|
|
458
532
|
console.log(` 1. weeve add ${presetArg} — install the lanes`);
|
|
@@ -464,7 +538,6 @@ switch (cmd) {
|
|
|
464
538
|
const subcmd = args[0];
|
|
465
539
|
|
|
466
540
|
if (!subcmd || subcmd === "show") {
|
|
467
|
-
// Walk up from cwd to find weeve.yaml
|
|
468
541
|
let dir = process.cwd();
|
|
469
542
|
let found = null;
|
|
470
543
|
for (let i = 0; i < 4; i++) {
|
|
@@ -487,7 +560,6 @@ switch (cmd) {
|
|
|
487
560
|
const raw = fs.readFileSync(found, "utf8");
|
|
488
561
|
console.log(`\nProject config: ${found}\n`);
|
|
489
562
|
|
|
490
|
-
// Parse and display key fields
|
|
491
563
|
const nameMatch = raw.match(/^\s{2}name:\s*(.+)$/m);
|
|
492
564
|
const tagMatch = raw.match(/^\s{2}tag:\s*(.+)$/m);
|
|
493
565
|
const docsMatch = raw.match(/^\s{2}docs_root:\s*(.+)$/m);
|
|
@@ -495,22 +567,35 @@ switch (cmd) {
|
|
|
495
567
|
|
|
496
568
|
const name = nameMatch ? nameMatch[1].trim() : "(not set)";
|
|
497
569
|
const tag = tagMatch ? tagMatch[1].trim() : "(not set)";
|
|
498
|
-
const
|
|
499
|
-
|
|
570
|
+
const schemaVersion = schemaMatch ? schemaMatch[1].trim() : "(not set)";
|
|
571
|
+
|
|
572
|
+
// Resolve docs_root: explicit in weeve.yaml > weeve config > default
|
|
573
|
+
let docsRoot;
|
|
574
|
+
if (docsMatch) {
|
|
575
|
+
docsRoot = docsMatch[1].trim();
|
|
576
|
+
} else {
|
|
577
|
+
const weeveConfig = path.join(os.homedir(), ".config", "weeve", "config.yaml");
|
|
578
|
+
if (fs.existsSync(weeveConfig)) {
|
|
579
|
+
const cfgRaw = fs.readFileSync(weeveConfig, "utf8");
|
|
580
|
+
const vaultMatch = cfgRaw.match(/^vault_path:\s*"?(.+?)"?\s*$/m);
|
|
581
|
+
const vault = vaultMatch ? vaultMatch[1] : null;
|
|
582
|
+
docsRoot = vault ? `${vault}/Projects/${tag}/weeve` : `<vault>/Projects/${tag}/weeve`;
|
|
583
|
+
} else {
|
|
584
|
+
docsRoot = `<vault>/Projects/${tag}/weeve (run 'weeve setup' to configure)`;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
500
587
|
|
|
501
588
|
const maxLabel = 14;
|
|
502
589
|
console.log(` ${"name".padEnd(maxLabel)}${name}`);
|
|
503
590
|
console.log(` ${"tag".padEnd(maxLabel)}${tag}`);
|
|
504
591
|
console.log(` ${"docs_root".padEnd(maxLabel)}${docsRoot}`);
|
|
505
|
-
console.log(` ${"schema_version".padEnd(maxLabel)}${
|
|
592
|
+
console.log(` ${"schema_version".padEnd(maxLabel)}${schemaVersion}`);
|
|
506
593
|
|
|
507
|
-
// Check for repos
|
|
508
594
|
const reposBlock = raw.match(/^\s{2}repos:\n((?:\s{4}.+\n?)*)/m);
|
|
509
595
|
if (reposBlock) {
|
|
510
596
|
console.log(` ${"repos".padEnd(maxLabel)}${reposBlock[1].trim().split("\n").length} registered`);
|
|
511
597
|
}
|
|
512
598
|
|
|
513
|
-
// Check for depends_on
|
|
514
599
|
const depsBlock = raw.match(/^\s{2}depends_on:\n((?:\s{4}.+\n?)*)/m);
|
|
515
600
|
if (depsBlock) {
|
|
516
601
|
const depTags = [...depsBlock[1].matchAll(/tag:\s*(\S+)/g)].map((m) => m[1]);
|
|
@@ -519,13 +604,6 @@ switch (cmd) {
|
|
|
519
604
|
console.log(` ${"depends_on".padEnd(maxLabel)}${depList}`);
|
|
520
605
|
}
|
|
521
606
|
|
|
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
607
|
console.log();
|
|
530
608
|
break;
|
|
531
609
|
}
|
|
@@ -570,8 +648,10 @@ switch (cmd) {
|
|
|
570
648
|
Cross-functional contract framework
|
|
571
649
|
|
|
572
650
|
Commands:
|
|
651
|
+
weeve setup Configure weeve (vault path, repos root)
|
|
573
652
|
weeve add <lane> [lane2...] Install one or more lanes
|
|
574
653
|
weeve add <preset> Install a preset group of lanes
|
|
654
|
+
weeve update Update all installed lanes to latest
|
|
575
655
|
weeve remove <lane> [lane2…] Remove one or more lanes
|
|
576
656
|
weeve remove <preset> Remove a preset group of lanes
|
|
577
657
|
weeve init [preset] Scaffold weeve structure in current directory
|
|
@@ -582,9 +662,11 @@ Commands:
|
|
|
582
662
|
weeve presets Show preset groups
|
|
583
663
|
|
|
584
664
|
Examples:
|
|
665
|
+
weeve setup First-time setup — configure vault and repos root
|
|
585
666
|
weeve add compass ally pilot Install three lanes
|
|
586
667
|
weeve add core Install core preset (compass, layers-plus, ally, rubric, pilot)
|
|
587
668
|
weeve add all Install everything
|
|
669
|
+
weeve update Pull latest versions of all installed lanes
|
|
588
670
|
weeve init core Scaffold folder structure for the core preset
|
|
589
671
|
weeve demo core Copy the Prism demo project (ready to run immediately)
|
|
590
672
|
weeve status See which lanes are installed
|