ast-mcp-server 0.7.2 → 0.8.1

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.
@@ -1,127 +1,188 @@
1
- import { randomUUID } from "node:crypto";
2
- import { link, mkdir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
1
+ import { readFile, realpath, stat } from "node:fs/promises";
3
2
  import os from "node:os";
4
3
  import path from "node:path";
4
+ import { z } from "zod";
5
+ import { applyManagedFilePlan, canonicalizeFuturePath, captureManagedFileSnapshot, createManagedFileApplyContext, hashBytes, verifyManagedFilePostimage, } from "./managed-file.js";
5
6
  const SKILL_NAME = "structural-code-editing";
7
+ const SHA256_PATTERN = /^[0-9a-f]{64}$/;
8
+ const ReleaseRecordSchema = z
9
+ .object({
10
+ version: z.string().min(1),
11
+ sha256: z.string().regex(SHA256_PATTERN),
12
+ npm_versions: z.array(z.string().min(1)).min(1),
13
+ })
14
+ .strict();
15
+ const SkillReleaseManifestSchema = z
16
+ .object({
17
+ schema_version: z.literal(1),
18
+ algorithm: z.literal("sha256"),
19
+ current: z
20
+ .object({
21
+ version: z.string().min(1),
22
+ sha256: z.string().regex(SHA256_PATTERN),
23
+ })
24
+ .strict(),
25
+ predecessors: z.array(ReleaseRecordSchema),
26
+ })
27
+ .strict();
6
28
  export class SkillConflictError extends Error {
7
29
  destination;
8
30
  constructor(destination) {
9
31
  super(`Skill already exists with different content at ${destination}; pass --force to replace it.`);
10
- this.name = "SkillConflictError";
11
32
  this.destination = destination;
33
+ this.name = "SkillConflictError";
12
34
  }
13
35
  }
36
+ export async function resolveBundledSkillAssets(executablePath) {
37
+ const root = path.resolve(path.dirname(await realpath(executablePath)), "..", "skills", SKILL_NAME);
38
+ const assets = {
39
+ skillPath: path.join(root, "SKILL.md"),
40
+ guidancePath: path.join(root, "guidance.md"),
41
+ releasesPath: path.join(root, "releases.json"),
42
+ };
43
+ for (const [label, filePath] of Object.entries(assets)) {
44
+ const info = await stat(filePath).catch(() => undefined);
45
+ if (!info?.isFile())
46
+ throw new Error(`Bundled ${label} is missing or not a regular file: ${filePath}`);
47
+ }
48
+ return assets;
49
+ }
14
50
  export async function resolveBundledSkillPath(executablePath) {
15
- const resolvedExecutable = await realpath(executablePath);
16
- return path.resolve(path.dirname(resolvedExecutable), "..", "skills", SKILL_NAME, "SKILL.md");
51
+ return (await resolveBundledSkillAssets(executablePath)).skillPath;
17
52
  }
18
- function resolveConfiguredDirectory(configured, fallback, workingDirectory) {
19
- return path.resolve(workingDirectory, configured ?? fallback);
53
+ function configured(value, fallback, cwd) {
54
+ return path.resolve(cwd, value ?? fallback);
20
55
  }
21
- async function resolveDestinationPlans(options) {
22
- if (options.scope === "project" && options.target !== "claude") {
56
+ async function destinations(options) {
57
+ const targets = options.target === "all"
58
+ ? ["claude", "hermes"]
59
+ : Array.isArray(options.target)
60
+ ? [...options.target]
61
+ : [options.target];
62
+ if (options.scope === "project" && targets.some((target) => target !== "claude"))
23
63
  throw new Error("Project scope is supported only for Claude Code skills.");
24
- }
25
- const homeDirectory = options.homeDirectory ?? os.homedir();
26
- const workingDirectory = options.workingDirectory ?? process.cwd();
27
- const environment = options.environment ?? process.env;
28
- const targets = options.target === "all" ? ["claude", "hermes"] : [options.target];
64
+ const home = options.homeDirectory ?? os.homedir();
65
+ const cwd = options.workingDirectory ?? process.cwd();
66
+ const env = options.environment ?? process.env;
29
67
  let projectRoot;
30
68
  if (options.scope === "project") {
31
- projectRoot = path.resolve(workingDirectory, options.projectRoot ?? workingDirectory);
32
- const projectStats = await stat(projectRoot).catch((error) => {
33
- if (error.code === "ENOENT") {
34
- throw new Error(`Project root does not exist: ${projectRoot}`);
35
- }
36
- throw error;
37
- });
38
- if (!projectStats.isDirectory()) {
39
- throw new Error(`Project root is not a directory: ${projectRoot}`);
40
- }
69
+ projectRoot = path.resolve(cwd, options.projectRoot ?? cwd);
70
+ const info = await stat(projectRoot).catch(() => undefined);
71
+ if (!info?.isDirectory())
72
+ throw new Error(`Project root does not exist or is not a directory: ${projectRoot}`);
41
73
  }
42
- return targets.map((target) => {
74
+ const result = targets.map((target) => {
75
+ let destination;
43
76
  if (target === "claude") {
44
77
  const root = options.scope === "project"
45
78
  ? path.join(projectRoot, ".claude")
46
- : resolveConfiguredDirectory(environment.CLAUDE_CONFIG_DIR, path.join(homeDirectory, ".claude"), workingDirectory);
47
- return {
48
- target,
49
- scope: options.scope,
50
- path: path.join(root, "skills", SKILL_NAME, "SKILL.md"),
51
- };
79
+ : configured(env.CLAUDE_CONFIG_DIR, path.join(home, ".claude"), cwd);
80
+ destination = path.join(root, "skills", SKILL_NAME, "SKILL.md");
81
+ }
82
+ else if (target === "hermes") {
83
+ destination = path.join(configured(env.HERMES_HOME, path.join(home, ".hermes"), cwd), "skills", "software-development", SKILL_NAME, "SKILL.md");
84
+ }
85
+ else {
86
+ destination = path.join(home, ".agents", "skills", SKILL_NAME, "SKILL.md");
52
87
  }
53
- const hermesHome = resolveConfiguredDirectory(environment.HERMES_HOME, path.join(homeDirectory, ".hermes"), workingDirectory);
54
88
  return {
55
89
  target,
56
- scope: "user",
57
- path: path.join(hermesHome, "skills", "software-development", SKILL_NAME, "SKILL.md"),
90
+ scope: target === "claude" ? options.scope : "user",
91
+ path: destination,
58
92
  };
59
93
  });
94
+ return Promise.all(result.map(async (item) => ({ ...item, path: await canonicalizeFuturePath(item.path) })));
60
95
  }
61
- async function classifyDestination(destination, sourceContent, force) {
96
+ async function readReleaseManifest(manifestPath) {
97
+ let parsed;
62
98
  try {
63
- const existingContent = await readFile(destination, "utf8");
64
- if (existingContent === sourceContent)
65
- return "unchanged";
66
- if (!force) {
67
- throw new SkillConflictError(destination);
68
- }
69
- return "updated";
99
+ parsed = JSON.parse(await readFile(manifestPath, "utf8"));
70
100
  }
71
101
  catch (error) {
72
- if (error.code === "ENOENT")
73
- return "installed";
74
- throw error;
102
+ throw new Error(`Skill release manifest is unreadable or invalid JSON: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
103
+ }
104
+ const result = SkillReleaseManifestSchema.safeParse(parsed);
105
+ if (!result.success) {
106
+ throw new Error(`Skill release manifest is invalid: ${result.error.issues[0]?.message ?? "unknown error"}`);
107
+ }
108
+ const digests = [
109
+ result.data.current.sha256,
110
+ ...result.data.predecessors.map((item) => item.sha256),
111
+ ];
112
+ if (new Set(digests).size !== digests.length) {
113
+ throw new Error("Skill release manifest contains a duplicate digest.");
75
114
  }
115
+ const predecessorVersions = result.data.predecessors.flatMap((item) => item.npm_versions);
116
+ if (new Set(predecessorVersions).size !== predecessorVersions.length) {
117
+ throw new Error("Skill release manifest contains a duplicate npm version provenance entry.");
118
+ }
119
+ return result.data;
76
120
  }
77
- async function writeSkill(destination, sourceContent, status) {
78
- if (status === "unchanged")
79
- return;
80
- const directory = path.dirname(destination);
81
- await mkdir(directory, { recursive: true });
82
- const temporaryPath = path.join(directory, `.${path.basename(destination)}.${process.pid}.${randomUUID()}.tmp`);
83
- await writeFile(temporaryPath, sourceContent, {
84
- encoding: "utf8",
85
- flag: "wx",
86
- mode: 0o644,
87
- });
88
- try {
89
- if (status === "installed") {
90
- await link(temporaryPath, destination).catch((error) => {
91
- if (error.code === "EEXIST") {
92
- throw new Error(`Skill appeared concurrently at ${destination}; no files were replaced.`);
93
- }
94
- throw error;
95
- });
96
- await rm(temporaryPath);
97
- return;
98
- }
99
- await rename(temporaryPath, destination);
121
+ export async function planBundledSkillInstallation(options) {
122
+ const sourcePath = path.resolve(options.sourceSkillPath);
123
+ const source = await readFile(sourcePath);
124
+ const sourceDigest = hashBytes(source);
125
+ const manifestPath = path.resolve(options.releaseManifestPath ?? path.join(path.dirname(sourcePath), "releases.json"));
126
+ const manifest = await readReleaseManifest(manifestPath);
127
+ if (sourceDigest !== manifest.current.sha256) {
128
+ throw new Error(`Skill release manifest current digest does not match source skill bytes: ${sourcePath}`);
100
129
  }
101
- finally {
102
- await rm(temporaryPath, { force: true });
130
+ const admittedPredecessors = new Set(manifest.predecessors.map((item) => item.sha256));
131
+ const logical = await destinations(options);
132
+ const filesByPath = new Map();
133
+ for (const item of logical) {
134
+ if (filesByPath.has(item.path))
135
+ continue;
136
+ const snapshot = await captureManagedFileSnapshot(item.path);
137
+ let status;
138
+ if (!snapshot.exists)
139
+ status = "installed";
140
+ else if (snapshot.sha256 === sourceDigest)
141
+ status = "unchanged";
142
+ else if (admittedPredecessors.has(snapshot.sha256) || options.force === true)
143
+ status = "updated";
144
+ else
145
+ throw new SkillConflictError(item.path);
146
+ filesByPath.set(item.path, {
147
+ path: item.path,
148
+ snapshot,
149
+ postimage: source,
150
+ postimageSha256: sourceDigest,
151
+ status,
152
+ });
103
153
  }
154
+ return {
155
+ installations: logical.map((item) => ({
156
+ ...item,
157
+ status: filesByPath.get(item.path).status,
158
+ })),
159
+ files: [...filesByPath.values()],
160
+ };
104
161
  }
105
- export async function installBundledSkill(options) {
106
- const sourceContent = await readFile(path.resolve(options.sourceSkillPath), "utf8");
107
- const destinations = await resolveDestinationPlans(options);
108
- const force = options.force ?? false;
109
- const statusByPath = new Map();
110
- for (const destination of destinations) {
111
- if (!statusByPath.has(destination.path)) {
112
- statusByPath.set(destination.path, await classifyDestination(destination.path, sourceContent, force));
162
+ export async function applySkillInstallationPlan(plan, onApplied, context = createManagedFileApplyContext()) {
163
+ const authenticated = [];
164
+ for (const file of plan.files) {
165
+ for (const current of authenticated) {
166
+ await verifyManagedFilePostimage(current, context);
113
167
  }
168
+ await applyManagedFilePlan(file, context);
169
+ authenticated.push(file);
170
+ await onApplied?.(file);
114
171
  }
115
- for (const [destination, status] of statusByPath) {
116
- await writeSkill(destination, sourceContent, status);
172
+ for (const current of authenticated) {
173
+ await verifyManagedFilePostimage(current, context);
117
174
  }
175
+ }
176
+ export async function installBundledSkill(options) {
177
+ const plan = await planBundledSkillInstallation(options);
178
+ await applySkillInstallationPlan(plan);
118
179
  return {
119
180
  version: 1,
120
181
  status: "ok",
121
182
  skill: SKILL_NAME,
122
- installations: destinations.map((destination) => ({
123
- ...destination,
124
- status: statusByPath.get(destination.path),
125
- })),
183
+ installations: plan.installations,
184
+ physicalWrites: plan.files
185
+ .filter((file) => file.status !== "unchanged")
186
+ .map((file) => ({ path: file.path, status: file.status })),
126
187
  };
127
188
  }
package/docs/support.md CHANGED
@@ -1,21 +1,21 @@
1
1
  # Support policy
2
2
 
3
- This document defines the supported platform, runtime, persistence, and operational boundary for `ast-mcp-server` v0.7.2.
3
+ This document defines the supported platform, runtime, persistence, and operational boundary for `ast-mcp-server` v0.8.1 and the current `Unreleased` development line.
4
4
 
5
5
  ## Supported release target
6
6
 
7
- | Environment | Status | Contract |
8
- | ------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
9
- | Linux x64 with required GNU coreutils, Node.js 22.5.0 | Supported | Full repository, MCP/CLI/package, lifecycle, mutation, and production-readiness gates. Persistence canary runs require `--experimental-sqlite`. |
10
- | Linux x64 with required GNU coreutils, current Node.js 24 line | Supported | Full repository, MCP/CLI/package, lifecycle, mutation, and production-readiness gates. |
11
- | Other Linux architectures or systems without required GNU coreutils | Unverified | Not supported until equivalent architecture and filesystem-publication gates pass. |
12
- | macOS | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass. |
13
- | Windows | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass. |
14
- | Remote, untrusted, or multi-tenant service use | Unsupported | No network authentication, sandbox, or tenant-isolation boundary is provided. |
7
+ | Environment | Status | Contract |
8
+ | ----------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------- |
9
+ | Linux x64 with required GNU coreutils and procfs, Node.js 22.5.0 | Supported | v0.8.1 exact release matrix for MCP/CLI/package, lifecycle, mutation, and managed setup. |
10
+ | Linux x64 with required GNU coreutils and procfs, current Node.js 24 line | Supported | v0.8.1 exact release matrix for MCP/CLI/package, lifecycle, mutation, and managed setup. |
11
+ | Other Linux architectures or systems without the required filesystem features | Unverified | Not supported until equivalent architecture, mutation, and filesystem-publication gates pass. |
12
+ | macOS | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass. |
13
+ | Windows | Unverified | Not supported until equivalent filesystem, process, package, mutation, and canary gates pass. |
14
+ | Remote, untrusted, or multi-tenant service use | Unsupported | No network authentication, sandbox, or tenant-isolation boundary is provided. |
15
15
 
16
- The package engine floor is Node.js `>=22.5.0`, but release evidence is deliberately narrower: exact Node.js 22.5.0 and the current Node.js 24 line on Linux. A newer version satisfying the engine range is not automatically a verified release target.
16
+ The package engine floor is Node.js `>=22.5.0`. The v0.8.1 release matrix exercises exact Node.js 22.5.0 and the current Node.js 24 line on Linux, including managed setup-file publication; a future version satisfying the engine range is not automatically a verified release target.
17
17
 
18
- The checked-evidence freezer requires GNU coreutils `mv` with `--update=none-fail`, `--no-copy`, and `--no-target-directory`. Equivalent publication semantics have not been verified on other operating systems or architectures.
18
+ The checked-evidence freezer requires GNU coreutils 9.7 `mv` with `--update=none-fail`, `--no-copy`, and `--no-target-directory`. Managed setup-file mutation additionally requires that same `mv` with `--exchange`, GNU coreutils `ln -L -T`, procfs descriptor paths at `/proc/self/fd`, `O_DIRECTORY`, and `O_NOFOLLOW`. It fails closed when any verified Linux primitive is unavailable. Equivalent publication, exchange, descriptor-link, and descriptor-relative mutation semantics have not been verified on other operating systems or architectures.
19
19
 
20
20
  ## Local stdio trust boundary
21
21
 
@@ -35,7 +35,7 @@ Operation locks coordinate cooperating same-user processes. They do not stop edi
35
35
 
36
36
  The supported default is memory-only indexing. When `AST_SYMBOL_INDEX_PERSISTENCE` is absent, persistence remains disabled and no persistent cache is created.
37
37
 
38
- The only candidate-authorized opt-in persistence policy is `canary`; it is not enabled by default in v0.7.2:
38
+ The only candidate-authorized opt-in persistence policy is `canary`; it is not enabled by default in v0.8.1:
39
39
 
40
40
  ```bash
41
41
  AST_SYMBOL_INDEX_PERSISTENCE=canary \
@@ -49,7 +49,7 @@ The cache root must be explicit and absolute. Use a dedicated local directory wi
49
49
 
50
50
  ## Production-readiness acceptance
51
51
 
52
- The checked Linux x64 matrix covers `ast-mcp-server` and `x-scraper` under Node.js 22.5.0 and Node.js 24. MCP-PROD-404 is normative; its complete release gate requires all four reports to pass the following contract:
52
+ The four retained Linux x64 production-readiness reports cover `ast-mcp-server` and `x-scraper` under Node.js 22.5.0 and Node.js 24 for package version 0.6.0, commit `2d0b21bbb80fae1acfca6a85d5891d87e68b59c1`, and tree `af931d49769fabdf06f623965a6cfe1f9afb8a81`. They are historical integration evidence only: they are not reattributed to v0.8.1 and do not establish managed setup-file publication for that release. The separate exact v0.8.1 release matrix validates the current repository under both supported runtime lines, including managed setup, but does not rebind those historical reports. MCP-PROD-404 remains normative for admission:
53
53
 
54
54
  - zero semantic mismatches across disabled, cold, warm, restart, and rollback reads;
55
55
  - byte-identical real-repository status and worktree trees;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ast-mcp-server",
3
- "version": "0.7.2",
3
+ "version": "0.8.1",
4
4
  "description": "Structural code MCP server and batch CLI for compact TypeScript/JavaScript reads and hash-bound AST edits.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "resolutions": {
46
46
  "hono": "4.12.34",
47
- "nanoid": "3.3.17"
47
+ "nanoid": "3.3.18"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "tsc",
@@ -56,6 +56,8 @@
56
56
  "test:cli": "yarn build && node scripts/cli-smoke.mjs",
57
57
  "test:errors": "yarn build && node scripts/public-error-smoke.mjs",
58
58
  "test:package": "node scripts/package-smoke.mjs",
59
+ "test:installed-agents": "yarn build && node scripts/installed-agent-guidance-smoke.mjs",
60
+ "test:agent-fixtures": "node scripts/admit-agent-fixtures.mjs --check",
59
61
  "test:release-candidate": "node scripts/release-candidate-matrix.mjs",
60
62
  "benchmark": "yarn build && node scripts/benchmark.mjs",
61
63
  "benchmark:corpus": "yarn build && node scripts/benchmark-corpus.mjs",
@@ -80,6 +82,7 @@
80
82
  "@modelcontextprotocol/sdk": "^1.12.0",
81
83
  "@toon-format/toon": "4.1.0",
82
84
  "diff": "^9.0.0",
85
+ "jsonc-parser": "^3.3.1",
83
86
  "ts-morph": "24.0.0",
84
87
  "zod": "^3.23.8"
85
88
  },
@@ -93,5 +96,5 @@
93
96
  "typescript-eslint": "^8.65.0",
94
97
  "vitest": "^4.1.10"
95
98
  },
96
- "gitHead": "d27f8f80cf81bf81b1d1e5e76db7b8875344d91e"
99
+ "gitHead": "bc8026fa534262c293384b50a8dff0eaa2ca22da"
97
100
  }
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: structural-code-editing
3
3
  description: Leer, navegar y editar proyectos TypeScript/JavaScript mediante el resolver real del compilador y operaciones AST preparadas, revisadas y vinculadas por hash.
4
- version: "4.1.0"
4
+ version: "4.2.0"
5
5
  author: "yail"
6
6
  license: "ISC"
7
7
  metadata:
@@ -12,6 +12,12 @@ metadata:
12
12
 
13
13
  # Edición estructural con el servidor MCP `ast`
14
14
 
15
+ ## Contrato de activación
16
+
17
+ Cargar `structural-code-editing` antes de navegar semánticamente, analizar impacto o referencias, consultar diagnostics o preparar una mutación estructural en un proyecto TypeScript/JavaScript gobernado por el compilador. Preferir las tools compiler-backed del servidor `ast` para esas operaciones y revisar cualquier plan antes de aplicarlo.
18
+
19
+ No forzar un roundtrip AST para Markdown, configuración, comentarios ni una edición textual trivial en un archivo ya conocido. Si las tools `ast_*` no están disponibles, declarar el fallback y no presentar búsquedas textuales como evidencia compiler-backed.
20
+
15
21
  ## Cuándo usarlo
16
22
 
17
23
  Usar las tools del servidor MCP `ast` en proyectos TypeScript/JavaScript con `tsconfig.json` cuando haya que:
@@ -40,13 +46,13 @@ El setup falla cerrado ante un registro `ast` conflictivo. No eliminar ni reempl
40
46
 
41
47
  ## Runtime, soporte y errores
42
48
 
43
- El paquete requiere Node.js `>=22.5.0`. El target verificado para v0.7.2 es Linux x64 con Node.js 22.5.0 o la línea actual de Node.js 24 y el `mv` de GNU coreutils requerido por la publicación atómica de evidencia. Otras arquitecturas Linux, sistemas sin ese primitivo, macOS y Windows están sin verificar.
49
+ El paquete requiere Node.js `>=22.5.0`. La matriz de release de v0.8.1 cubre Linux x64 con Node.js 22.5.0 y la línea actual de Node.js 24, incluida la publicación de archivos gestionados por setup. Esa mutación requiere además GNU coreutils 9.7 `mv` con `--update=none-fail`, `--exchange`, `--no-copy` y `--no-target-directory`, GNU coreutils `ln -L -T`, y procfs en `/proc/self/fd` con `O_DIRECTORY`/`O_NOFOLLOW`. Otras arquitecturas Linux, sistemas sin esos primitivos, macOS y Windows están sin verificar.
44
50
 
45
51
  El transporte soportado es stdio local bajo los permisos filesystem del usuario que invoca el proceso. No asumir autenticación HTTP, sandbox, aislamiento entre tenants ni una frontera segura para clientes remotos o no confiables.
46
52
 
47
53
  La persistencia del índice está deshabilitada por default. El modo `canary` requiere `AST_SYMBOL_INDEX_PERSISTENCE=canary` y un `AST_SYMBOL_INDEX_CACHE_ROOT` absoluto y aislado; `enabled` sigue sin publicarse y falla cerrado a memoria con `enabled_not_released`.
48
54
 
49
- Los errores públicos usan un vocabulario cerrado dentro de `{ "error": { "code", "message", "correlation_id" } }`, con `isError: true`. Conservar el `correlation_id` al reportar un fallo; no pedir ni exponer stacks, paths absolutos, source, cache, argumentos crudos, environment ni credenciales para diagnosticarlo.
55
+ Los errores públicos de las tools MCP usan un vocabulario cerrado dentro de `{ "error": { "code", "message", "correlation_id" } }`, con `isError: true`. Conservar el `correlation_id` al reportar un fallo; no pedir ni exponer stacks, paths absolutos, source, cache, argumentos crudos, environment ni credenciales para diagnosticarlo. Los errores del comando local `setup` pueden incluir un path destino acotado para permitir inspeccionar una escritura incierta o pendiente, pero no argumentos, environment, credenciales ni output crudo del proveedor.
50
56
 
51
57
  ## Flujo compacto de lectura
52
58
 
@@ -0,0 +1,3 @@
1
+ Load the `structural-code-editing` skill before semantic TypeScript/JavaScript navigation, reference or impact analysis, compiler diagnostics, or structural mutation in a compiler-governed project. Prefer the compiler-backed `ast` MCP tools for symbols, references, impact, diagnostics, and reviewed structural mutations.
2
+
3
+ Use ordinary file tools for Markdown, configuration, comments, and trivial text edits in already known files. If the `ast` tools are unavailable, disclose the fallback and never describe textual search as compiler-backed evidence.
@@ -0,0 +1,50 @@
1
+ {
2
+ "schema_version": 1,
3
+ "algorithm": "sha256",
4
+ "current": {
5
+ "version": "4.2.0",
6
+ "sha256": "43a07546fcc8417b5e937881e20d4bb87e57bba0bce1ae0c5bb58d75a6f75870"
7
+ },
8
+ "predecessors": [
9
+ {
10
+ "version": "3.3.0",
11
+ "sha256": "61251830f0a247d63873fbe6a912187a36ff99489241c24c5b2558563f79ab7f",
12
+ "npm_versions": ["0.3.0"]
13
+ },
14
+ {
15
+ "version": "3.4.0",
16
+ "sha256": "b9f75a8cb4900f5c6ad652c106d373ea1e951c8757870b4424a59469dd3b0868",
17
+ "npm_versions": ["0.4.0"]
18
+ },
19
+ {
20
+ "version": "4.0.0",
21
+ "sha256": "fbc6a9eeb11d3e4ecb5a585283a49b0e4df102b413e7fd82316c2f6e44b2cd56",
22
+ "npm_versions": ["0.5.0", "0.5.1"]
23
+ },
24
+ {
25
+ "version": "4.0.0",
26
+ "sha256": "622ab59511396f4acf289bad39fb2a03114352f6dc8556f12b37bde98d67a6b3",
27
+ "npm_versions": ["0.6.0"]
28
+ },
29
+ {
30
+ "version": "4.1.0",
31
+ "sha256": "e10f89e8834c97fdb68157b7c5d97ebe59311fb8bbcca0f5f26fb47d684cbf52",
32
+ "npm_versions": ["0.7.0"]
33
+ },
34
+ {
35
+ "version": "4.1.0",
36
+ "sha256": "672307b01a93a16e395d40b0b58e06cfca34b54ffd05b697ef471633bc476ad1",
37
+ "npm_versions": ["0.7.1"]
38
+ },
39
+ {
40
+ "version": "4.1.0",
41
+ "sha256": "6284fbeaf054947d20693d317604ceabf5a53d32e3c836855a321e5e26766e29",
42
+ "npm_versions": ["0.7.2"]
43
+ },
44
+ {
45
+ "version": "4.2.0",
46
+ "sha256": "b072ccf68ebd50a825dc9fdbdaaf35cdbf7045ae5fb9a9d7a412dc7b702ee7a0",
47
+ "npm_versions": ["0.8.0"]
48
+ }
49
+ ]
50
+ }