@weave-tools/weave-it 0.1.1 → 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/README.md +39 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +525 -213
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/skills/weave-architect/SKILL.md +17 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command11 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/add.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -18,60 +18,60 @@ import { realpath } from "fs/promises";
|
|
|
18
18
|
import { constants } from "fs";
|
|
19
19
|
import { access, mkdir, readFile, readdir, rename, stat, writeFile } from "fs/promises";
|
|
20
20
|
import { dirname } from "path";
|
|
21
|
-
async function pathExists(
|
|
21
|
+
async function pathExists(path16) {
|
|
22
22
|
try {
|
|
23
|
-
await access(
|
|
23
|
+
await access(path16, constants.F_OK);
|
|
24
24
|
return true;
|
|
25
25
|
} catch {
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
async function writeNewFile(
|
|
30
|
-
await writeFile(
|
|
29
|
+
async function writeNewFile(path16, contents) {
|
|
30
|
+
await writeFile(path16, contents, { flag: "wx" });
|
|
31
31
|
}
|
|
32
|
-
async function writeFileIfMissing(
|
|
33
|
-
if (await pathExists(
|
|
32
|
+
async function writeFileIfMissing(path16, contents) {
|
|
33
|
+
if (await pathExists(path16)) {
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
|
-
await writeNewFile(
|
|
36
|
+
await writeNewFile(path16, contents);
|
|
37
37
|
return true;
|
|
38
38
|
}
|
|
39
|
-
async function writeFileAtomic(
|
|
40
|
-
const tempPath = `${
|
|
39
|
+
async function writeFileAtomic(path16, contents) {
|
|
40
|
+
const tempPath = `${path16}.${process.pid}.tmp`;
|
|
41
41
|
await writeFile(tempPath, contents);
|
|
42
|
-
await rename(tempPath,
|
|
42
|
+
await rename(tempPath, path16);
|
|
43
43
|
}
|
|
44
|
-
async function ensureDir(
|
|
45
|
-
await mkdir(
|
|
44
|
+
async function ensureDir(path16) {
|
|
45
|
+
await mkdir(path16, { recursive: true });
|
|
46
46
|
}
|
|
47
|
-
async function createDirExclusive(
|
|
48
|
-
await mkdir(
|
|
47
|
+
async function createDirExclusive(path16) {
|
|
48
|
+
await mkdir(path16, { recursive: false });
|
|
49
49
|
}
|
|
50
|
-
async function ensureDirectory(
|
|
51
|
-
const value = await stat(
|
|
50
|
+
async function ensureDirectory(path16) {
|
|
51
|
+
const value = await stat(path16);
|
|
52
52
|
if (!value.isDirectory()) {
|
|
53
|
-
throw new Error(`Expected a directory: ${
|
|
53
|
+
throw new Error(`Expected a directory: ${path16}`);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
async function isDirectoryEmpty(
|
|
57
|
-
const entries = await readdir(
|
|
56
|
+
async function isDirectoryEmpty(path16) {
|
|
57
|
+
const entries = await readdir(path16);
|
|
58
58
|
return entries.length === 0;
|
|
59
59
|
}
|
|
60
60
|
async function movePath(source, target) {
|
|
61
61
|
await rename(source, target);
|
|
62
62
|
}
|
|
63
|
-
async function readJsonCache(
|
|
63
|
+
async function readJsonCache(path16) {
|
|
64
64
|
try {
|
|
65
|
-
const contents = await readFile(
|
|
65
|
+
const contents = await readFile(path16, "utf8");
|
|
66
66
|
return JSON.parse(contents);
|
|
67
67
|
} catch {
|
|
68
68
|
return null;
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
async function writeJsonCache(
|
|
71
|
+
async function writeJsonCache(path16, data) {
|
|
72
72
|
try {
|
|
73
|
-
await mkdir(dirname(
|
|
74
|
-
await writeFileAtomic(
|
|
73
|
+
await mkdir(dirname(path16), { recursive: true });
|
|
74
|
+
await writeFileAtomic(path16, JSON.stringify(data, null, 2));
|
|
75
75
|
return true;
|
|
76
76
|
} catch {
|
|
77
77
|
return false;
|
|
@@ -302,6 +302,7 @@ async function ensureWeaveScaffold(input) {
|
|
|
302
302
|
const knowledgeReadme = knowledgeReadmeTemplate();
|
|
303
303
|
const domainsReadme = domainsReadmeTemplate();
|
|
304
304
|
const sharedReadme = sharedReadmeTemplate();
|
|
305
|
+
const architectureConsiderations = architectureConsiderationsTemplate();
|
|
305
306
|
const knowledgeDir = path3.join(wikiDir, "knowledge");
|
|
306
307
|
const changesDir = path3.join(wikiDir, "changes");
|
|
307
308
|
const changesExisted = await pathExists(changesDir);
|
|
@@ -313,6 +314,9 @@ async function ensureWeaveScaffold(input) {
|
|
|
313
314
|
if (await writeFileIfMissing(path3.join(metadataDir, "sync.yml"), syncTemplate(knowledgeIndex))) {
|
|
314
315
|
created.push(".weave/sync.yml");
|
|
315
316
|
}
|
|
317
|
+
if (await writeFileIfMissing(path3.join(metadataDir, "architecture-considerations.md"), architectureConsiderations)) {
|
|
318
|
+
created.push(".weave/architecture-considerations.md");
|
|
319
|
+
}
|
|
316
320
|
if (await writeFileIfMissing(path3.join(knowledgeDir, "index.md"), knowledgeIndex)) {
|
|
317
321
|
created.push("wiki/knowledge/index.md");
|
|
318
322
|
}
|
|
@@ -358,6 +362,49 @@ Examples:
|
|
|
358
362
|
- Notifications
|
|
359
363
|
`;
|
|
360
364
|
}
|
|
365
|
+
function architectureConsiderationsTemplate() {
|
|
366
|
+
return `# Architecture Considerations
|
|
367
|
+
|
|
368
|
+
This file is user-owned. Weave creates it once and never overwrites it.
|
|
369
|
+
|
|
370
|
+
Use this file to capture team-specific architecture guidance that agents should keep in mind when discussing technical design.
|
|
371
|
+
|
|
372
|
+
## Design Principles
|
|
373
|
+
|
|
374
|
+
- _Add preferred design principles here._
|
|
375
|
+
|
|
376
|
+
## Patterns To Prefer
|
|
377
|
+
|
|
378
|
+
- _Example: Prefer service objects for cross-domain workflows._
|
|
379
|
+
|
|
380
|
+
## Patterns To Avoid
|
|
381
|
+
|
|
382
|
+
- _Example: Avoid callback-driven cross-domain side effects._
|
|
383
|
+
|
|
384
|
+
## Data Access And Scaling
|
|
385
|
+
|
|
386
|
+
- _Example: Watch for N+1 queries in list/detail views._
|
|
387
|
+
- _Example: Prefer batched reads for high-cardinality associations._
|
|
388
|
+
|
|
389
|
+
## Caching And Consistency
|
|
390
|
+
|
|
391
|
+
- _Add caching rules, invalidation expectations, or consistency constraints._
|
|
392
|
+
|
|
393
|
+
## Async Boundaries And Events
|
|
394
|
+
|
|
395
|
+
- _Add guidance for jobs, queues, events, callbacks, or synchronous flows._
|
|
396
|
+
|
|
397
|
+
## Observability And Operations
|
|
398
|
+
|
|
399
|
+
- _Add logging, metrics, alerting, rollout, and failure-mode expectations._
|
|
400
|
+
|
|
401
|
+
## Notes For Agents
|
|
402
|
+
|
|
403
|
+
- Apply relevant guidance silently.
|
|
404
|
+
- Surface only constraints, conflicts, or risks that materially affect the design.
|
|
405
|
+
- Do not treat examples as mandatory unless this file says they are.
|
|
406
|
+
`;
|
|
407
|
+
}
|
|
361
408
|
function knowledgeReadmeTemplate() {
|
|
362
409
|
return `# Knowledge Structure
|
|
363
410
|
|
|
@@ -1274,8 +1321,8 @@ function formatFullFileDiff(installedPath, defaultName, installed, currentDefaul
|
|
|
1274
1321
|
function splitLines(value) {
|
|
1275
1322
|
return value.endsWith("\n") ? value.slice(0, -1).split("\n") : value.split("\n");
|
|
1276
1323
|
}
|
|
1277
|
-
function result(agent, artifact,
|
|
1278
|
-
return { agent, kind: artifact.kind, skill: artifact.name, path:
|
|
1324
|
+
function result(agent, artifact, path16, status, message) {
|
|
1325
|
+
return { agent, kind: artifact.kind, skill: artifact.name, path: path16, status, message };
|
|
1279
1326
|
}
|
|
1280
1327
|
function summarize(results) {
|
|
1281
1328
|
return {
|
|
@@ -2991,9 +3038,9 @@ async function safeListDefaultSkills(templatesDir) {
|
|
|
2991
3038
|
return [];
|
|
2992
3039
|
}
|
|
2993
3040
|
}
|
|
2994
|
-
async function safeHashFile(
|
|
3041
|
+
async function safeHashFile(path16) {
|
|
2995
3042
|
try {
|
|
2996
|
-
const content = await readFile8(
|
|
3043
|
+
const content = await readFile8(path16, "utf8");
|
|
2997
3044
|
return `sha256:${createHash3("sha256").update(content).digest("hex")}`;
|
|
2998
3045
|
} catch {
|
|
2999
3046
|
return null;
|
|
@@ -3337,13 +3384,409 @@ function errorResult2(error) {
|
|
|
3337
3384
|
};
|
|
3338
3385
|
}
|
|
3339
3386
|
|
|
3340
|
-
// src/commands/
|
|
3387
|
+
// src/commands/doctor.ts
|
|
3341
3388
|
import { Command as Command5 } from "commander";
|
|
3342
3389
|
|
|
3390
|
+
// src/lib/doctor.ts
|
|
3391
|
+
import path10 from "path";
|
|
3392
|
+
|
|
3393
|
+
// src/lib/status.ts
|
|
3394
|
+
import { createHash as createHash4 } from "crypto";
|
|
3395
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
3396
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3397
|
+
import { dirname as dirname4, join as join5 } from "path";
|
|
3398
|
+
var agentOrder = ["claude", "codex", "cursor", "opencode"];
|
|
3399
|
+
async function buildStatus(options) {
|
|
3400
|
+
const packageVersion = options.packageVersion ?? await readPackageVersion2();
|
|
3401
|
+
const inRepo = await pathExists(join5(options.cwd, ".weave", "agents.yml"));
|
|
3402
|
+
const skills = inRepo ? await collectSkillRows(options) : [];
|
|
3403
|
+
const npmLatest = options.npmLatest === void 0 ? (await getNpmVersionInfo({ packageVersion, env: options.env })).cachedLatest : options.npmLatest;
|
|
3404
|
+
const notices = await gatherNotices({
|
|
3405
|
+
cwd: options.cwd,
|
|
3406
|
+
packageVersion,
|
|
3407
|
+
npmLatest: npmLatest ?? null,
|
|
3408
|
+
templatesDir: options.templatesDir,
|
|
3409
|
+
env: options.env
|
|
3410
|
+
});
|
|
3411
|
+
return {
|
|
3412
|
+
status: "ok",
|
|
3413
|
+
packageVersion,
|
|
3414
|
+
cwd: options.cwd,
|
|
3415
|
+
inRepo,
|
|
3416
|
+
skills,
|
|
3417
|
+
notices,
|
|
3418
|
+
message: renderStatusMessage({
|
|
3419
|
+
packageVersion,
|
|
3420
|
+
cwd: options.cwd,
|
|
3421
|
+
inRepo,
|
|
3422
|
+
skills,
|
|
3423
|
+
notices,
|
|
3424
|
+
npmLatest: npmLatest ?? null
|
|
3425
|
+
})
|
|
3426
|
+
};
|
|
3427
|
+
}
|
|
3428
|
+
async function collectSkillRows(options) {
|
|
3429
|
+
const manifest = await loadAgentsManifest(options.cwd);
|
|
3430
|
+
const templates = await listDefaultSkills({ templatesDir: options.templatesDir }).catch(() => []);
|
|
3431
|
+
const templatesByName = new Map(templates.map((skill) => [skill.name, skill]));
|
|
3432
|
+
const rows = [];
|
|
3433
|
+
for (const agent of agentOrder) {
|
|
3434
|
+
const buckets = manifest.installed[agent];
|
|
3435
|
+
if (!buckets) continue;
|
|
3436
|
+
const skills = buckets.skills ?? {};
|
|
3437
|
+
for (const [name, entry] of Object.entries(skills)) {
|
|
3438
|
+
const template = templatesByName.get(name);
|
|
3439
|
+
const current = template?.lastChangedIn ?? "unknown";
|
|
3440
|
+
const absolutePath = join5(options.cwd, entry.path);
|
|
3441
|
+
const fileExists = await pathExists(absolutePath);
|
|
3442
|
+
let state = "current";
|
|
3443
|
+
if (!fileExists) {
|
|
3444
|
+
state = "missing";
|
|
3445
|
+
} else {
|
|
3446
|
+
const diskContent = await readFile10(absolutePath, "utf8").catch(() => "");
|
|
3447
|
+
const diskHash = `sha256:${createHash4("sha256").update(diskContent).digest("hex")}`;
|
|
3448
|
+
if (diskHash !== entry.installed_hash) {
|
|
3449
|
+
state = "modified";
|
|
3450
|
+
} else if (template && entry.installed_from !== template.lastChangedIn) {
|
|
3451
|
+
state = "outdated";
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
rows.push({
|
|
3455
|
+
agent,
|
|
3456
|
+
name,
|
|
3457
|
+
installed_from: entry.installed_from,
|
|
3458
|
+
current,
|
|
3459
|
+
state
|
|
3460
|
+
});
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
return rows;
|
|
3464
|
+
}
|
|
3465
|
+
function renderStatusMessage(opts) {
|
|
3466
|
+
const lines = [];
|
|
3467
|
+
lines.push(`weave-it ${opts.packageVersion}`);
|
|
3468
|
+
lines.push(`cwd: ${opts.cwd}`);
|
|
3469
|
+
if (opts.npmLatest) {
|
|
3470
|
+
lines.push(`npm latest (cached): ${opts.npmLatest}`);
|
|
3471
|
+
} else {
|
|
3472
|
+
lines.push(`npm latest (cached): unknown`);
|
|
3473
|
+
}
|
|
3474
|
+
if (!opts.inRepo) {
|
|
3475
|
+
lines.push("");
|
|
3476
|
+
lines.push("Not inside a Weave-managed repo (no .weave/agents.yml).");
|
|
3477
|
+
} else if (opts.skills.length === 0) {
|
|
3478
|
+
lines.push("");
|
|
3479
|
+
lines.push("No skills installed.");
|
|
3480
|
+
} else {
|
|
3481
|
+
lines.push("");
|
|
3482
|
+
lines.push("Installed skills:");
|
|
3483
|
+
const header = ["agent", "skill", "state", "installed_from", "current"];
|
|
3484
|
+
const rows = opts.skills.map((row) => [
|
|
3485
|
+
row.agent,
|
|
3486
|
+
row.name,
|
|
3487
|
+
row.state,
|
|
3488
|
+
row.installed_from ?? "unknown",
|
|
3489
|
+
row.current
|
|
3490
|
+
]);
|
|
3491
|
+
const widths = header.map(
|
|
3492
|
+
(cell, index) => Math.max(cell.length, ...rows.map((row) => row[index].length))
|
|
3493
|
+
);
|
|
3494
|
+
const renderRow = (cells) => cells.map((cell, index) => cell.padEnd(widths[index])).join(" ").trimEnd();
|
|
3495
|
+
lines.push(renderRow(header));
|
|
3496
|
+
lines.push(widths.map((width) => "-".repeat(width)).join(" ").trimEnd());
|
|
3497
|
+
for (const row of rows) {
|
|
3498
|
+
lines.push(renderRow(row));
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
lines.push("");
|
|
3502
|
+
if (opts.notices.length === 0) {
|
|
3503
|
+
lines.push("Notices: none.");
|
|
3504
|
+
} else {
|
|
3505
|
+
lines.push(`Notices (${opts.notices.length}):`);
|
|
3506
|
+
for (const notice of opts.notices) {
|
|
3507
|
+
lines.push(`- [${notice.kind}] ${notice.message}`);
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
return lines.join("\n");
|
|
3511
|
+
}
|
|
3512
|
+
async function readPackageVersion2() {
|
|
3513
|
+
const moduleDir = dirname4(fileURLToPath3(import.meta.url));
|
|
3514
|
+
let current = moduleDir;
|
|
3515
|
+
while (true) {
|
|
3516
|
+
const candidate = join5(current, "package.json");
|
|
3517
|
+
if (await pathExists(candidate)) {
|
|
3518
|
+
try {
|
|
3519
|
+
const parsed = JSON.parse(await readFile10(candidate, "utf8"));
|
|
3520
|
+
if (typeof parsed?.version === "string") {
|
|
3521
|
+
return parsed.version;
|
|
3522
|
+
}
|
|
3523
|
+
} catch {
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
const parent = dirname4(current);
|
|
3527
|
+
if (parent === current) break;
|
|
3528
|
+
current = parent;
|
|
3529
|
+
}
|
|
3530
|
+
return "0.0.0";
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3533
|
+
// src/lib/doctor.ts
|
|
3534
|
+
var safeScaffoldFiles = [
|
|
3535
|
+
".weave/sync.yml",
|
|
3536
|
+
".weave/architecture-considerations.md",
|
|
3537
|
+
"wiki/knowledge/index.md",
|
|
3538
|
+
"wiki/knowledge/README.md",
|
|
3539
|
+
"wiki/knowledge/domains/README.md",
|
|
3540
|
+
"wiki/knowledge/shared/README.md"
|
|
3541
|
+
];
|
|
3542
|
+
var safeScaffoldDirs = [
|
|
3543
|
+
"wiki/changes",
|
|
3544
|
+
"wiki/knowledge/domains",
|
|
3545
|
+
"wiki/knowledge/shared",
|
|
3546
|
+
".weave"
|
|
3547
|
+
];
|
|
3548
|
+
async function buildDoctor(options) {
|
|
3549
|
+
const cwd = options.cwd;
|
|
3550
|
+
const fix = options.fix ?? false;
|
|
3551
|
+
const workspace = await findWorkspaceMode(cwd);
|
|
3552
|
+
const checks = [];
|
|
3553
|
+
let changed = [];
|
|
3554
|
+
let activeChange = null;
|
|
3555
|
+
let skills = [];
|
|
3556
|
+
if (!workspace) {
|
|
3557
|
+
checks.push({
|
|
3558
|
+
id: "weave_context",
|
|
3559
|
+
status: "error",
|
|
3560
|
+
message: "No readable Weave metadata found. Run `weave init` first.",
|
|
3561
|
+
fixable: false
|
|
3562
|
+
});
|
|
3563
|
+
return renderResult({ cwd, workspace: null, activeChange, checks, changed });
|
|
3564
|
+
}
|
|
3565
|
+
checks.push({
|
|
3566
|
+
id: "weave_context",
|
|
3567
|
+
status: "ok",
|
|
3568
|
+
message: `Weave metadata found: ${path10.relative(workspace.workspacePath, workspace.workspaceYmlPath)}`,
|
|
3569
|
+
fixable: false
|
|
3570
|
+
});
|
|
3571
|
+
const missingScaffold = await missingSafeScaffold(workspace.workspacePath);
|
|
3572
|
+
if (fix && missingScaffold.length > 0) {
|
|
3573
|
+
const scaffold = await ensureWeaveScaffold({ folder: { path: workspace.workspacePath } });
|
|
3574
|
+
changed = scaffold.created;
|
|
3575
|
+
}
|
|
3576
|
+
const remainingMissingScaffold = await missingSafeScaffold(workspace.workspacePath);
|
|
3577
|
+
if (remainingMissingScaffold.length > 0) {
|
|
3578
|
+
checks.push({
|
|
3579
|
+
id: "safe_scaffold",
|
|
3580
|
+
status: "warning",
|
|
3581
|
+
message: "Missing safe scaffold files.",
|
|
3582
|
+
fixable: true,
|
|
3583
|
+
files: remainingMissingScaffold,
|
|
3584
|
+
details: ["Fix: weave doctor --fix"]
|
|
3585
|
+
});
|
|
3586
|
+
} else {
|
|
3587
|
+
checks.push({
|
|
3588
|
+
id: "safe_scaffold",
|
|
3589
|
+
status: "ok",
|
|
3590
|
+
message: "Safe scaffold files found.",
|
|
3591
|
+
fixable: false
|
|
3592
|
+
});
|
|
3593
|
+
}
|
|
3594
|
+
const missingDirs = await missingSafeDirs(workspace.workspacePath);
|
|
3595
|
+
if (missingDirs.length > 0) {
|
|
3596
|
+
checks.push({
|
|
3597
|
+
id: "safe_scaffold_dirs",
|
|
3598
|
+
status: "warning",
|
|
3599
|
+
message: "Missing safe scaffold directories.",
|
|
3600
|
+
fixable: true,
|
|
3601
|
+
files: missingDirs,
|
|
3602
|
+
details: ["Fix: weave doctor --fix"]
|
|
3603
|
+
});
|
|
3604
|
+
} else {
|
|
3605
|
+
checks.push({
|
|
3606
|
+
id: "safe_scaffold_dirs",
|
|
3607
|
+
status: "ok",
|
|
3608
|
+
message: "Safe scaffold directories found.",
|
|
3609
|
+
fixable: false
|
|
3610
|
+
});
|
|
3611
|
+
}
|
|
3612
|
+
if (await pathExists(path10.join(workspace.workspacePath, ".weave", "agents.yml"))) {
|
|
3613
|
+
skills = await collectSkillRows({
|
|
3614
|
+
cwd: workspace.workspacePath,
|
|
3615
|
+
templatesDir: options.templatesDir
|
|
3616
|
+
}).catch(() => []);
|
|
3617
|
+
const drift = skills.filter((row) => row.state !== "current");
|
|
3618
|
+
if (drift.length > 0) {
|
|
3619
|
+
checks.push({
|
|
3620
|
+
id: "skill_drift",
|
|
3621
|
+
status: "warning",
|
|
3622
|
+
message: "Installed skills differ from bundled templates.",
|
|
3623
|
+
fixable: false,
|
|
3624
|
+
details: drift.map((row) => `${row.agent}/${row.name}: ${row.state}`)
|
|
3625
|
+
});
|
|
3626
|
+
} else {
|
|
3627
|
+
checks.push({
|
|
3628
|
+
id: "skill_drift",
|
|
3629
|
+
status: "ok",
|
|
3630
|
+
message: skills.length > 0 ? "Installed skills are current." : "No installed skills found.",
|
|
3631
|
+
fixable: false
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
} else {
|
|
3635
|
+
checks.push({
|
|
3636
|
+
id: "skill_drift",
|
|
3637
|
+
status: "ok",
|
|
3638
|
+
message: "No installed skill manifest found.",
|
|
3639
|
+
fixable: false
|
|
3640
|
+
});
|
|
3641
|
+
}
|
|
3642
|
+
activeChange = await readActiveChange(workspace.workspacePath, options.sessionPath ?? defaultSessionPath());
|
|
3643
|
+
if (activeChange) {
|
|
3644
|
+
checks.push({
|
|
3645
|
+
id: "active_change",
|
|
3646
|
+
status: activeChange.branchMatch === false ? "warning" : "ok",
|
|
3647
|
+
message: activeChange.branchMatch === false ? `Active change branch mismatch: expected ${activeChange.branch}, current ${activeChange.currentBranch ?? "unknown"}.` : `Active change found: ${activeChange.id}.`,
|
|
3648
|
+
fixable: false
|
|
3649
|
+
});
|
|
3650
|
+
} else {
|
|
3651
|
+
checks.push({
|
|
3652
|
+
id: "active_change",
|
|
3653
|
+
status: "warning",
|
|
3654
|
+
message: "No active change found for this context.",
|
|
3655
|
+
fixable: false
|
|
3656
|
+
});
|
|
3657
|
+
}
|
|
3658
|
+
return renderResult({ cwd, workspace, activeChange, checks, changed });
|
|
3659
|
+
}
|
|
3660
|
+
async function missingSafeScaffold(root) {
|
|
3661
|
+
const missing = [];
|
|
3662
|
+
for (const file of safeScaffoldFiles) {
|
|
3663
|
+
if (!await pathExists(path10.join(root, file))) {
|
|
3664
|
+
missing.push(file);
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
return missing;
|
|
3668
|
+
}
|
|
3669
|
+
async function missingSafeDirs(root) {
|
|
3670
|
+
const missing = [];
|
|
3671
|
+
for (const dir of safeScaffoldDirs) {
|
|
3672
|
+
if (!await pathExists(path10.join(root, dir))) {
|
|
3673
|
+
missing.push(dir);
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
return missing;
|
|
3677
|
+
}
|
|
3678
|
+
async function readActiveChange(root, sessionPath) {
|
|
3679
|
+
try {
|
|
3680
|
+
const session = await loadCurrentSession(sessionPath);
|
|
3681
|
+
if (!session) return null;
|
|
3682
|
+
const folderId = findFolderByPath(session, root);
|
|
3683
|
+
const current = folderId ? session.folders[folderId]?.current_change : void 0;
|
|
3684
|
+
if (!current) return null;
|
|
3685
|
+
const branch = await currentBranch(root) ?? null;
|
|
3686
|
+
return {
|
|
3687
|
+
id: current.id,
|
|
3688
|
+
branch: current.branch,
|
|
3689
|
+
branchMatch: branch ? branch === current.branch : null,
|
|
3690
|
+
currentBranch: branch
|
|
3691
|
+
};
|
|
3692
|
+
} catch {
|
|
3693
|
+
return null;
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
function renderResult(input) {
|
|
3697
|
+
const status = overallStatus(input.checks);
|
|
3698
|
+
return {
|
|
3699
|
+
status,
|
|
3700
|
+
cwd: input.cwd,
|
|
3701
|
+
mode: input.workspace?.mode ?? null,
|
|
3702
|
+
root: input.workspace?.workspacePath ?? null,
|
|
3703
|
+
activeChange: input.activeChange,
|
|
3704
|
+
checks: input.checks,
|
|
3705
|
+
changed: input.changed,
|
|
3706
|
+
message: renderDoctorMessage({
|
|
3707
|
+
status,
|
|
3708
|
+
cwd: input.cwd,
|
|
3709
|
+
workspace: input.workspace,
|
|
3710
|
+
activeChange: input.activeChange,
|
|
3711
|
+
checks: input.checks,
|
|
3712
|
+
changed: input.changed
|
|
3713
|
+
})
|
|
3714
|
+
};
|
|
3715
|
+
}
|
|
3716
|
+
function overallStatus(checks) {
|
|
3717
|
+
if (checks.some((check) => check.status === "error")) return "error";
|
|
3718
|
+
if (checks.some((check) => check.status === "warning")) return "warning";
|
|
3719
|
+
return "ok";
|
|
3720
|
+
}
|
|
3721
|
+
function renderDoctorMessage(input) {
|
|
3722
|
+
const lines = [];
|
|
3723
|
+
lines.push("Weave Doctor");
|
|
3724
|
+
lines.push("");
|
|
3725
|
+
lines.push("Context");
|
|
3726
|
+
lines.push(`- Cwd: ${input.cwd}`);
|
|
3727
|
+
lines.push(`- Root: ${input.workspace?.workspacePath ?? "unknown"}`);
|
|
3728
|
+
lines.push(`- Mode: ${input.workspace?.mode ?? "unknown"}`);
|
|
3729
|
+
lines.push(`- Active change: ${input.activeChange?.id ?? "none"}`);
|
|
3730
|
+
lines.push(`- Branch: ${input.activeChange?.currentBranch ?? "unknown"}`);
|
|
3731
|
+
lines.push("");
|
|
3732
|
+
lines.push("Checks");
|
|
3733
|
+
for (const check of input.checks) {
|
|
3734
|
+
lines.push(`${statusPrefix(check.status)} ${check.message}`);
|
|
3735
|
+
for (const file of check.files ?? []) {
|
|
3736
|
+
lines.push(` - ${file}`);
|
|
3737
|
+
}
|
|
3738
|
+
for (const detail of check.details ?? []) {
|
|
3739
|
+
lines.push(` ${detail}`);
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
lines.push("");
|
|
3743
|
+
lines.push("Summary");
|
|
3744
|
+
lines.push(`- Status: ${input.status}`);
|
|
3745
|
+
lines.push(`- Passed: ${input.checks.filter((check) => check.status === "ok").length}`);
|
|
3746
|
+
lines.push(`- Warnings: ${input.checks.filter((check) => check.status === "warning").length}`);
|
|
3747
|
+
lines.push(`- Errors: ${input.checks.filter((check) => check.status === "error").length}`);
|
|
3748
|
+
if (input.changed.length === 0) {
|
|
3749
|
+
lines.push("- No files were changed.");
|
|
3750
|
+
} else {
|
|
3751
|
+
lines.push("- Files changed:");
|
|
3752
|
+
for (const file of input.changed) {
|
|
3753
|
+
lines.push(` - ${file}`);
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
return lines.join("\n");
|
|
3757
|
+
}
|
|
3758
|
+
function statusPrefix(status) {
|
|
3759
|
+
if (status === "ok") return "[ok]";
|
|
3760
|
+
if (status === "warning") return "[warning]";
|
|
3761
|
+
return "[error]";
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3764
|
+
// src/commands/doctor.ts
|
|
3765
|
+
function doctorCommand() {
|
|
3766
|
+
return new Command5("doctor").description("Inspect Weave project health and optionally repair missing safe scaffold files.").option("--fix", "create missing safe scaffold files without overwriting existing files").option("--json", "print machine-readable JSON").action(async (options) => {
|
|
3767
|
+
const json = options.json ?? false;
|
|
3768
|
+
await withNotices({ commandName: "doctor", json }, async () => {
|
|
3769
|
+
const result3 = await buildDoctor({
|
|
3770
|
+
cwd: process.cwd(),
|
|
3771
|
+
fix: options.fix ?? false
|
|
3772
|
+
});
|
|
3773
|
+
const { message: _message, ...rest } = result3;
|
|
3774
|
+
return {
|
|
3775
|
+
json: rest,
|
|
3776
|
+
text: result3.message,
|
|
3777
|
+
exitCode: result3.status === "error" ? 1 : 0
|
|
3778
|
+
};
|
|
3779
|
+
});
|
|
3780
|
+
});
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3783
|
+
// src/commands/init.ts
|
|
3784
|
+
import { Command as Command6 } from "commander";
|
|
3785
|
+
|
|
3343
3786
|
// src/lib/init-workspace.ts
|
|
3344
3787
|
import * as prompts from "@clack/prompts";
|
|
3345
|
-
import { readFile as
|
|
3346
|
-
import
|
|
3788
|
+
import { readFile as readFile11, realpath as realpath5 } from "fs/promises";
|
|
3789
|
+
import path11 from "path";
|
|
3347
3790
|
import YAML8 from "yaml";
|
|
3348
3791
|
async function initWorkspace(options = {}) {
|
|
3349
3792
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -3413,7 +3856,7 @@ async function initWorkspaceMode(options) {
|
|
|
3413
3856
|
return initWorkspaceWithoutGitRepo(options);
|
|
3414
3857
|
}
|
|
3415
3858
|
async function initWorkspaceFromGitRepo(options, gitRoot) {
|
|
3416
|
-
const repoDirectoryName =
|
|
3859
|
+
const repoDirectoryName = path11.basename(gitRoot);
|
|
3417
3860
|
const defaultWorkspaceName = `${repoDirectoryName}-workspace`;
|
|
3418
3861
|
if ((options.yes || options.interactive === false) && !options.workspacePath && await isWeaveSourceRepo(gitRoot)) {
|
|
3419
3862
|
throw new Error(
|
|
@@ -3424,8 +3867,8 @@ async function initWorkspaceFromGitRepo(options, gitRoot) {
|
|
|
3424
3867
|
if (workspaceName === "cancelled") {
|
|
3425
3868
|
return cancelledWorkspace(options.cwd, options.sessionPath);
|
|
3426
3869
|
}
|
|
3427
|
-
const workspacePath = options.workspacePath ?
|
|
3428
|
-
const repoTargetPath =
|
|
3870
|
+
const workspacePath = options.workspacePath ? path11.resolve(options.cwd, options.workspacePath) : path11.join(path11.dirname(gitRoot), slugify(workspaceName, "workspace"));
|
|
3871
|
+
const repoTargetPath = path11.join(workspacePath, repoDirectoryName);
|
|
3429
3872
|
await assertPathMissing(workspacePath, "Workspace path already exists");
|
|
3430
3873
|
await assertPathMissing(repoTargetPath, "Repo target path already exists");
|
|
3431
3874
|
const remote = await getGitRemote(gitRoot);
|
|
@@ -3461,8 +3904,8 @@ async function initWorkspaceFromGitRepo(options, gitRoot) {
|
|
|
3461
3904
|
commitCreated
|
|
3462
3905
|
}),
|
|
3463
3906
|
folderPath: folder.path,
|
|
3464
|
-
wikiDir:
|
|
3465
|
-
metadataDir:
|
|
3907
|
+
wikiDir: path11.join(resolvedWorkspacePath, "wiki"),
|
|
3908
|
+
metadataDir: path11.join(resolvedWorkspacePath, ".weave"),
|
|
3466
3909
|
sessionPath: options.sessionPath,
|
|
3467
3910
|
commitCreated,
|
|
3468
3911
|
warning: commitCreated ? void 0 : "Initial workspace commit was not created. Run git status in the workspace to recover."
|
|
@@ -3473,7 +3916,7 @@ async function initWorkspaceWithoutGitRepo(options) {
|
|
|
3473
3916
|
if (workspacePath === "cancelled") {
|
|
3474
3917
|
return cancelledWorkspace(options.cwd, options.sessionPath);
|
|
3475
3918
|
}
|
|
3476
|
-
const workspaceName = await resolveWorkspaceName(options,
|
|
3919
|
+
const workspaceName = await resolveWorkspaceName(options, path11.basename(workspacePath));
|
|
3477
3920
|
if (workspaceName === "cancelled") {
|
|
3478
3921
|
return cancelledWorkspace(options.cwd, options.sessionPath);
|
|
3479
3922
|
}
|
|
@@ -3509,8 +3952,8 @@ async function initWorkspaceWithoutGitRepo(options) {
|
|
|
3509
3952
|
commitCreated
|
|
3510
3953
|
}),
|
|
3511
3954
|
folderPath: folder.path,
|
|
3512
|
-
wikiDir:
|
|
3513
|
-
metadataDir:
|
|
3955
|
+
wikiDir: path11.join(folder.path, "wiki"),
|
|
3956
|
+
metadataDir: path11.join(folder.path, ".weave"),
|
|
3514
3957
|
sessionPath: options.sessionPath,
|
|
3515
3958
|
commitCreated,
|
|
3516
3959
|
warning: commitCreated ? void 0 : "Initial workspace commit was not created. Run git status in the workspace to recover."
|
|
@@ -3582,14 +4025,14 @@ async function resolveWorkspaceName(options, defaultName) {
|
|
|
3582
4025
|
}
|
|
3583
4026
|
async function resolveWorkspacePath(options) {
|
|
3584
4027
|
if (options.workspacePath) {
|
|
3585
|
-
return
|
|
4028
|
+
return path11.resolve(options.cwd, options.workspacePath);
|
|
3586
4029
|
}
|
|
3587
4030
|
if (options.yes || options.interactive === false) {
|
|
3588
4031
|
throw new Error("Workspace path is required for workspace mode outside a git repo.");
|
|
3589
4032
|
}
|
|
3590
4033
|
const answer = await prompts.text({
|
|
3591
4034
|
message: "Workspace path",
|
|
3592
|
-
placeholder:
|
|
4035
|
+
placeholder: path11.join(path11.dirname(options.cwd), "my-workspace")
|
|
3593
4036
|
});
|
|
3594
4037
|
if (prompts.isCancel(answer)) {
|
|
3595
4038
|
prompts.cancel("Cancelled.");
|
|
@@ -3599,11 +4042,11 @@ async function resolveWorkspacePath(options) {
|
|
|
3599
4042
|
if (!value) {
|
|
3600
4043
|
throw new Error("Workspace path is required for workspace mode outside a git repo.");
|
|
3601
4044
|
}
|
|
3602
|
-
return
|
|
4045
|
+
return path11.resolve(options.cwd, value);
|
|
3603
4046
|
}
|
|
3604
4047
|
async function writeRepoWorkspaceMetadata(folder) {
|
|
3605
4048
|
return writeFileIfMissing(
|
|
3606
|
-
|
|
4049
|
+
path11.join(folder.path, ".weave", "workspace.yml"),
|
|
3607
4050
|
workspaceMetadataTemplate({
|
|
3608
4051
|
mode: "repo",
|
|
3609
4052
|
name: folder.name,
|
|
@@ -3617,7 +4060,7 @@ async function scaffoldWorkspace(input) {
|
|
|
3617
4060
|
const scaffold = await ensureWeaveScaffold({ folder });
|
|
3618
4061
|
const created = [...scaffold.created];
|
|
3619
4062
|
if (await writeFileIfMissing(
|
|
3620
|
-
|
|
4063
|
+
path11.join(input.workspacePath, ".weave", "workspace.yml"),
|
|
3621
4064
|
workspaceMetadataTemplate({
|
|
3622
4065
|
mode: "workspace",
|
|
3623
4066
|
name: input.workspaceName,
|
|
@@ -3627,7 +4070,7 @@ async function scaffoldWorkspace(input) {
|
|
|
3627
4070
|
created.push(".weave/workspace.yml");
|
|
3628
4071
|
}
|
|
3629
4072
|
if (await writeFileIfMissing(
|
|
3630
|
-
|
|
4073
|
+
path11.join(input.workspacePath, ".gitignore"),
|
|
3631
4074
|
workspaceGitignoreBaseTemplate()
|
|
3632
4075
|
)) {
|
|
3633
4076
|
created.push(".gitignore");
|
|
@@ -3668,7 +4111,7 @@ async function assertPathMissing(targetPath, message) {
|
|
|
3668
4111
|
}
|
|
3669
4112
|
async function isWeaveSourceRepo(gitRoot) {
|
|
3670
4113
|
try {
|
|
3671
|
-
const manifest = JSON.parse(await
|
|
4114
|
+
const manifest = JSON.parse(await readFile11(path11.join(gitRoot, "package.json"), "utf8"));
|
|
3672
4115
|
return manifest.name === "weave-it";
|
|
3673
4116
|
} catch {
|
|
3674
4117
|
return false;
|
|
@@ -3731,7 +4174,7 @@ Next:
|
|
|
3731
4174
|
|
|
3732
4175
|
// src/commands/init.ts
|
|
3733
4176
|
function initCommand() {
|
|
3734
|
-
return new
|
|
4177
|
+
return new Command6("init").description("Initialize Weave in repo mode or workspace mode and start a new session.").option("--id <id>", "folder id").option("--kind <kind>", "folder kind", "app").option("--mode <mode>", "init mode: repo or workspace; defaults to repo with --yes").option("--workspace-name <name>", "workspace name for workspace mode").option("--workspace-path <path>", "workspace path for workspace mode outside a git repo").option("--yes", "accept defaults and skip prompts").action(
|
|
3735
4178
|
async (options) => {
|
|
3736
4179
|
const result3 = await initWorkspace({
|
|
3737
4180
|
cwd: process.cwd(),
|
|
@@ -3753,9 +4196,9 @@ function initCommand() {
|
|
|
3753
4196
|
}
|
|
3754
4197
|
|
|
3755
4198
|
// src/commands/skills.ts
|
|
3756
|
-
import { Command as
|
|
4199
|
+
import { Command as Command7 } from "commander";
|
|
3757
4200
|
function skillsCommand() {
|
|
3758
|
-
const command = new
|
|
4201
|
+
const command = new Command7("skills").description("List Weave skills.");
|
|
3759
4202
|
command.command("list").description("List default skills shipped with Weave.").option("--json", "print machine-readable JSON").action(async (options) => {
|
|
3760
4203
|
await runAction4(async () => {
|
|
3761
4204
|
const skills = await listDefaultSkills();
|
|
@@ -3773,7 +4216,7 @@ function skillsCommand() {
|
|
|
3773
4216
|
return command;
|
|
3774
4217
|
}
|
|
3775
4218
|
function skillCommand() {
|
|
3776
|
-
const command = new
|
|
4219
|
+
const command = new Command7("skill").description("Show Weave skill content.");
|
|
3777
4220
|
command.command("show").description("Print a default skill shipped with Weave.").argument("<name>", "skill name").action(async (name) => {
|
|
3778
4221
|
await runAction4(async () => {
|
|
3779
4222
|
const skill = await readDefaultSkill(name);
|
|
@@ -3796,151 +4239,9 @@ async function runAction4(action) {
|
|
|
3796
4239
|
}
|
|
3797
4240
|
|
|
3798
4241
|
// src/commands/status.ts
|
|
3799
|
-
import { Command as
|
|
3800
|
-
|
|
3801
|
-
// src/lib/status.ts
|
|
3802
|
-
import { createHash as createHash4 } from "crypto";
|
|
3803
|
-
import { readFile as readFile11 } from "fs/promises";
|
|
3804
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3805
|
-
import { dirname as dirname4, join as join5 } from "path";
|
|
3806
|
-
var agentOrder = ["claude", "codex", "cursor", "opencode"];
|
|
3807
|
-
async function buildStatus(options) {
|
|
3808
|
-
const packageVersion = options.packageVersion ?? await readPackageVersion2();
|
|
3809
|
-
const inRepo = await pathExists(join5(options.cwd, ".weave", "agents.yml"));
|
|
3810
|
-
const skills = inRepo ? await collectSkillRows(options) : [];
|
|
3811
|
-
const npmLatest = options.npmLatest === void 0 ? (await getNpmVersionInfo({ packageVersion, env: options.env })).cachedLatest : options.npmLatest;
|
|
3812
|
-
const notices = await gatherNotices({
|
|
3813
|
-
cwd: options.cwd,
|
|
3814
|
-
packageVersion,
|
|
3815
|
-
npmLatest: npmLatest ?? null,
|
|
3816
|
-
templatesDir: options.templatesDir,
|
|
3817
|
-
env: options.env
|
|
3818
|
-
});
|
|
3819
|
-
return {
|
|
3820
|
-
status: "ok",
|
|
3821
|
-
packageVersion,
|
|
3822
|
-
cwd: options.cwd,
|
|
3823
|
-
inRepo,
|
|
3824
|
-
skills,
|
|
3825
|
-
notices,
|
|
3826
|
-
message: renderStatusMessage({
|
|
3827
|
-
packageVersion,
|
|
3828
|
-
cwd: options.cwd,
|
|
3829
|
-
inRepo,
|
|
3830
|
-
skills,
|
|
3831
|
-
notices,
|
|
3832
|
-
npmLatest: npmLatest ?? null
|
|
3833
|
-
})
|
|
3834
|
-
};
|
|
3835
|
-
}
|
|
3836
|
-
async function collectSkillRows(options) {
|
|
3837
|
-
const manifest = await loadAgentsManifest(options.cwd);
|
|
3838
|
-
const templates = await listDefaultSkills({ templatesDir: options.templatesDir }).catch(() => []);
|
|
3839
|
-
const templatesByName = new Map(templates.map((skill) => [skill.name, skill]));
|
|
3840
|
-
const rows = [];
|
|
3841
|
-
for (const agent of agentOrder) {
|
|
3842
|
-
const buckets = manifest.installed[agent];
|
|
3843
|
-
if (!buckets) continue;
|
|
3844
|
-
const skills = buckets.skills ?? {};
|
|
3845
|
-
for (const [name, entry] of Object.entries(skills)) {
|
|
3846
|
-
const template = templatesByName.get(name);
|
|
3847
|
-
const current = template?.lastChangedIn ?? "unknown";
|
|
3848
|
-
const absolutePath = join5(options.cwd, entry.path);
|
|
3849
|
-
const fileExists = await pathExists(absolutePath);
|
|
3850
|
-
let state = "current";
|
|
3851
|
-
if (!fileExists) {
|
|
3852
|
-
state = "missing";
|
|
3853
|
-
} else {
|
|
3854
|
-
const diskContent = await readFile11(absolutePath, "utf8").catch(() => "");
|
|
3855
|
-
const diskHash = `sha256:${createHash4("sha256").update(diskContent).digest("hex")}`;
|
|
3856
|
-
if (diskHash !== entry.installed_hash) {
|
|
3857
|
-
state = "modified";
|
|
3858
|
-
} else if (template && entry.installed_from !== template.lastChangedIn) {
|
|
3859
|
-
state = "outdated";
|
|
3860
|
-
}
|
|
3861
|
-
}
|
|
3862
|
-
rows.push({
|
|
3863
|
-
agent,
|
|
3864
|
-
name,
|
|
3865
|
-
installed_from: entry.installed_from,
|
|
3866
|
-
current,
|
|
3867
|
-
state
|
|
3868
|
-
});
|
|
3869
|
-
}
|
|
3870
|
-
}
|
|
3871
|
-
return rows;
|
|
3872
|
-
}
|
|
3873
|
-
function renderStatusMessage(opts) {
|
|
3874
|
-
const lines = [];
|
|
3875
|
-
lines.push(`weave-it ${opts.packageVersion}`);
|
|
3876
|
-
lines.push(`cwd: ${opts.cwd}`);
|
|
3877
|
-
if (opts.npmLatest) {
|
|
3878
|
-
lines.push(`npm latest (cached): ${opts.npmLatest}`);
|
|
3879
|
-
} else {
|
|
3880
|
-
lines.push(`npm latest (cached): unknown`);
|
|
3881
|
-
}
|
|
3882
|
-
if (!opts.inRepo) {
|
|
3883
|
-
lines.push("");
|
|
3884
|
-
lines.push("Not inside a Weave-managed repo (no .weave/agents.yml).");
|
|
3885
|
-
} else if (opts.skills.length === 0) {
|
|
3886
|
-
lines.push("");
|
|
3887
|
-
lines.push("No skills installed.");
|
|
3888
|
-
} else {
|
|
3889
|
-
lines.push("");
|
|
3890
|
-
lines.push("Installed skills:");
|
|
3891
|
-
const header = ["agent", "skill", "state", "installed_from", "current"];
|
|
3892
|
-
const rows = opts.skills.map((row) => [
|
|
3893
|
-
row.agent,
|
|
3894
|
-
row.name,
|
|
3895
|
-
row.state,
|
|
3896
|
-
row.installed_from ?? "unknown",
|
|
3897
|
-
row.current
|
|
3898
|
-
]);
|
|
3899
|
-
const widths = header.map(
|
|
3900
|
-
(cell, index) => Math.max(cell.length, ...rows.map((row) => row[index].length))
|
|
3901
|
-
);
|
|
3902
|
-
const renderRow = (cells) => cells.map((cell, index) => cell.padEnd(widths[index])).join(" ").trimEnd();
|
|
3903
|
-
lines.push(renderRow(header));
|
|
3904
|
-
lines.push(widths.map((width) => "-".repeat(width)).join(" ").trimEnd());
|
|
3905
|
-
for (const row of rows) {
|
|
3906
|
-
lines.push(renderRow(row));
|
|
3907
|
-
}
|
|
3908
|
-
}
|
|
3909
|
-
lines.push("");
|
|
3910
|
-
if (opts.notices.length === 0) {
|
|
3911
|
-
lines.push("Notices: none.");
|
|
3912
|
-
} else {
|
|
3913
|
-
lines.push(`Notices (${opts.notices.length}):`);
|
|
3914
|
-
for (const notice of opts.notices) {
|
|
3915
|
-
lines.push(`- [${notice.kind}] ${notice.message}`);
|
|
3916
|
-
}
|
|
3917
|
-
}
|
|
3918
|
-
return lines.join("\n");
|
|
3919
|
-
}
|
|
3920
|
-
async function readPackageVersion2() {
|
|
3921
|
-
const moduleDir = dirname4(fileURLToPath3(import.meta.url));
|
|
3922
|
-
let current = moduleDir;
|
|
3923
|
-
while (true) {
|
|
3924
|
-
const candidate = join5(current, "package.json");
|
|
3925
|
-
if (await pathExists(candidate)) {
|
|
3926
|
-
try {
|
|
3927
|
-
const parsed = JSON.parse(await readFile11(candidate, "utf8"));
|
|
3928
|
-
if (typeof parsed?.version === "string") {
|
|
3929
|
-
return parsed.version;
|
|
3930
|
-
}
|
|
3931
|
-
} catch {
|
|
3932
|
-
}
|
|
3933
|
-
}
|
|
3934
|
-
const parent = dirname4(current);
|
|
3935
|
-
if (parent === current) break;
|
|
3936
|
-
current = parent;
|
|
3937
|
-
}
|
|
3938
|
-
return "0.0.0";
|
|
3939
|
-
}
|
|
3940
|
-
|
|
3941
|
-
// src/commands/status.ts
|
|
4242
|
+
import { Command as Command8 } from "commander";
|
|
3942
4243
|
function statusCommand() {
|
|
3943
|
-
return new
|
|
4244
|
+
return new Command8("status").description("Show installed weave-it package and skill version state.").option("--json", "print machine-readable JSON").action(async (options) => {
|
|
3944
4245
|
await withNotices(
|
|
3945
4246
|
{ commandName: "status", json: options.json ?? false },
|
|
3946
4247
|
async () => {
|
|
@@ -3956,18 +4257,18 @@ function statusCommand() {
|
|
|
3956
4257
|
}
|
|
3957
4258
|
|
|
3958
4259
|
// src/commands/task.ts
|
|
3959
|
-
import { Command as
|
|
4260
|
+
import { Command as Command9, InvalidArgumentError as InvalidArgumentError4 } from "commander";
|
|
3960
4261
|
|
|
3961
4262
|
// src/lib/task-prepare.ts
|
|
3962
4263
|
import { readFile as readFile13, writeFile as writeFile5 } from "fs/promises";
|
|
3963
|
-
import
|
|
4264
|
+
import path13 from "path";
|
|
3964
4265
|
import YAML9 from "yaml";
|
|
3965
4266
|
|
|
3966
4267
|
// src/lib/tasks.ts
|
|
3967
4268
|
import { readFile as readFile12 } from "fs/promises";
|
|
3968
|
-
import
|
|
4269
|
+
import path12 from "path";
|
|
3969
4270
|
async function loadTasksForChange(changePath) {
|
|
3970
|
-
const tasksPath =
|
|
4271
|
+
const tasksPath = path12.join(changePath, "tasks.md");
|
|
3971
4272
|
if (!await pathExists(tasksPath)) {
|
|
3972
4273
|
throw new ChangeCommandError("tasks_missing", "No tasks.md found for the active change. Run `weave-issues` first.", { path: tasksPath });
|
|
3973
4274
|
}
|
|
@@ -4125,7 +4426,7 @@ async function workspaceModeTargets(rootPath, tasks, blockers) {
|
|
|
4125
4426
|
blockers.push({ target: repoId, reason: "Task references a repo id that is not registered in workspace metadata." });
|
|
4126
4427
|
continue;
|
|
4127
4428
|
}
|
|
4128
|
-
const absolutePath =
|
|
4429
|
+
const absolutePath = path13.join(rootPath, entry.path);
|
|
4129
4430
|
if (!await pathExists(absolutePath)) {
|
|
4130
4431
|
blockers.push({ target: repoId, reason: `Registered repo path does not exist: ${entry.path}` });
|
|
4131
4432
|
continue;
|
|
@@ -4211,7 +4512,7 @@ async function applyPlans(plans, branch) {
|
|
|
4211
4512
|
return repos;
|
|
4212
4513
|
}
|
|
4213
4514
|
async function writeExecutionState(changePath, branch, repos, now) {
|
|
4214
|
-
const statusPath =
|
|
4515
|
+
const statusPath = path13.join(changePath, "status.yml");
|
|
4215
4516
|
const raw = YAML9.parse(await readFile13(statusPath, "utf8"));
|
|
4216
4517
|
const existingExecution = isRecord2(raw.execution) ? raw.execution : void 0;
|
|
4217
4518
|
const existingBranch = typeof existingExecution?.branch === "string" ? existingExecution.branch : void 0;
|
|
@@ -4288,7 +4589,7 @@ function isRecord2(value) {
|
|
|
4288
4589
|
|
|
4289
4590
|
// src/commands/task.ts
|
|
4290
4591
|
function taskCommand() {
|
|
4291
|
-
const command = new
|
|
4592
|
+
const command = new Command9("task").description("Prepare and inspect Weave local tasks.");
|
|
4292
4593
|
command.command("prepare").description("Prepare local branches for selected tasks.").argument("[tasks...]", "task ids, e.g. T1 T3").option("--scope <scope>", "prepare repos referenced by tasks with this scope").option("--all", "prepare repos referenced by all T# tasks").option("--json", "print machine-readable JSON").action(async (tasks, options) => {
|
|
4293
4594
|
await runAction5(options.json ?? false, async () => {
|
|
4294
4595
|
const selector = parsePrepareSelector(tasks, options);
|
|
@@ -4364,10 +4665,10 @@ function errorResult3(error) {
|
|
|
4364
4665
|
}
|
|
4365
4666
|
|
|
4366
4667
|
// src/commands/workspace.ts
|
|
4367
|
-
import { Command as
|
|
4668
|
+
import { Command as Command10 } from "commander";
|
|
4368
4669
|
|
|
4369
4670
|
// src/lib/show-workspace.ts
|
|
4370
|
-
import
|
|
4671
|
+
import path14 from "path";
|
|
4371
4672
|
async function showWorkspace(options = {}) {
|
|
4372
4673
|
const cwd = options.cwd ?? process.cwd();
|
|
4373
4674
|
const sessionPath = options.sessionPath ?? defaultSessionPath();
|
|
@@ -4438,8 +4739,8 @@ function buildFolderOutput(id, folderPath, kind) {
|
|
|
4438
4739
|
id,
|
|
4439
4740
|
path: folderPath,
|
|
4440
4741
|
kind,
|
|
4441
|
-
wiki:
|
|
4442
|
-
metadata:
|
|
4742
|
+
wiki: path14.join(folderPath, "wiki"),
|
|
4743
|
+
metadata: path14.join(folderPath, ".weave")
|
|
4443
4744
|
};
|
|
4444
4745
|
}
|
|
4445
4746
|
function sessionJson(session) {
|
|
@@ -4453,7 +4754,7 @@ function sessionJson(session) {
|
|
|
4453
4754
|
}
|
|
4454
4755
|
function workspaceSummary(workspacePath, metadata) {
|
|
4455
4756
|
return {
|
|
4456
|
-
name: metadata?.name ??
|
|
4757
|
+
name: metadata?.name ?? path14.basename(workspacePath),
|
|
4457
4758
|
path: workspacePath,
|
|
4458
4759
|
mode: "workspace"
|
|
4459
4760
|
};
|
|
@@ -4462,7 +4763,7 @@ async function reposWithAvailability(workspacePath, repos) {
|
|
|
4462
4763
|
return Promise.all(
|
|
4463
4764
|
repos.map(async (repo) => ({
|
|
4464
4765
|
...repo,
|
|
4465
|
-
availability: await pathExists(
|
|
4766
|
+
availability: await pathExists(path14.join(workspacePath, repo.path)) ? "present" : "missing"
|
|
4466
4767
|
}))
|
|
4467
4768
|
);
|
|
4468
4769
|
}
|
|
@@ -4497,7 +4798,7 @@ Next:
|
|
|
4497
4798
|
|
|
4498
4799
|
// src/commands/workspace.ts
|
|
4499
4800
|
function workspaceCommand() {
|
|
4500
|
-
return new
|
|
4801
|
+
return new Command10("workspace").description("Show the current Weave workspace (workspace mode) or session folders (repo mode).").option("--json", "print machine-readable JSON").action(async (options) => {
|
|
4501
4802
|
await withNotices(
|
|
4502
4803
|
{ commandName: "workspace", json: options.json ?? false },
|
|
4503
4804
|
async () => {
|
|
@@ -4513,16 +4814,26 @@ function workspaceCommand() {
|
|
|
4513
4814
|
}
|
|
4514
4815
|
|
|
4515
4816
|
// src/cli.ts
|
|
4516
|
-
import { realpathSync } from "fs";
|
|
4817
|
+
import { readFileSync, realpathSync } from "fs";
|
|
4818
|
+
import path15 from "path";
|
|
4517
4819
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
4820
|
+
function readPackageVersion3(moduleUrl = import.meta.url) {
|
|
4821
|
+
const packageJsonPath = path15.join(path15.dirname(fileURLToPath4(moduleUrl)), "..", "package.json");
|
|
4822
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
4823
|
+
if (typeof packageJson.version !== "string") {
|
|
4824
|
+
throw new Error(`Missing version in ${packageJsonPath}`);
|
|
4825
|
+
}
|
|
4826
|
+
return packageJson.version;
|
|
4827
|
+
}
|
|
4518
4828
|
function createProgram() {
|
|
4519
|
-
const program = new
|
|
4520
|
-
program.name("weave").description("Repo-local LLM wiki and temporary multi-folder AI session tooling.").version(
|
|
4829
|
+
const program = new Command11();
|
|
4830
|
+
program.name("weave").description("Repo-local LLM wiki and temporary multi-folder AI session tooling.").version(readPackageVersion3());
|
|
4521
4831
|
program.addCommand(initCommand());
|
|
4522
4832
|
program.addCommand(addCommand());
|
|
4523
4833
|
program.addCommand(workspaceCommand());
|
|
4524
4834
|
program.addCommand(changeCommand());
|
|
4525
4835
|
program.addCommand(artifactCommand());
|
|
4836
|
+
program.addCommand(doctorCommand());
|
|
4526
4837
|
program.addCommand(agentCommand());
|
|
4527
4838
|
program.addCommand(skillsCommand());
|
|
4528
4839
|
program.addCommand(skillCommand());
|
|
@@ -4545,6 +4856,7 @@ if (isDirectCliInvocation()) {
|
|
|
4545
4856
|
}
|
|
4546
4857
|
export {
|
|
4547
4858
|
createProgram,
|
|
4548
|
-
isDirectCliInvocation
|
|
4859
|
+
isDirectCliInvocation,
|
|
4860
|
+
readPackageVersion3 as readPackageVersion
|
|
4549
4861
|
};
|
|
4550
4862
|
//# sourceMappingURL=cli.js.map
|