@codexguild/mcp 1.7.0 → 1.8.0

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
@@ -10,6 +10,7 @@ skills and MCP servers, knowledge base) and adds what has to run on your machine
10
10
  | `codexguild_scan_skills` | Audits the skills installed for every supported harness (`~/.claude/skills`, `~/.claude/plugins`, `~/.codex/skills`, `~/.agents/skills`, `~/.hermes/skills`, `~/.cursor/skills`, `~/.gemini/skills`, … and the project's own `.claude/`, `.agents/`, `.github/` skills) **offline**, with the same scanner CodexGuild runs server-side (pipe-to-shell, secret exfiltration, prompt injection, hidden unicode, config tampering…). Nothing is uploaded. |
11
11
  | `codexguild_sync` | Detects your stack with the **exact installed versions from your lockfiles** (package-lock, pnpm-lock, yarn.lock, bun.lock, poetry.lock, uv.lock, Pipfile.lock, Cargo.lock; go.mod), the Node/Python runtime (.nvmrc, .tool-versions, engines) and your installed skills, keys it to the current repository (each project keeps its own sync window), then asks CodexGuild what changed and which installed versions have known vulnerabilities. |
12
12
  | `codexguild_scan_code` | Finds APIs that are **deprecated or removed in the versions you run** in your own code — file, line and the replacement (e.g. `app.mount` in hono 4.13 → `mount()` from `hono/mount`). Only package names and versions are sent; the code is searched locally. Runs automatically after `codexguild_sync`. |
13
+ | `codexguild_note_save` / `codexguild_notes` | Fill in the repository from its **git remote** (credentials in the URL are dropped first), so your agent's notes — lessons it saved after correcting its own mistakes — follow the repository across machines and harnesses. `codexguild_sync` sends it too, so every session starts with them. |
13
14
  | `codexguild_upgrade_plan` | Fills in the installed version from your lockfile: ask for a package and get the releases up to the target, possibly breaking ones, removed APIs with replacements and the steps in order. |
14
15
  | `codexguild_advise` | Detects the instruction files in your project (`CLAUDE.md`, `AGENTS.md`, …) before proposing setup changes. Proposals always need your approval. |
15
16
  | `codexguild_scan_workflows` | Reviews the project's GitHub Actions workflows that run coding agents: untrusted triggers with secrets, issue/PR text in prompts or shell scripts, `pull_request_target` with fork code, unrestricted agent tools, permissions, unpinned agent actions. Runs locally. |
@@ -760,7 +760,9 @@ function formatLocalScan({ roots, reports }, verbose = false) {
760
760
  }
761
761
 
762
762
  // src/stack-detect.ts
763
+ import { execFile } from "node:child_process";
763
764
  import { createHash as createHash2 } from "node:crypto";
765
+ import { promisify } from "node:util";
764
766
  import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "node:fs/promises";
765
767
  import { basename, dirname, join as join2, relative as relative2, resolve as resolve2, sep } from "node:path";
766
768
 
@@ -1058,6 +1060,29 @@ async function detectStack(reader, base = "") {
1058
1060
  ranges: sent.filter((d) => !d.runtime).length - exact
1059
1061
  };
1060
1062
  }
1063
+ function normalizeRepoKey(raw) {
1064
+ let s = (raw ?? "").trim();
1065
+ if (!s) return null;
1066
+ if (s.startsWith("local:")) return s.slice(0, 200).toLowerCase();
1067
+ const scp = s.match(/^[\w.-]+@([\w.-]+):(?!\/)(.+)$/);
1068
+ if (scp) s = `${scp[1]}/${scp[2]}`;
1069
+ else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(s)) {
1070
+ try {
1071
+ const u = new URL(s);
1072
+ s = `${u.hostname}${u.pathname}`;
1073
+ } catch {
1074
+ return null;
1075
+ }
1076
+ } else if (/^[\w.-]+\/[\w.-]+$/.test(s)) s = `github.com/${s}`;
1077
+ else if (!/^[\w.-]+\.[a-z]{2,}\/[\w.-]+\/[\w.\/-]+$/i.test(s)) {
1078
+ const local = s.replace(/[^\w.#-]+/g, "-").replace(/^-+|-+$/g, "");
1079
+ return local ? `local:${local.slice(0, 120).toLowerCase()}` : null;
1080
+ }
1081
+ const key = s.toLowerCase().replace(/\.git$/, "").replace(/\/+$/, "").replace(/^www\./, "");
1082
+ const parts = key.split("/").filter(Boolean);
1083
+ if (parts.length < 3 || parts.some((p) => !/^[\w.-]+$/.test(p) || p === "..")) return null;
1084
+ return parts.join("/").slice(0, 200);
1085
+ }
1061
1086
 
1062
1087
  // src/stack-detect.ts
1063
1088
  async function read(path) {
@@ -1087,6 +1112,17 @@ async function projectKey(cwd) {
1087
1112
  const root = await projectRoot(cwd);
1088
1113
  return `${basename(root).slice(0, 100)}#${createHash2("sha256").update(root).digest("hex").slice(0, 8)}`;
1089
1114
  }
1115
+ async function repoKey(cwd) {
1116
+ const root = await projectRoot(cwd);
1117
+ const git = async (...args2) => (await promisify(execFile)("git", ["-C", root, ...args2], { timeout: 3e3 })).stdout.trim();
1118
+ try {
1119
+ const url = await git("config", "--get", "remote.origin.url").catch(() => "") || await git("remote", "get-url", (await git("remote")).split("\n")[0] || "origin");
1120
+ const key = normalizeRepoKey(url);
1121
+ if (key && !key.startsWith("local:")) return key;
1122
+ } catch {
1123
+ }
1124
+ return `local:${(await projectKey(cwd)).toLowerCase()}`;
1125
+ }
1090
1126
  function fsReader(root) {
1091
1127
  const abs = (p) => p ? join2(root, ...p.split("/")) : root;
1092
1128
  return {
@@ -1501,7 +1537,7 @@ function formatCodeScan(hits, files, checked) {
1501
1537
  // src/index.ts
1502
1538
  var API = (process.env.CODEXGUILD_API_URL || "https://api.codexguild.com/v1").replace(/\/$/, "");
1503
1539
  var KEY = process.env.CODEXGUILD_API_KEY ?? "";
1504
- var VERSION = "1.7.0";
1540
+ var VERSION = "1.8.0";
1505
1541
  if (process.argv[2] === "guard") process.exit(await runGuard({ api: API, key: KEY }));
1506
1542
  var INSTRUCTION_FILES = ["CLAUDE.md", ".claude/CLAUDE.md", "AGENTS.md", "AGENTS.override.md", ".hermes.md", "HERMES.md", "GEMINI.md", ".cursorrules"];
1507
1543
  async function existingInstructionFiles(cwd) {
@@ -1545,6 +1581,8 @@ var ENHANCED = {
1545
1581
  codexguild_advise: "Runs locally first: stack, installed skills and the instruction files present in the project are detected automatically; you only need harness.",
1546
1582
  codexguild_debug: "Runs locally first: the project's package versions are read from its lockfiles and sent with the error \u2014 you only need error (and stack).",
1547
1583
  codexguild_upgrade_plan: "Runs locally first: `from` (and the ecosystem) is read from the lockfile when you only give `package`.",
1584
+ codexguild_note_save: "Runs locally first: `repo` is read from the git remote of the current repository \u2014 you only need text and kind.",
1585
+ codexguild_notes: "Runs locally first: `repo` is read from the git remote of the current repository.",
1548
1586
  codexguild_fix_submit: "Runs locally first: `packages` (names and versions after your changes) is read from the project's manifests and lockfiles \u2014 you only need task_id and summary. Run the package manager first so the lockfile is current."
1549
1587
  };
1550
1588
  function enhance(t) {
@@ -1559,7 +1597,8 @@ ${note}`,
1559
1597
  properties: { ...t.inputSchema.properties, cwd: { type: "string", description: "Project directory (defaults to the current directory)" } },
1560
1598
  // Filled from the lockfile here, so the agent need not pass it.
1561
1599
  ...t.name === "codexguild_upgrade_plan" && Array.isArray(t.inputSchema.required) ? { required: t.inputSchema.required.filter((r) => r !== "from") } : {},
1562
- ...t.name === "codexguild_fix_submit" && Array.isArray(t.inputSchema.required) ? { required: t.inputSchema.required.filter((r) => r !== "packages") } : {}
1600
+ ...t.name === "codexguild_fix_submit" && Array.isArray(t.inputSchema.required) ? { required: t.inputSchema.required.filter((r) => r !== "packages") } : {},
1601
+ ...(t.name === "codexguild_note_save" || t.name === "codexguild_notes") && Array.isArray(t.inputSchema.required) ? { required: t.inputSchema.required.filter((r) => r !== "repo") } : {}
1563
1602
  }
1564
1603
  };
1565
1604
  }
@@ -1575,11 +1614,12 @@ async function detected(name, cwd, explicit = {}) {
1575
1614
  const packages = deps2.slice(0, 300).map((d) => ({ name: d.name, ...d.version ? { version: d.version } : {}, ...d.ecosystem ? { ecosystem: d.ecosystem } : {}, ...d.runtime ? { runtime: true } : {} }));
1576
1615
  return { args: { packages }, summary: `CodexGuild: submitted ${packages.length} dependencies from ${lockfiles2.length ? lockfiles2.join(", ") : "manifests (no lockfile \u2014 versions are ranges)"}.` };
1577
1616
  }
1617
+ if (name === "codexguild_note_save" || name === "codexguild_notes") return { args: { repo: await repoKey(cwd) } };
1578
1618
  if (name === "codexguild_debug") {
1579
1619
  const { deps: deps2 } = await detectStack2(cwd);
1580
1620
  return { args: { packages: deps2.slice(0, 300).map((d) => ({ name: d.name, ...d.version ? { version: d.version } : {}, ...d.ecosystem ? { ecosystem: d.ecosystem } : {} })) } };
1581
1621
  }
1582
- const [{ deps, manifests, lockfiles, exact, ranges }, local, project] = await Promise.all([detectStack2(cwd), scanLocalSkills(), projectKey(cwd)]);
1622
+ const [{ deps, manifests, lockfiles, exact, ranges }, local, project, repo] = await Promise.all([detectStack2(cwd), scanLocalSkills(), projectKey(cwd), repoKey(cwd)]);
1583
1623
  const installedSkills = await installedSkillNames(local.reports.map((r) => r.dir));
1584
1624
  if (name === "codexguild_advise") {
1585
1625
  return { args: { stack: deps, installedSkills, existingInstructionFiles: await existingInstructionFiles(cwd), project } };
@@ -1592,7 +1632,7 @@ async function detected(name, cwd, explicit = {}) {
1592
1632
  ...flagged.slice(0, 10).map((x) => ` - FLAGGED ${x.dir}: ${x.result.findings[0]?.title ?? ""}`),
1593
1633
  ...flagged.length ? [" Run codexguild_scan_skills for details and ask the user before using flagged skills."] : []
1594
1634
  ].join("\n");
1595
- return { args: { stack: deps, installedSkills, project }, summary };
1635
+ return { args: { stack: deps, installedSkills, project, repo }, summary };
1596
1636
  }
1597
1637
  var LINT_TOOL = {
1598
1638
  name: "codexguild_lint_instructions",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexguild/mcp",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "CodexGuild MCP server (stdio) \u2014 boosts your coding agent: remote CodexGuild tools plus local stack detection from lockfiles, deprecated APIs found in your own code, and offline security audits of skills, instructions and agent workflows",
5
5
  "keywords": [
6
6
  "mcp",