@kage-core/kage-graph-mcp 1.1.14 → 1.1.15

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
@@ -9,6 +9,20 @@ This package exposes two surfaces:
9
9
 
10
10
  ## Latest Release
11
11
 
12
+ `1.1.15` hardens the npm release path and memory-only review flow:
13
+
14
+ - `npm run release:npm:dry-run` runs the guarded release checks without
15
+ publishing.
16
+ - `npm run release:npm` builds the release helper, requires a clean worktree,
17
+ fetches the remote branch, verifies local `HEAD` contains `origin/<branch>`,
18
+ runs tests and `npm pack --dry-run`, pushes the branch before publishing,
19
+ publishes with `--access public`, verifies npm registry metadata, and performs
20
+ a smoke install.
21
+ - all git steps run with `GIT_EDITOR=true` so agent sessions cannot get stuck in
22
+ an interactive commit or rebase editor.
23
+ - `kage propose --from-diff` now includes repo memory packet-only changes from
24
+ `.agent_memory/packets/*.json` and `.agent_memory/pending/*.json`.
25
+
12
26
  `1.1.14` publishes the memory/code graph trust and retrieval pass:
13
27
 
14
28
  - recall now uses vectorless BM25 lexical ranking with graph, path/type/tag,
@@ -40,8 +54,20 @@ This package exposes two surfaces:
40
54
  ```bash
41
55
  npm install
42
56
  npm run build
57
+ npm run release:npm:dry-run
58
+ ```
59
+
60
+ Publishing from the repo should use the guarded release script after the release
61
+ commit is ready:
62
+
63
+ ```bash
64
+ npm run release:npm
43
65
  ```
44
66
 
67
+ The script fetches the current branch and blocks if the remote branch is not an
68
+ ancestor of local `HEAD`, which prevents publishing an npm version from a branch
69
+ that cannot be pushed cleanly.
70
+
45
71
  ## CLI
46
72
 
47
73
  ```bash
package/dist/kernel.js CHANGED
@@ -911,7 +911,12 @@ const NOISE_PATH_PREFIXES = [
911
911
  ".pub-cache/",
912
912
  "elm-stuff/",
913
913
  ];
914
+ function isReviewableMemoryPath(filePath) {
915
+ return /^\.agent_memory\/(?:packets|pending)\/[^/]+\.json$/.test(filePath);
916
+ }
914
917
  function isNoisePath(filePath) {
918
+ if (isReviewableMemoryPath(filePath))
919
+ return false;
915
920
  return NOISE_PATH_PREFIXES.some((prefix) => filePath.startsWith(prefix));
916
921
  }
917
922
  function parsePorcelainStatus(status) {
@@ -926,7 +931,26 @@ function parsePorcelainPath(line) {
926
931
  const raw = line.length > 2 && line[2] === " " ? line.slice(3) : line.slice(2);
927
932
  return raw.trim();
928
933
  }
934
+ function branchDiffStat(projectDir, changedFiles) {
935
+ const diffStats = [
936
+ readGit(projectDir, ["diff", "--stat"]),
937
+ readGit(projectDir, ["diff", "--cached", "--stat"]),
938
+ ].filter(Boolean).join("\n").trim();
939
+ const untracked = new Set((readGit(projectDir, ["ls-files", "--others", "--exclude-standard"]) ?? "")
940
+ .split(/\r?\n/)
941
+ .map((path) => path.trim())
942
+ .filter(Boolean)
943
+ .filter((path) => changedFiles.includes(path)));
944
+ const untrackedStats = [...untracked]
945
+ .filter((file) => !diffStats.includes(file))
946
+ .map((file) => `${file} | untracked`)
947
+ .join("\n");
948
+ return [diffStats, untrackedStats].filter(Boolean).join("\n").trim()
949
+ || changedFiles.map((file) => `${file} | changed`).join("\n");
950
+ }
929
951
  function shouldSkipRepoMemoryPath(relativePath) {
952
+ if (isReviewableMemoryPath(relativePath))
953
+ return false;
930
954
  return isNoisePath(relativePath) || shouldSkipCodePath(relativePath);
931
955
  }
932
956
  function migrateLegacyMarkdown(projectDir) {
@@ -4822,7 +4846,7 @@ function proposeFromDiff(projectDir) {
4822
4846
  const changedFiles = parsePorcelainStatus(status);
4823
4847
  if (changedFiles.length === 0)
4824
4848
  return { ok: false, changedFiles: [], errors: ["No changed files found."] };
4825
- const stat = readGit(projectDir, ["diff", "--stat"]) || "Untracked or staged files changed; inspect git status for details.";
4849
+ const stat = branchDiffStat(projectDir, changedFiles);
4826
4850
  const branch = gitBranch(projectDir);
4827
4851
  const summary = {
4828
4852
  schema_version: 1,
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseReleaseArgs = parseReleaseArgs;
4
+ exports.buildNpmReleasePlan = buildNpmReleasePlan;
5
+ exports.runNpmRelease = runNpmRelease;
6
+ const node_child_process_1 = require("node:child_process");
7
+ const node_fs_1 = require("node:fs");
8
+ const node_os_1 = require("node:os");
9
+ const node_path_1 = require("node:path");
10
+ const DEFAULT_CACHE = "/private/tmp/kage-npm-cache";
11
+ function parseReleaseArgs(argv) {
12
+ const options = {
13
+ publish: false,
14
+ push: false,
15
+ smoke: false,
16
+ cache: DEFAULT_CACHE,
17
+ };
18
+ for (let index = 0; index < argv.length; index += 1) {
19
+ const arg = argv[index];
20
+ if (arg === "--publish")
21
+ options.publish = true;
22
+ else if (arg === "--dry-run")
23
+ options.publish = false;
24
+ else if (arg === "--push")
25
+ options.push = true;
26
+ else if (arg === "--smoke")
27
+ options.smoke = true;
28
+ else if (arg === "--cache") {
29
+ const value = argv[index + 1];
30
+ if (!value)
31
+ throw new Error("--cache requires a path");
32
+ options.cache = value;
33
+ index += 1;
34
+ }
35
+ else {
36
+ throw new Error(`Unknown release option: ${arg}`);
37
+ }
38
+ }
39
+ return options;
40
+ }
41
+ function buildNpmReleasePlan(context) {
42
+ const gitEnv = { GIT_EDITOR: "true" };
43
+ const steps = [
44
+ { name: "ensure clean worktree", command: "git", args: ["status", "--porcelain", "-uall"], env: gitEnv, expectEmptyStdout: true },
45
+ { name: "fetch remote branch", command: "git", args: ["fetch", "origin", context.branch], env: gitEnv },
46
+ { name: "ensure branch contains remote", command: "git", args: ["merge-base", "--is-ancestor", `origin/${context.branch}`, "HEAD"], env: gitEnv },
47
+ { name: "run package tests", command: "npm", args: ["test"] },
48
+ { name: "pack dry run", command: "npm", args: ["--cache", context.cache, "pack", "--dry-run"] },
49
+ ];
50
+ if (context.push) {
51
+ steps.push({ name: "push branch", command: "git", args: ["push", "origin", context.branch], env: gitEnv });
52
+ }
53
+ if (context.publish) {
54
+ steps.push({ name: "publish package", command: "npm", args: ["--cache", context.cache, "publish", "--access", "public"] }, { name: "verify npm version", command: "npm", args: ["view", `${context.packageName}@${context.version}`, "version"] });
55
+ if (context.smoke) {
56
+ steps.push({
57
+ name: "smoke install published package",
58
+ command: "npm",
59
+ args: ["--cache", context.cache, "install", "--prefix", smokeInstallDir(context.version), `${context.packageName}@${context.version}`],
60
+ });
61
+ }
62
+ }
63
+ return steps;
64
+ }
65
+ function smokeInstallDir(version) {
66
+ return (0, node_path_1.join)((0, node_os_1.tmpdir)(), `kage-npm-smoke-${version}`);
67
+ }
68
+ function stdout(command, args, cwd) {
69
+ return (0, node_child_process_1.execFileSync)(command, args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
70
+ }
71
+ function packageMetadata(packageDir) {
72
+ const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(packageDir, "package.json"), "utf8"));
73
+ if (typeof pkg.name !== "string" || typeof pkg.version !== "string") {
74
+ throw new Error("package.json must contain string name and version");
75
+ }
76
+ return { name: pkg.name, version: pkg.version };
77
+ }
78
+ function runStep(step, cwd) {
79
+ console.log(`release:npm: ${step.name}`);
80
+ if (step.expectEmptyStdout) {
81
+ const output = (0, node_child_process_1.execFileSync)(step.command, step.args, {
82
+ cwd,
83
+ encoding: "utf8",
84
+ stdio: ["ignore", "pipe", "inherit"],
85
+ env: { ...process.env, ...step.env },
86
+ }).trim();
87
+ if (output)
88
+ throw new Error(`release:npm: ${step.name} failed because output was not empty:\n${output}`);
89
+ return;
90
+ }
91
+ (0, node_child_process_1.execFileSync)(step.command, step.args, {
92
+ cwd,
93
+ stdio: "inherit",
94
+ env: { ...process.env, ...step.env },
95
+ });
96
+ }
97
+ function runNpmRelease(argv = process.argv.slice(2), packageDir = process.cwd()) {
98
+ const options = parseReleaseArgs(argv);
99
+ const repoRoot = stdout("git", ["rev-parse", "--show-toplevel"], packageDir);
100
+ const branch = stdout("git", ["branch", "--show-current"], repoRoot);
101
+ if (!branch)
102
+ throw new Error("npm release requires a named git branch");
103
+ (0, node_fs_1.mkdirSync)(options.cache, { recursive: true });
104
+ if (options.smoke)
105
+ (0, node_fs_1.mkdirSync)(smokeInstallDir(packageMetadata(packageDir).version), { recursive: true });
106
+ const metadata = packageMetadata(packageDir);
107
+ const plan = buildNpmReleasePlan({ ...options, branch, packageName: metadata.name, version: metadata.version });
108
+ console.log(`release:npm: package ${metadata.name}@${metadata.version}`);
109
+ console.log(`release:npm: mode ${options.publish ? "publish" : "dry-run"}`);
110
+ for (const step of plan)
111
+ runStep(step, repoRoot === (0, node_path_1.resolve)(packageDir, "..") ? packageDir : repoRoot);
112
+ }
113
+ if (process.argv[1] && process.argv[1].endsWith("release.js")) {
114
+ try {
115
+ runNpmRelease();
116
+ }
117
+ catch (error) {
118
+ console.error(error instanceof Error ? error.message : String(error));
119
+ process.exit(2);
120
+ }
121
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kage-core/kage-graph-mcp",
3
- "version": "1.1.14",
3
+ "version": "1.1.15",
4
4
  "description": "Local-first repo memory, code graph, and recall MCP server for coding agents",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -21,7 +21,9 @@
21
21
  "build": "tsc",
22
22
  "start": "node dist/index.js",
23
23
  "dev": "ts-node index.ts",
24
- "test": "npm run build && node --test dist/**/*.test.js"
24
+ "test": "npm run build && node --test dist/**/*.test.js",
25
+ "release:npm:dry-run": "npm run build && node dist/release.js --dry-run",
26
+ "release:npm": "npm run build && node dist/release.js --publish --push --smoke"
25
27
  },
26
28
  "keywords": [
27
29
  "mcp",