@anthropologies/claudestory 0.1.6 → 0.1.8

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 CHANGED
@@ -95,16 +95,11 @@ The MCP server provides 19 tools for Claude Code integration. It imports the sam
95
95
  ### Setup with Claude Code
96
96
 
97
97
  ```bash
98
- claude mcp add claudestory -- npx -y @anthropologies/claudestory --mcp
98
+ npm install -g @anthropologies/claudestory
99
+ claude mcp add claudestory -s user -- claudestory --mcp
99
100
  ```
100
101
 
101
- That's it. No global install required. Claude Code will spawn the MCP server automatically.
102
-
103
- For a specific project root (recommended when working across multiple projects):
104
-
105
- ```bash
106
- claude mcp add claudestory -- env CLAUDESTORY_PROJECT_ROOT=/path/to/project npx -y @anthropologies/claudestory --mcp
107
- ```
102
+ Two commands: install globally, register as MCP server. Works in every project that has a `.story/` directory. The MCP server auto-discovers the project root by walking up from the working directory.
108
103
 
109
104
  ### MCP Tools
110
105
 
package/dist/cli.js CHANGED
@@ -3590,50 +3590,58 @@ import { realpathSync, existsSync as existsSync5 } from "fs";
3590
3590
  import { resolve as resolve7, join as join8, isAbsolute } from "path";
3591
3591
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3592
3592
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3593
- function pinProjectRoot() {
3593
+ function tryDiscoverRoot() {
3594
3594
  const envRoot = process.env[ENV_VAR2];
3595
3595
  if (envRoot) {
3596
3596
  if (!isAbsolute(envRoot)) {
3597
- process.stderr.write(`Error: ${ENV_VAR2} must be an absolute path, got: ${envRoot}
3597
+ process.stderr.write(`Warning: ${ENV_VAR2} must be an absolute path, got: ${envRoot}
3598
3598
  `);
3599
- process.exit(1);
3599
+ return null;
3600
3600
  }
3601
3601
  const resolved = resolve7(envRoot);
3602
- let canonical;
3603
3602
  try {
3604
- canonical = realpathSync(resolved);
3605
- } catch {
3606
- process.stderr.write(`Error: ${ENV_VAR2} path does not exist: ${resolved}
3603
+ const canonical = realpathSync(resolved);
3604
+ if (existsSync5(join8(canonical, CONFIG_PATH2))) {
3605
+ return canonical;
3606
+ }
3607
+ process.stderr.write(`Warning: No .story/config.json at ${canonical}
3607
3608
  `);
3608
- process.exit(1);
3609
- }
3610
- if (!existsSync5(join8(canonical, CONFIG_PATH2))) {
3611
- process.stderr.write(`Error: No .story/config.json at ${canonical}
3609
+ } catch {
3610
+ process.stderr.write(`Warning: ${ENV_VAR2} path does not exist: ${resolved}
3612
3611
  `);
3613
- process.exit(1);
3614
3612
  }
3615
- return canonical;
3613
+ return null;
3616
3614
  }
3617
- const root = discoverProjectRoot();
3618
- if (!root) {
3619
- process.stderr.write("Error: No .story/ project found. Set CLAUDESTORY_PROJECT_ROOT or run from a project directory.\n");
3620
- process.exit(1);
3615
+ try {
3616
+ const root = discoverProjectRoot();
3617
+ return root ? realpathSync(root) : null;
3618
+ } catch {
3619
+ return null;
3621
3620
  }
3622
- return realpathSync(root);
3623
3621
  }
3624
3622
  async function main() {
3625
- const root = pinProjectRoot();
3623
+ const root = tryDiscoverRoot();
3626
3624
  const server = new McpServer(
3627
3625
  { name: "claudestory", version },
3628
3626
  {
3629
- instructions: "Start with claudestory_status for a project overview, then claudestory_ticket_next for the highest-priority work, then claudestory_handover_latest for session context."
3627
+ instructions: root ? "Start with claudestory_status for a project overview, then claudestory_ticket_next for the highest-priority work, then claudestory_handover_latest for session context." : "No .story/ project found in the current directory. Navigate to a project with a .story/ directory, or set CLAUDESTORY_PROJECT_ROOT."
3630
3628
  }
3631
3629
  );
3632
- registerAllTools(server, root);
3630
+ if (root) {
3631
+ registerAllTools(server, root);
3632
+ process.stderr.write(`claudestory MCP server running (root: ${root})
3633
+ `);
3634
+ } else {
3635
+ server.registerTool("claudestory_status", {
3636
+ description: "Project summary \u2014 returns error if no .story/ project found"
3637
+ }, () => Promise.resolve({
3638
+ content: [{ type: "text", text: "No .story/ project found. Navigate to a directory containing .story/ or set CLAUDESTORY_PROJECT_ROOT." }],
3639
+ isError: true
3640
+ }));
3641
+ process.stderr.write("claudestory MCP server running (no project found \u2014 tools will report errors)\n");
3642
+ }
3633
3643
  const transport = new StdioServerTransport();
3634
3644
  await server.connect(transport);
3635
- process.stderr.write(`claudestory MCP server running (root: ${root})
3636
- `);
3637
3645
  }
3638
3646
  var ENV_VAR2, CONFIG_PATH2, version;
3639
3647
  var init_mcp = __esm({
@@ -3644,7 +3652,7 @@ var init_mcp = __esm({
3644
3652
  init_tools();
3645
3653
  ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
3646
3654
  CONFIG_PATH2 = ".story/config.json";
3647
- version = "0.1.6";
3655
+ version = "0.1.8";
3648
3656
  main().catch((err) => {
3649
3657
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
3650
3658
  `);
@@ -3654,7 +3662,8 @@ var init_mcp = __esm({
3654
3662
  });
3655
3663
 
3656
3664
  // src/core/init.ts
3657
- import { mkdir as mkdir3, stat as stat2 } from "fs/promises";
3665
+ import { mkdir as mkdir3, stat as stat2, writeFile as writeFile2 } from "fs/promises";
3666
+ import { existsSync as existsSync6 } from "fs";
3658
3667
  import { join as join9, resolve as resolve8 } from "path";
3659
3668
  async function initProject(root, options) {
3660
3669
  const absRoot = resolve8(root);
@@ -3681,6 +3690,13 @@ async function initProject(root, options) {
3681
3690
  await mkdir3(join9(wrapDir, "tickets"), { recursive: true });
3682
3691
  await mkdir3(join9(wrapDir, "issues"), { recursive: true });
3683
3692
  await mkdir3(join9(wrapDir, "handovers"), { recursive: true });
3693
+ const created = [
3694
+ ".story/config.json",
3695
+ ".story/roadmap.json",
3696
+ ".story/tickets/",
3697
+ ".story/issues/",
3698
+ ".story/handovers/"
3699
+ ];
3684
3700
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3685
3701
  const config = {
3686
3702
  version: 2,
@@ -3711,6 +3727,13 @@ async function initProject(root, options) {
3711
3727
  };
3712
3728
  await writeConfig(config, absRoot);
3713
3729
  await writeRoadmap(roadmap, absRoot);
3730
+ const skillDir = join9(absRoot, ".claude", "skills", "prime");
3731
+ const skillPath = join9(skillDir, "SKILL.md");
3732
+ if (!existsSync6(skillPath)) {
3733
+ await mkdir3(skillDir, { recursive: true });
3734
+ await writeFile2(skillPath, PRIME_SKILL_CONTENT, "utf-8");
3735
+ created.push(".claude/skills/prime/SKILL.md");
3736
+ }
3714
3737
  const warnings = [];
3715
3738
  if (options.force && exists) {
3716
3739
  try {
@@ -3725,22 +3748,82 @@ async function initProject(root, options) {
3725
3748
  }
3726
3749
  return {
3727
3750
  root: absRoot,
3728
- created: [
3729
- ".story/config.json",
3730
- ".story/roadmap.json",
3731
- ".story/tickets/",
3732
- ".story/issues/",
3733
- ".story/handovers/"
3734
- ],
3751
+ created,
3735
3752
  warnings
3736
3753
  };
3737
3754
  }
3755
+ var PRIME_SKILL_CONTENT;
3738
3756
  var init_init = __esm({
3739
3757
  "src/core/init.ts"() {
3740
3758
  "use strict";
3741
3759
  init_esm_shims();
3742
3760
  init_project_loader();
3743
3761
  init_errors();
3762
+ PRIME_SKILL_CONTENT = `---
3763
+ name: prime
3764
+ description: Load full claudestory project context. Use at session start for any project with a .story/ directory.
3765
+ ---
3766
+
3767
+ # Prime: Load Project Context
3768
+
3769
+ Get full project context in one command for any project using claudestory.
3770
+
3771
+ ## Step 0: Check Setup
3772
+
3773
+ First, check if the claudestory MCP tools are available by looking for \`claudestory_status\` in your available tools.
3774
+
3775
+ **If MCP tools ARE available**, proceed to Step 1.
3776
+
3777
+ **If MCP tools are NOT available**, help the user set up:
3778
+
3779
+ 1. Check if the \`claudestory\` CLI is installed by running: \`claudestory --version\`
3780
+ 2. If NOT installed, tell the user:
3781
+ \`\`\`
3782
+ claudestory CLI not found. To set up:
3783
+ npm install -g @anthropologies/claudestory
3784
+ claude mcp add claudestory -s user -- claudestory --mcp
3785
+ Then restart Claude Code and run /prime again.
3786
+ \`\`\`
3787
+ 3. If CLI IS installed but MCP not registered, offer to register it for them.
3788
+ With user permission, run: \`claude mcp add claudestory -s user -- claudestory --mcp\`
3789
+ Tell the user to restart Claude Code and run /prime again.
3790
+
3791
+ **If MCP tools are unavailable and user doesn't want to set up**, fall back to CLI:
3792
+ - Run \`claudestory status\` via Bash
3793
+ - Run \`claudestory recap\` via Bash
3794
+ - Run \`claudestory handover latest\` via Bash
3795
+ - Then continue to Steps 4-6 below.
3796
+
3797
+ ## Step 1: Project Status
3798
+ Call the \`claudestory_status\` MCP tool.
3799
+
3800
+ ## Step 2: Session Recap
3801
+ Call the \`claudestory_recap\` MCP tool.
3802
+
3803
+ ## Step 3: Latest Handover
3804
+ Call the \`claudestory_handover_latest\` MCP tool.
3805
+
3806
+ ## Step 4: Development Rules
3807
+ Read \`RULES.md\` if it exists in the project root.
3808
+
3809
+ ## Step 5: Lessons Learned
3810
+ Read \`WORK_STRATEGIES.md\` if it exists in the project root.
3811
+
3812
+ ## Step 6: Recent Commits
3813
+ Run \`git log --oneline -10\`.
3814
+
3815
+ ## After Loading
3816
+
3817
+ Present a concise summary:
3818
+ - Project progress (X/Y tickets, current phase)
3819
+ - What changed since last snapshot
3820
+ - What the last session accomplished
3821
+ - Next ticket to work on
3822
+ - Any high-severity issues or blockers
3823
+ - Key process rules (if WORK_STRATEGIES.md exists)
3824
+
3825
+ Then ask: "What would you like to work on?"
3826
+ `;
3744
3827
  }
3745
3828
  });
3746
3829
 
@@ -5168,7 +5251,7 @@ async function runCli() {
5168
5251
  registerRecapCommand: registerRecapCommand2,
5169
5252
  registerExportCommand: registerExportCommand2
5170
5253
  } = await Promise.resolve().then(() => (init_register(), register_exports));
5171
- const version2 = "0.1.6";
5254
+ const version2 = "0.1.8";
5172
5255
  class HandledError extends Error {
5173
5256
  constructor() {
5174
5257
  super("HANDLED_ERROR");
package/dist/index.js CHANGED
@@ -1382,7 +1382,8 @@ function dfsBlocked(id, state, visited, inStack, findings) {
1382
1382
  }
1383
1383
 
1384
1384
  // src/core/init.ts
1385
- import { mkdir, stat as stat2 } from "fs/promises";
1385
+ import { mkdir, stat as stat2, writeFile as writeFile2 } from "fs/promises";
1386
+ import { existsSync as existsSync4 } from "fs";
1386
1387
  import { join as join4, resolve as resolve3 } from "path";
1387
1388
  async function initProject(root, options) {
1388
1389
  const absRoot = resolve3(root);
@@ -1409,6 +1410,13 @@ async function initProject(root, options) {
1409
1410
  await mkdir(join4(wrapDir, "tickets"), { recursive: true });
1410
1411
  await mkdir(join4(wrapDir, "issues"), { recursive: true });
1411
1412
  await mkdir(join4(wrapDir, "handovers"), { recursive: true });
1413
+ const created = [
1414
+ ".story/config.json",
1415
+ ".story/roadmap.json",
1416
+ ".story/tickets/",
1417
+ ".story/issues/",
1418
+ ".story/handovers/"
1419
+ ];
1412
1420
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1413
1421
  const config = {
1414
1422
  version: 2,
@@ -1439,6 +1447,13 @@ async function initProject(root, options) {
1439
1447
  };
1440
1448
  await writeConfig(config, absRoot);
1441
1449
  await writeRoadmap(roadmap, absRoot);
1450
+ const skillDir = join4(absRoot, ".claude", "skills", "prime");
1451
+ const skillPath = join4(skillDir, "SKILL.md");
1452
+ if (!existsSync4(skillPath)) {
1453
+ await mkdir(skillDir, { recursive: true });
1454
+ await writeFile2(skillPath, PRIME_SKILL_CONTENT, "utf-8");
1455
+ created.push(".claude/skills/prime/SKILL.md");
1456
+ }
1442
1457
  const warnings = [];
1443
1458
  if (options.force && exists) {
1444
1459
  try {
@@ -1453,20 +1468,79 @@ async function initProject(root, options) {
1453
1468
  }
1454
1469
  return {
1455
1470
  root: absRoot,
1456
- created: [
1457
- ".story/config.json",
1458
- ".story/roadmap.json",
1459
- ".story/tickets/",
1460
- ".story/issues/",
1461
- ".story/handovers/"
1462
- ],
1471
+ created,
1463
1472
  warnings
1464
1473
  };
1465
1474
  }
1475
+ var PRIME_SKILL_CONTENT = `---
1476
+ name: prime
1477
+ description: Load full claudestory project context. Use at session start for any project with a .story/ directory.
1478
+ ---
1479
+
1480
+ # Prime: Load Project Context
1481
+
1482
+ Get full project context in one command for any project using claudestory.
1483
+
1484
+ ## Step 0: Check Setup
1485
+
1486
+ First, check if the claudestory MCP tools are available by looking for \`claudestory_status\` in your available tools.
1487
+
1488
+ **If MCP tools ARE available**, proceed to Step 1.
1489
+
1490
+ **If MCP tools are NOT available**, help the user set up:
1491
+
1492
+ 1. Check if the \`claudestory\` CLI is installed by running: \`claudestory --version\`
1493
+ 2. If NOT installed, tell the user:
1494
+ \`\`\`
1495
+ claudestory CLI not found. To set up:
1496
+ npm install -g @anthropologies/claudestory
1497
+ claude mcp add claudestory -s user -- claudestory --mcp
1498
+ Then restart Claude Code and run /prime again.
1499
+ \`\`\`
1500
+ 3. If CLI IS installed but MCP not registered, offer to register it for them.
1501
+ With user permission, run: \`claude mcp add claudestory -s user -- claudestory --mcp\`
1502
+ Tell the user to restart Claude Code and run /prime again.
1503
+
1504
+ **If MCP tools are unavailable and user doesn't want to set up**, fall back to CLI:
1505
+ - Run \`claudestory status\` via Bash
1506
+ - Run \`claudestory recap\` via Bash
1507
+ - Run \`claudestory handover latest\` via Bash
1508
+ - Then continue to Steps 4-6 below.
1509
+
1510
+ ## Step 1: Project Status
1511
+ Call the \`claudestory_status\` MCP tool.
1512
+
1513
+ ## Step 2: Session Recap
1514
+ Call the \`claudestory_recap\` MCP tool.
1515
+
1516
+ ## Step 3: Latest Handover
1517
+ Call the \`claudestory_handover_latest\` MCP tool.
1518
+
1519
+ ## Step 4: Development Rules
1520
+ Read \`RULES.md\` if it exists in the project root.
1521
+
1522
+ ## Step 5: Lessons Learned
1523
+ Read \`WORK_STRATEGIES.md\` if it exists in the project root.
1524
+
1525
+ ## Step 6: Recent Commits
1526
+ Run \`git log --oneline -10\`.
1527
+
1528
+ ## After Loading
1529
+
1530
+ Present a concise summary:
1531
+ - Project progress (X/Y tickets, current phase)
1532
+ - What changed since last snapshot
1533
+ - What the last session accomplished
1534
+ - Next ticket to work on
1535
+ - Any high-severity issues or blockers
1536
+ - Key process rules (if WORK_STRATEGIES.md exists)
1537
+
1538
+ Then ask: "What would you like to work on?"
1539
+ `;
1466
1540
 
1467
1541
  // src/core/snapshot.ts
1468
1542
  import { readdir as readdir3, readFile as readFile3, mkdir as mkdir2, unlink as unlink2 } from "fs/promises";
1469
- import { existsSync as existsSync4 } from "fs";
1543
+ import { existsSync as existsSync5 } from "fs";
1470
1544
  import { join as join5, resolve as resolve4 } from "path";
1471
1545
  import { z as z6 } from "zod";
1472
1546
  var LoadWarningSchema = z6.object({
@@ -1519,7 +1593,7 @@ async function saveSnapshot(root, loadResult) {
1519
1593
  }
1520
1594
  async function loadLatestSnapshot(root) {
1521
1595
  const snapshotsDir = join5(resolve4(root), ".story", "snapshots");
1522
- if (!existsSync4(snapshotsDir)) return null;
1596
+ if (!existsSync5(snapshotsDir)) return null;
1523
1597
  const files = await listSnapshotFiles(snapshotsDir);
1524
1598
  if (files.length === 0) return null;
1525
1599
  for (const filename of files) {
package/dist/mcp.js CHANGED
@@ -2491,51 +2491,59 @@ function registerAllTools(server, pinnedRoot) {
2491
2491
  // src/mcp/index.ts
2492
2492
  var ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
2493
2493
  var CONFIG_PATH2 = ".story/config.json";
2494
- var version = "0.1.6";
2495
- function pinProjectRoot() {
2494
+ var version = "0.1.8";
2495
+ function tryDiscoverRoot() {
2496
2496
  const envRoot = process.env[ENV_VAR2];
2497
2497
  if (envRoot) {
2498
2498
  if (!isAbsolute(envRoot)) {
2499
- process.stderr.write(`Error: ${ENV_VAR2} must be an absolute path, got: ${envRoot}
2499
+ process.stderr.write(`Warning: ${ENV_VAR2} must be an absolute path, got: ${envRoot}
2500
2500
  `);
2501
- process.exit(1);
2501
+ return null;
2502
2502
  }
2503
2503
  const resolved = resolve7(envRoot);
2504
- let canonical;
2505
2504
  try {
2506
- canonical = realpathSync(resolved);
2507
- } catch {
2508
- process.stderr.write(`Error: ${ENV_VAR2} path does not exist: ${resolved}
2505
+ const canonical = realpathSync(resolved);
2506
+ if (existsSync5(join8(canonical, CONFIG_PATH2))) {
2507
+ return canonical;
2508
+ }
2509
+ process.stderr.write(`Warning: No .story/config.json at ${canonical}
2509
2510
  `);
2510
- process.exit(1);
2511
- }
2512
- if (!existsSync5(join8(canonical, CONFIG_PATH2))) {
2513
- process.stderr.write(`Error: No .story/config.json at ${canonical}
2511
+ } catch {
2512
+ process.stderr.write(`Warning: ${ENV_VAR2} path does not exist: ${resolved}
2514
2513
  `);
2515
- process.exit(1);
2516
2514
  }
2517
- return canonical;
2515
+ return null;
2518
2516
  }
2519
- const root = discoverProjectRoot();
2520
- if (!root) {
2521
- process.stderr.write("Error: No .story/ project found. Set CLAUDESTORY_PROJECT_ROOT or run from a project directory.\n");
2522
- process.exit(1);
2517
+ try {
2518
+ const root = discoverProjectRoot();
2519
+ return root ? realpathSync(root) : null;
2520
+ } catch {
2521
+ return null;
2523
2522
  }
2524
- return realpathSync(root);
2525
2523
  }
2526
2524
  async function main() {
2527
- const root = pinProjectRoot();
2525
+ const root = tryDiscoverRoot();
2528
2526
  const server = new McpServer(
2529
2527
  { name: "claudestory", version },
2530
2528
  {
2531
- instructions: "Start with claudestory_status for a project overview, then claudestory_ticket_next for the highest-priority work, then claudestory_handover_latest for session context."
2529
+ instructions: root ? "Start with claudestory_status for a project overview, then claudestory_ticket_next for the highest-priority work, then claudestory_handover_latest for session context." : "No .story/ project found in the current directory. Navigate to a project with a .story/ directory, or set CLAUDESTORY_PROJECT_ROOT."
2532
2530
  }
2533
2531
  );
2534
- registerAllTools(server, root);
2532
+ if (root) {
2533
+ registerAllTools(server, root);
2534
+ process.stderr.write(`claudestory MCP server running (root: ${root})
2535
+ `);
2536
+ } else {
2537
+ server.registerTool("claudestory_status", {
2538
+ description: "Project summary \u2014 returns error if no .story/ project found"
2539
+ }, () => Promise.resolve({
2540
+ content: [{ type: "text", text: "No .story/ project found. Navigate to a directory containing .story/ or set CLAUDESTORY_PROJECT_ROOT." }],
2541
+ isError: true
2542
+ }));
2543
+ process.stderr.write("claudestory MCP server running (no project found \u2014 tools will report errors)\n");
2544
+ }
2535
2545
  const transport = new StdioServerTransport();
2536
2546
  await server.connect(transport);
2537
- process.stderr.write(`claudestory MCP server running (root: ${root})
2538
- `);
2539
2547
  }
2540
2548
  main().catch((err) => {
2541
2549
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anthropologies/claudestory",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {