@anthropologies/claudestory 0.1.5 → 0.1.7
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 +3 -9
- package/dist/cli.js +3093 -2459
- package/dist/index.d.ts +1 -0
- package/dist/index.js +21 -8
- package/dist/mcp.js +71 -48
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -573,6 +573,7 @@ declare function guardPath(target: string, root: string): Promise<void>;
|
|
|
573
573
|
*
|
|
574
574
|
* CLAUDESTORY_PROJECT_ROOT env var overrides walk-up discovery.
|
|
575
575
|
* Returns the canonical absolute path, or null if not found.
|
|
576
|
+
* Throws ProjectLoaderError if .story/ exists but is unreadable.
|
|
576
577
|
*/
|
|
577
578
|
declare function discoverProjectRoot(startDir?: string): string | null;
|
|
578
579
|
|
package/dist/index.js
CHANGED
|
@@ -971,30 +971,43 @@ async function withLock(wrapDir, fn) {
|
|
|
971
971
|
}
|
|
972
972
|
|
|
973
973
|
// src/core/project-root-discovery.ts
|
|
974
|
-
import { existsSync as existsSync3 } from "fs";
|
|
974
|
+
import { existsSync as existsSync3, accessSync, constants } from "fs";
|
|
975
975
|
import { resolve as resolve2, dirname as dirname2, join as join3 } from "path";
|
|
976
976
|
var ENV_VAR = "CLAUDESTORY_PROJECT_ROOT";
|
|
977
|
+
var STORY_DIR = ".story";
|
|
977
978
|
var CONFIG_PATH = ".story/config.json";
|
|
978
979
|
function discoverProjectRoot(startDir) {
|
|
979
980
|
const envRoot = process.env[ENV_VAR];
|
|
980
981
|
if (envRoot) {
|
|
981
982
|
const resolved = resolve2(envRoot);
|
|
982
|
-
|
|
983
|
-
return resolved;
|
|
984
|
-
}
|
|
985
|
-
return null;
|
|
983
|
+
return checkRoot(resolved);
|
|
986
984
|
}
|
|
987
985
|
let current = resolve2(startDir ?? process.cwd());
|
|
988
986
|
for (; ; ) {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
}
|
|
987
|
+
const result = checkRoot(current);
|
|
988
|
+
if (result) return result;
|
|
992
989
|
const parent = dirname2(current);
|
|
993
990
|
if (parent === current) break;
|
|
994
991
|
current = parent;
|
|
995
992
|
}
|
|
996
993
|
return null;
|
|
997
994
|
}
|
|
995
|
+
function checkRoot(candidate) {
|
|
996
|
+
if (existsSync3(join3(candidate, CONFIG_PATH))) {
|
|
997
|
+
return candidate;
|
|
998
|
+
}
|
|
999
|
+
if (existsSync3(join3(candidate, STORY_DIR))) {
|
|
1000
|
+
try {
|
|
1001
|
+
accessSync(join3(candidate, STORY_DIR), constants.R_OK);
|
|
1002
|
+
} catch {
|
|
1003
|
+
throw new ProjectLoaderError(
|
|
1004
|
+
"io_error",
|
|
1005
|
+
`Permission denied: cannot read .story/ in ${candidate}`
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
998
1011
|
|
|
999
1012
|
// src/core/queries.ts
|
|
1000
1013
|
function nextTicket(state) {
|
package/dist/mcp.js
CHANGED
|
@@ -7,30 +7,61 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
8
|
|
|
9
9
|
// src/core/project-root-discovery.ts
|
|
10
|
-
import { existsSync } from "fs";
|
|
10
|
+
import { existsSync, accessSync, constants } from "fs";
|
|
11
11
|
import { resolve, dirname, join } from "path";
|
|
12
|
+
|
|
13
|
+
// src/core/errors.ts
|
|
14
|
+
var CURRENT_SCHEMA_VERSION = 1;
|
|
15
|
+
var ProjectLoaderError = class extends Error {
|
|
16
|
+
constructor(code, message, cause) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.cause = cause;
|
|
20
|
+
}
|
|
21
|
+
name = "ProjectLoaderError";
|
|
22
|
+
};
|
|
23
|
+
var INTEGRITY_WARNING_TYPES = [
|
|
24
|
+
"parse_error",
|
|
25
|
+
"schema_error",
|
|
26
|
+
"duplicate_id"
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// src/core/project-root-discovery.ts
|
|
12
30
|
var ENV_VAR = "CLAUDESTORY_PROJECT_ROOT";
|
|
31
|
+
var STORY_DIR = ".story";
|
|
13
32
|
var CONFIG_PATH = ".story/config.json";
|
|
14
33
|
function discoverProjectRoot(startDir) {
|
|
15
34
|
const envRoot = process.env[ENV_VAR];
|
|
16
35
|
if (envRoot) {
|
|
17
36
|
const resolved = resolve(envRoot);
|
|
18
|
-
|
|
19
|
-
return resolved;
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
37
|
+
return checkRoot(resolved);
|
|
22
38
|
}
|
|
23
39
|
let current = resolve(startDir ?? process.cwd());
|
|
24
40
|
for (; ; ) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
41
|
+
const result = checkRoot(current);
|
|
42
|
+
if (result) return result;
|
|
28
43
|
const parent = dirname(current);
|
|
29
44
|
if (parent === current) break;
|
|
30
45
|
current = parent;
|
|
31
46
|
}
|
|
32
47
|
return null;
|
|
33
48
|
}
|
|
49
|
+
function checkRoot(candidate) {
|
|
50
|
+
if (existsSync(join(candidate, CONFIG_PATH))) {
|
|
51
|
+
return candidate;
|
|
52
|
+
}
|
|
53
|
+
if (existsSync(join(candidate, STORY_DIR))) {
|
|
54
|
+
try {
|
|
55
|
+
accessSync(join(candidate, STORY_DIR), constants.R_OK);
|
|
56
|
+
} catch {
|
|
57
|
+
throw new ProjectLoaderError(
|
|
58
|
+
"io_error",
|
|
59
|
+
`Permission denied: cannot read .story/ in ${candidate}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
34
65
|
|
|
35
66
|
// src/mcp/tools.ts
|
|
36
67
|
import { z as z7 } from "zod";
|
|
@@ -365,22 +396,6 @@ var ProjectState = class _ProjectState {
|
|
|
365
396
|
}
|
|
366
397
|
};
|
|
367
398
|
|
|
368
|
-
// src/core/errors.ts
|
|
369
|
-
var CURRENT_SCHEMA_VERSION = 1;
|
|
370
|
-
var ProjectLoaderError = class extends Error {
|
|
371
|
-
constructor(code, message, cause) {
|
|
372
|
-
super(message);
|
|
373
|
-
this.code = code;
|
|
374
|
-
this.cause = cause;
|
|
375
|
-
}
|
|
376
|
-
name = "ProjectLoaderError";
|
|
377
|
-
};
|
|
378
|
-
var INTEGRITY_WARNING_TYPES = [
|
|
379
|
-
"parse_error",
|
|
380
|
-
"schema_error",
|
|
381
|
-
"duplicate_id"
|
|
382
|
-
];
|
|
383
|
-
|
|
384
399
|
// src/core/handover-parser.ts
|
|
385
400
|
import { readdir, readFile } from "fs/promises";
|
|
386
401
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -2476,51 +2491,59 @@ function registerAllTools(server, pinnedRoot) {
|
|
|
2476
2491
|
// src/mcp/index.ts
|
|
2477
2492
|
var ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
2478
2493
|
var CONFIG_PATH2 = ".story/config.json";
|
|
2479
|
-
var version = "0.1.
|
|
2480
|
-
function
|
|
2494
|
+
var version = "0.1.7";
|
|
2495
|
+
function tryDiscoverRoot() {
|
|
2481
2496
|
const envRoot = process.env[ENV_VAR2];
|
|
2482
2497
|
if (envRoot) {
|
|
2483
2498
|
if (!isAbsolute(envRoot)) {
|
|
2484
|
-
process.stderr.write(`
|
|
2499
|
+
process.stderr.write(`Warning: ${ENV_VAR2} must be an absolute path, got: ${envRoot}
|
|
2485
2500
|
`);
|
|
2486
|
-
|
|
2501
|
+
return null;
|
|
2487
2502
|
}
|
|
2488
2503
|
const resolved = resolve7(envRoot);
|
|
2489
|
-
let canonical;
|
|
2490
2504
|
try {
|
|
2491
|
-
canonical = realpathSync(resolved);
|
|
2492
|
-
|
|
2493
|
-
|
|
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}
|
|
2494
2510
|
`);
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
if (!existsSync5(join8(canonical, CONFIG_PATH2))) {
|
|
2498
|
-
process.stderr.write(`Error: No .story/config.json at ${canonical}
|
|
2511
|
+
} catch {
|
|
2512
|
+
process.stderr.write(`Warning: ${ENV_VAR2} path does not exist: ${resolved}
|
|
2499
2513
|
`);
|
|
2500
|
-
process.exit(1);
|
|
2501
2514
|
}
|
|
2502
|
-
return
|
|
2515
|
+
return null;
|
|
2503
2516
|
}
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2517
|
+
try {
|
|
2518
|
+
const root = discoverProjectRoot();
|
|
2519
|
+
return root ? realpathSync(root) : null;
|
|
2520
|
+
} catch {
|
|
2521
|
+
return null;
|
|
2508
2522
|
}
|
|
2509
|
-
return realpathSync(root);
|
|
2510
2523
|
}
|
|
2511
2524
|
async function main() {
|
|
2512
|
-
const root =
|
|
2525
|
+
const root = tryDiscoverRoot();
|
|
2513
2526
|
const server = new McpServer(
|
|
2514
2527
|
{ name: "claudestory", version },
|
|
2515
2528
|
{
|
|
2516
|
-
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."
|
|
2517
2530
|
}
|
|
2518
2531
|
);
|
|
2519
|
-
|
|
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
|
+
}
|
|
2520
2545
|
const transport = new StdioServerTransport();
|
|
2521
2546
|
await server.connect(transport);
|
|
2522
|
-
process.stderr.write(`claudestory MCP server running (root: ${root})
|
|
2523
|
-
`);
|
|
2524
2547
|
}
|
|
2525
2548
|
main().catch((err) => {
|
|
2526
2549
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
|