@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/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 Command10 } from "commander";
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(path14) {
21
+ async function pathExists(path16) {
22
22
  try {
23
- await access(path14, constants.F_OK);
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(path14, contents) {
30
- await writeFile(path14, contents, { flag: "wx" });
29
+ async function writeNewFile(path16, contents) {
30
+ await writeFile(path16, contents, { flag: "wx" });
31
31
  }
32
- async function writeFileIfMissing(path14, contents) {
33
- if (await pathExists(path14)) {
32
+ async function writeFileIfMissing(path16, contents) {
33
+ if (await pathExists(path16)) {
34
34
  return false;
35
35
  }
36
- await writeNewFile(path14, contents);
36
+ await writeNewFile(path16, contents);
37
37
  return true;
38
38
  }
39
- async function writeFileAtomic(path14, contents) {
40
- const tempPath = `${path14}.${process.pid}.tmp`;
39
+ async function writeFileAtomic(path16, contents) {
40
+ const tempPath = `${path16}.${process.pid}.tmp`;
41
41
  await writeFile(tempPath, contents);
42
- await rename(tempPath, path14);
42
+ await rename(tempPath, path16);
43
43
  }
44
- async function ensureDir(path14) {
45
- await mkdir(path14, { recursive: true });
44
+ async function ensureDir(path16) {
45
+ await mkdir(path16, { recursive: true });
46
46
  }
47
- async function createDirExclusive(path14) {
48
- await mkdir(path14, { recursive: false });
47
+ async function createDirExclusive(path16) {
48
+ await mkdir(path16, { recursive: false });
49
49
  }
50
- async function ensureDirectory(path14) {
51
- const value = await stat(path14);
50
+ async function ensureDirectory(path16) {
51
+ const value = await stat(path16);
52
52
  if (!value.isDirectory()) {
53
- throw new Error(`Expected a directory: ${path14}`);
53
+ throw new Error(`Expected a directory: ${path16}`);
54
54
  }
55
55
  }
56
- async function isDirectoryEmpty(path14) {
57
- const entries = await readdir(path14);
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(path14) {
63
+ async function readJsonCache(path16) {
64
64
  try {
65
- const contents = await readFile(path14, "utf8");
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(path14, data) {
71
+ async function writeJsonCache(path16, data) {
72
72
  try {
73
- await mkdir(dirname(path14), { recursive: true });
74
- await writeFileAtomic(path14, JSON.stringify(data, null, 2));
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, path14, status, message) {
1278
- return { agent, kind: artifact.kind, skill: artifact.name, path: path14, status, message };
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(path14) {
3041
+ async function safeHashFile(path16) {
2995
3042
  try {
2996
- const content = await readFile8(path14, "utf8");
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/init.ts
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 readFile10, realpath as realpath5 } from "fs/promises";
3346
- import path10 from "path";
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 = path10.basename(gitRoot);
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 ? path10.resolve(options.cwd, options.workspacePath) : path10.join(path10.dirname(gitRoot), slugify(workspaceName, "workspace"));
3428
- const repoTargetPath = path10.join(workspacePath, repoDirectoryName);
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: path10.join(resolvedWorkspacePath, "wiki"),
3465
- metadataDir: path10.join(resolvedWorkspacePath, ".weave"),
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, path10.basename(workspacePath));
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: path10.join(folder.path, "wiki"),
3513
- metadataDir: path10.join(folder.path, ".weave"),
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 path10.resolve(options.cwd, options.workspacePath);
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: path10.join(path10.dirname(options.cwd), "my-workspace")
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 path10.resolve(options.cwd, value);
4045
+ return path11.resolve(options.cwd, value);
3603
4046
  }
3604
4047
  async function writeRepoWorkspaceMetadata(folder) {
3605
4048
  return writeFileIfMissing(
3606
- path10.join(folder.path, ".weave", "workspace.yml"),
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
- path10.join(input.workspacePath, ".weave", "workspace.yml"),
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
- path10.join(input.workspacePath, ".gitignore"),
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 readFile10(path10.join(gitRoot, "package.json"), "utf8"));
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 Command5("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(
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 Command6 } from "commander";
4199
+ import { Command as Command7 } from "commander";
3757
4200
  function skillsCommand() {
3758
- const command = new Command6("skills").description("List Weave skills.");
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 Command6("skill").description("Show Weave skill content.");
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 Command7 } from "commander";
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 Command7("status").description("Show installed weave-it package and skill version state.").option("--json", "print machine-readable JSON").action(async (options) => {
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 Command8, InvalidArgumentError as InvalidArgumentError4 } from "commander";
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 path12 from "path";
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 path11 from "path";
4269
+ import path12 from "path";
3969
4270
  async function loadTasksForChange(changePath) {
3970
- const tasksPath = path11.join(changePath, "tasks.md");
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 = path12.join(rootPath, entry.path);
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 = path12.join(changePath, "status.yml");
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 Command8("task").description("Prepare and inspect Weave local tasks.");
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 Command9 } from "commander";
4668
+ import { Command as Command10 } from "commander";
4368
4669
 
4369
4670
  // src/lib/show-workspace.ts
4370
- import path13 from "path";
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: path13.join(folderPath, "wiki"),
4442
- metadata: path13.join(folderPath, ".weave")
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 ?? path13.basename(workspacePath),
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(path13.join(workspacePath, repo.path)) ? "present" : "missing"
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 Command9("workspace").description("Show the current Weave workspace (workspace mode) or session folders (repo mode).").option("--json", "print machine-readable JSON").action(async (options) => {
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 Command10();
4520
- program.name("weave").description("Repo-local LLM wiki and temporary multi-folder AI session tooling.").version("0.1.0");
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