@flyingrobots/graft 0.3.1 → 0.3.5

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/CHANGELOG.md CHANGED
@@ -5,6 +5,51 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [0.3.5] - 2026-04-05
9
+
10
+ ### Fixed
11
+
12
+ - **CI**: use `npx npm@latest` for OIDC trusted publishing — avoids
13
+ self-upgrade breakage on Node 22's bundled npm.
14
+
15
+ ## [0.3.4] - 2026-04-05
16
+
17
+ ### Fixed
18
+
19
+ - **CI**: upgrade npm CLI to >=11.5.1 for OIDC trusted publishing.
20
+
21
+ ## [0.3.3] - 2026-04-05
22
+
23
+ ### Fixed
24
+
25
+ - **CI**: use `npm publish` instead of `pnpm publish` for OIDC
26
+ provenance.
27
+
28
+ ## [0.3.2] - 2026-04-05
29
+
30
+ ### Added
31
+
32
+ - **`graft init`**: zero-friction project onboarding. Scaffolds
33
+ `.graftignore`, adds `.graft/` to `.gitignore`, generates
34
+ `CLAUDE.md` snippet instructing agents to prefer graft tools,
35
+ and prints Claude Code hook config. Idempotent.
36
+ - **CI**: release workflow attaches npm tarball + SHA256SUMS to
37
+ GitHub releases as downloadable assets.
38
+ - **CI**: npm publish via OIDC provenance (no secret needed).
39
+
40
+ ### Changed
41
+
42
+ - **CLI bootstrap**: `bin/graft.js` resolves tsx from the package's
43
+ own `node_modules`, so `graft init` works from any directory.
44
+ - **Docs**: regenerated README, GUIDE, BEARING, and VISION signposts.
45
+ - **package.json**: added `publishConfig`, `homepage`, `bugs`,
46
+ `packageManager`, upgraded keywords for MCP/agent discovery.
47
+
48
+ ### Fixed
49
+
50
+ - **CI**: removed pnpm version override that conflicted with
51
+ `packageManager` field.
52
+
8
53
  ## [0.3.1] - 2026-04-05
9
54
 
10
55
  ### Changed
package/README.md CHANGED
@@ -10,7 +10,7 @@ tools (outlines, diffs, symbol history) are useful to anyone.
10
10
 
11
11
  ## Why
12
12
 
13
- Empirical analysis of 1,091 real coding sessions (Blacklight) found
13
+ Empirical analysis of 1,091 real coding sessions ([Blacklight](https://github.com/flyingrobots/blacklight)) found
14
14
  that **Read accounts for 96.2 GB of context burden** — 6.6x all
15
15
  other tools combined. 58% of reads are full-file. The fattest 2.4%
16
16
  of reads produce 24% of raw bytes. Dynamic read caps + session
@@ -36,6 +36,16 @@ docker run -i --rm -v "$PWD:/workspace" flyingrobots/graft
36
36
 
37
37
  ## Quick start
38
38
 
39
+ ```bash
40
+ npx @flyingrobots/graft init
41
+ ```
42
+
43
+ Scaffolds `.graftignore`, adds `.graft/` to `.gitignore`, generates
44
+ a `CLAUDE.md` snippet telling agents to prefer graft tools, and
45
+ prints Claude Code hook config.
46
+
47
+ Then add graft to your MCP config:
48
+
39
49
  ```json
40
50
  {
41
51
  "mcpServers": {
@@ -96,6 +106,47 @@ is structured JSON.
96
106
  | `doctor` | Runtime health check |
97
107
  | `stats` | Decision metrics summary |
98
108
 
109
+ ## Claude Code hooks
110
+
111
+ Two hooks work alongside the MCP server to govern native `Read`
112
+ calls — a safety net for when agents bypass graft's tools:
113
+
114
+ ```json
115
+ {
116
+ "hooks": {
117
+ "PreToolUse": [
118
+ {
119
+ "matcher": "Read",
120
+ "hooks": [
121
+ {
122
+ "type": "command",
123
+ "command": "node --import tsx node_modules/@flyingrobots/graft/src/hooks/pretooluse-read.ts"
124
+ }
125
+ ]
126
+ }
127
+ ],
128
+ "PostToolUse": [
129
+ {
130
+ "matcher": "Read",
131
+ "hooks": [
132
+ {
133
+ "type": "command",
134
+ "command": "node --import tsx node_modules/@flyingrobots/graft/src/hooks/posttooluse-read.ts"
135
+ }
136
+ ]
137
+ }
138
+ ]
139
+ }
140
+ }
141
+ ```
142
+
143
+ Add to `.claude/settings.json` in your project root.
144
+ **PreToolUse** blocks banned files before the read.
145
+ **PostToolUse** shows the agent what `safe_read` would have saved.
146
+
147
+ See the **[Setup Guide](docs/GUIDE.md)** for full details on hooks,
148
+ per-editor MCP config, `.graftignore`, and troubleshooting.
149
+
99
150
  ## Reason codes
100
151
 
101
152
  Every refusal or policy decision includes a machine-readable reason
package/bin/graft.js CHANGED
@@ -1,11 +1,39 @@
1
- #!/usr/bin/env -S node --import tsx
1
+ #!/usr/bin/env node
2
2
 
3
- // Graft MCP server stdio transport
4
- // Usage: npx @flyingrobots/graft
3
+ // Graft context governor for coding agents
4
+ // Bootstrap: re-exec with tsx loader resolved from the package's own deps.
5
5
 
6
- import { createGraftServer } from "../src/mcp/server.js";
7
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { fileURLToPath } from "node:url";
7
+ import { dirname, join } from "node:path";
8
+ import { execFileSync } from "node:child_process";
9
+ import { createRequire } from "node:module";
8
10
 
9
- const graft = createGraftServer();
10
- const transport = new StdioServerTransport();
11
- await graft.getMcpServer().connect(transport);
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const require = createRequire(import.meta.url);
13
+
14
+ // If already running under tsx, proceed directly
15
+ if (process.env.__GRAFT_TSX_LOADED === "1") {
16
+ const command = process.argv[2];
17
+ if (command === "init") {
18
+ const { runInit } = await import("../src/cli/init.js");
19
+ runInit();
20
+ } else {
21
+ const { createGraftServer } = await import("../src/mcp/server.js");
22
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
23
+ const graft = createGraftServer();
24
+ const transport = new StdioServerTransport();
25
+ await graft.getMcpServer().connect(transport);
26
+ }
27
+ } else {
28
+ // Re-exec with tsx loader from our own node_modules
29
+ const tsxPath = require.resolve("tsx/esm");
30
+ const script = join(__dirname, "graft.js");
31
+ try {
32
+ execFileSync(process.execPath, ["--import", tsxPath, script, ...process.argv.slice(2)], {
33
+ stdio: "inherit",
34
+ env: { ...process.env, __GRAFT_TSX_LOADED: "1" },
35
+ });
36
+ } catch (err) {
37
+ process.exit(err?.status ?? 1);
38
+ }
39
+ }
package/docs/GUIDE.md CHANGED
@@ -12,6 +12,20 @@ Or run without installing:
12
12
  npx @flyingrobots/graft
13
13
  ```
14
14
 
15
+ ## Quick setup
16
+
17
+ ```bash
18
+ npx @flyingrobots/graft init
19
+ ```
20
+
21
+ Scaffolds your project for graft in one command:
22
+ - Creates `.graftignore` (template with examples)
23
+ - Adds `.graft/` to `.gitignore`
24
+ - Generates a `CLAUDE.md` snippet instructing agents to prefer graft tools
25
+ - Prints Claude Code hook config for manual setup
26
+
27
+ Idempotent — safe to run again without duplicating entries.
28
+
15
29
  ## MCP Configuration
16
30
 
17
31
  Graft runs as an MCP server over stdio. Add it to your editor or
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@flyingrobots/graft",
3
- "version": "0.3.1",
3
+ "version": "0.3.5",
4
4
  "description": "Context governor for coding agents — MCP server with policy-enforced reads, structural outlines, and session tracking",
5
5
  "type": "module",
6
+ "packageManager": "pnpm@10.30.0",
6
7
  "bin": {
7
8
  "graft": "./bin/graft.js",
8
9
  "git-graft": "./bin/graft.js"
@@ -16,6 +17,14 @@
16
17
  "README.md",
17
18
  "CHANGELOG.md"
18
19
  ],
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.build.json",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "lint": "eslint .",
25
+ "typecheck": "tsc --noEmit",
26
+ "pack:check": "pnpm pack --dry-run"
27
+ },
19
28
  "engines": {
20
29
  "node": ">=20.11.0"
21
30
  },
@@ -65,12 +74,7 @@
65
74
  "typescript-eslint": "^8.58.0",
66
75
  "vitest": "^4.1.2"
67
76
  },
68
- "scripts": {
69
- "build": "tsc -p tsconfig.build.json",
70
- "test": "vitest run",
71
- "test:watch": "vitest",
72
- "lint": "eslint .",
73
- "typecheck": "tsc --noEmit",
74
- "pack:check": "pnpm pack --dry-run"
77
+ "pnpm": {
78
+ "onlyBuiltDependencies": []
75
79
  }
76
- }
80
+ }
@@ -0,0 +1,112 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+
4
+ const GRAFTIGNORE_TEMPLATE = `# Graft ignore patterns — files matching these are refused by safe_read.
5
+ # Syntax: same as .gitignore (glob matching via picomatch).
6
+
7
+ # Examples:
8
+ # *.generated.ts
9
+ # vendor/**
10
+ # data/**/*.json
11
+ `;
12
+
13
+ const AGENT_SNIPPET = `## File reads
14
+
15
+ This project uses [graft](https://github.com/flyingrobots/graft) as
16
+ a context governor. Prefer graft's MCP tools over native file reads:
17
+
18
+ - Use \`safe_read\` instead of \`Read\` for file contents
19
+ - Use \`file_outline\` to see structure before reading
20
+ - Use \`read_range\` with jump table entries for targeted reads
21
+ - Use \`graft_diff\` instead of \`git diff\` for structural changes
22
+ - Use \`explain\` if you get an unfamiliar reason code
23
+ - Call \`set_budget\` at session start if context is tight
24
+
25
+ These tools enforce read policy, cache observations, and track
26
+ session metrics. Native reads bypass all of that.
27
+ `;
28
+
29
+ const GITIGNORE_ENTRY = "\n# Graft runtime data\n.graft/\n";
30
+
31
+ const HOOKS_CONFIG = `
32
+ Add to .claude/settings.json for Claude Code hook integration:
33
+
34
+ {
35
+ "hooks": {
36
+ "PreToolUse": [
37
+ {
38
+ "matcher": "Read",
39
+ "hooks": [
40
+ {
41
+ "type": "command",
42
+ "command": "node --import tsx node_modules/@flyingrobots/graft/src/hooks/pretooluse-read.ts"
43
+ }
44
+ ]
45
+ }
46
+ ],
47
+ "PostToolUse": [
48
+ {
49
+ "matcher": "Read",
50
+ "hooks": [
51
+ {
52
+ "type": "command",
53
+ "command": "node --import tsx node_modules/@flyingrobots/graft/src/hooks/posttooluse-read.ts"
54
+ }
55
+ ]
56
+ }
57
+ ]
58
+ }
59
+ }
60
+ `;
61
+
62
+ function writeIfMissing(filePath: string, content: string, label: string): void {
63
+ if (fs.existsSync(filePath)) {
64
+ console.log(` exists ${label}`);
65
+ } else {
66
+ fs.writeFileSync(filePath, content);
67
+ console.log(` create ${label}`);
68
+ }
69
+ }
70
+
71
+ function appendIfMissing(filePath: string, marker: string, content: string, label: string): void {
72
+ if (fs.existsSync(filePath)) {
73
+ const existing = fs.readFileSync(filePath, "utf-8");
74
+ if (existing.includes(marker)) {
75
+ console.log(` exists ${label} (already has graft entry)`);
76
+ return;
77
+ }
78
+ fs.appendFileSync(filePath, content);
79
+ console.log(` append ${label}`);
80
+ } else {
81
+ fs.writeFileSync(filePath, content.trimStart());
82
+ console.log(` create ${label}`);
83
+ }
84
+ }
85
+
86
+ export function runInit(): void {
87
+ const cwd = process.cwd();
88
+ console.log(`\nInitializing graft in ${cwd}\n`);
89
+
90
+ // 1. .graftignore
91
+ writeIfMissing(path.join(cwd, ".graftignore"), GRAFTIGNORE_TEMPLATE, ".graftignore");
92
+
93
+ // 2. .gitignore — append .graft/
94
+ appendIfMissing(path.join(cwd, ".gitignore"), ".graft/", GITIGNORE_ENTRY, ".gitignore");
95
+
96
+ // 3. CLAUDE.md — append agent instructions snippet
97
+ appendIfMissing(path.join(cwd, "CLAUDE.md"), "safe_read", "\n" + AGENT_SNIPPET, "CLAUDE.md");
98
+
99
+ // 4. Print hooks config for manual setup
100
+ console.log(HOOKS_CONFIG);
101
+
102
+ console.log("Done. Add graft to your MCP config:\n");
103
+ console.log(` {
104
+ "mcpServers": {
105
+ "graft": {
106
+ "command": "npx",
107
+ "args": ["-y", "@flyingrobots/graft"]
108
+ }
109
+ }
110
+ }
111
+ `);
112
+ }