@netanelyasi/agent-ready 0.2.0 → 0.2.2

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
@@ -14,7 +14,7 @@
14
14
 
15
15
  <p align="center">
16
16
  <img alt="Status" src="https://img.shields.io/badge/status-experimental-f59e0b?style=flat-square" />
17
- <img alt="Version" src="https://img.shields.io/badge/version-0.2.0-111827?style=flat-square" />
17
+ <img alt="Version" src="https://img.shields.io/badge/version-0.2.2-111827?style=flat-square" />
18
18
  <img alt="License" src="https://img.shields.io/badge/license-MIT-0f766e?style=flat-square" />
19
19
  <img alt="Runtime" src="https://img.shields.io/badge/runtime-Node.js-3c873a?style=flat-square" />
20
20
  <img alt="Built by BrainboxAI" src="https://img.shields.io/badge/by-BrainboxAI-111827?style=flat-square" />
@@ -76,7 +76,7 @@ CODEMAP.md # repository map for navigation
76
76
  .agent-ready/report.md # readiness score and findings
77
77
  .agent-ready/recommendations.md
78
78
  .agent-ready/hooks/README.md
79
- .agent-ready/skills/*/SKILL.md
79
+ .claude/skills/*/SKILL.md # Claude Code-loadable skills
80
80
  apps/*/CLAUDE.md # generated for detected monorepo workspaces
81
81
  ```
82
82
 
@@ -122,6 +122,14 @@ Preview generated files:
122
122
  agent-ready init /path/to/project --dry-run
123
123
  ```
124
124
 
125
+ Preview generated file contents:
126
+
127
+ ```bash
128
+ agent-ready init /path/to/project --dry-run --verbose
129
+ ```
130
+
131
+ > Run `agent-ready init` manually from your terminal, not from inside an autonomous coding agent. It writes agent harness files such as `CLAUDE.md`, `.claude/settings.json`, and `.claude/hooks/*`, which some agent security classifiers correctly treat as self-modification.
132
+
125
133
  Generate the harness:
126
134
 
127
135
  ```bash
@@ -194,7 +202,7 @@ Generated hooks include:
194
202
  - `PreToolUse` for `Write|Edit|MultiEdit` — blocks edits to generated/noisy paths such as `node_modules`, `dist`, `build`, `coverage`, `.next`, `vendor`, and `*.generated.*`.
195
203
  - `PostToolUse` for `Write|Edit|MultiEdit` — reminds the agent which local validation command to run after edits.
196
204
 
197
- ### `.agent-ready/skills/*/SKILL.md`
205
+ ### `.claude/skills/*/SKILL.md`
198
206
 
199
207
  On-demand task expertise. Examples:
200
208
 
@@ -327,10 +335,10 @@ Generating 14 files:
327
335
  - created: .agent-ready/report.md
328
336
  - created: .agent-ready/recommendations.md
329
337
  - created: .agent-ready/hooks/README.md
330
- - created: .agent-ready/skills/codebase-navigation/SKILL.md
331
- - created: .agent-ready/skills/validation/SKILL.md
332
- - created: .agent-ready/skills/nextjs-hydration/SKILL.md
333
- - created: .agent-ready/skills/supabase-debugging/SKILL.md
338
+ - created: .claude/skills/codebase-navigation/SKILL.md
339
+ - created: .claude/skills/validation/SKILL.md
340
+ - created: .claude/skills/nextjs-hydration/SKILL.md
341
+ - created: .claude/skills/supabase-debugging/SKILL.md
334
342
  - created: apps/web/CLAUDE.md
335
343
  - created: packages/db/CLAUDE.md
336
344
  ```
@@ -355,12 +363,20 @@ agent-ready init [path] --dry-run
355
363
 
356
364
  Show which files would be generated without writing anything.
357
365
 
366
+ ```bash
367
+ agent-ready init [path] --dry-run --verbose
368
+ ```
369
+
370
+ Show the generated contents too. Use this before deciding whether to run `init` for real or manually merge proposed files.
371
+
358
372
  ```bash
359
373
  agent-ready init [path] --force
360
374
  ```
361
375
 
362
376
  Overwrite existing files instead of writing `*.agent-ready-proposed`.
363
377
 
378
+ When existing harness files are present, default `init` writes `*.agent-ready-proposed` files. Review and manually merge them; `agent-ready` does not assume its generated `CLAUDE.md` is better than a maintainer-authored one.
379
+
364
380
  ## Development
365
381
 
366
382
  Requirements:
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import path from "node:path";
3
3
  import { scoreReadiness } from "./analyzers/scoreReadiness.js";
4
4
  import { generateFiles } from "./generators/generate.js";
5
5
  import { scanProject } from "./scanner/scanProject.js";
6
- import { safeWriteFile } from "./utils/fs.js";
6
+ import { pathExists, safeWriteFile } from "./utils/fs.js";
7
7
  async function main() {
8
8
  const args = parseArgs(process.argv.slice(2));
9
9
  if (args.command === "help") {
@@ -18,17 +18,47 @@ async function main() {
18
18
  return;
19
19
  const files = generateFiles(scan, score, args.force);
20
20
  console.log(`\nGenerating ${files.length} files${args.dryRun ? " (dry-run)" : ""}:`);
21
+ let proposedCount = 0;
21
22
  for (const file of files) {
22
23
  const relative = path.relative(root, file.path).replaceAll(path.sep, "/");
23
24
  if (args.dryRun) {
24
- console.log(`- would write ${relative}`);
25
+ const result = await plannedWriteResult(file.path, args.force);
26
+ if (result === "proposed")
27
+ proposedCount += 1;
28
+ const target = result === "proposed" ? `${relative}.agent-ready-proposed` : relative;
29
+ console.log(`- would ${writeVerb(result)}: ${target}`);
30
+ if (args.verbose)
31
+ printDryRunContent(target, file.content);
25
32
  continue;
26
33
  }
27
34
  const result = await safeWriteFile(file.path, file.content, args.force);
35
+ if (result === "proposed")
36
+ proposedCount += 1;
28
37
  const target = result === "proposed" ? `${relative}.agent-ready-proposed` : relative;
29
38
  console.log(`- ${result}: ${target}`);
30
39
  }
31
40
  console.log("\nDone. Start with CODEMAP.md and .agent-ready/report.md.");
41
+ if (proposedCount > 0)
42
+ console.log(`Review and manually merge ${proposedCount} *.agent-ready-proposed file(s); existing harness files were not overwritten.`);
43
+ }
44
+ async function plannedWriteResult(filePath, force) {
45
+ const exists = await pathExists(filePath);
46
+ if (force)
47
+ return exists ? "overwritten" : "created";
48
+ return exists ? "proposed" : "created";
49
+ }
50
+ function writeVerb(result) {
51
+ if (result === "created")
52
+ return "create";
53
+ if (result === "overwritten")
54
+ return "overwrite";
55
+ return "propose";
56
+ }
57
+ function printDryRunContent(target, content) {
58
+ console.log(` --- ${target} begin ---`);
59
+ for (const line of content.trimEnd().split("\n"))
60
+ console.log(` ${line}`);
61
+ console.log(` --- ${target} end ---`);
32
62
  }
33
63
  function parseArgs(argv) {
34
64
  const command = argv[0] === "init" || argv[0] === "analyze" ? argv[0] : argv[0] ? "help" : "help";
@@ -65,7 +95,7 @@ function printSummary(scan, score) {
65
95
  }
66
96
  }
67
97
  function printHelp() {
68
- console.log(`agent-ready\n\nUsage:\n agent-ready analyze [path]\n agent-ready init [path] [--dry-run] [--force]\n\nCommands:\n analyze Scan project and print readiness summary\n init Generate CLAUDE.md, CODEMAP.md, .aiignore, settings, skills, and reports\n\nOptions:\n --dry-run Show files that would be written\n --force Overwrite existing files instead of writing *.agent-ready-proposed\n`);
98
+ console.log(`agent-ready\n\nUsage:\n agent-ready analyze [path]\n agent-ready init [path] [--dry-run] [--verbose] [--force]\n\nCommands:\n analyze Scan project and print readiness summary\n init Generate CLAUDE.md, CODEMAP.md, .aiignore, settings, skills, and reports\n\nOptions:\n --dry-run Show files that would be written\n --verbose With --dry-run, print generated file contents\n --force Overwrite existing files instead of writing *.agent-ready-proposed\n`);
69
99
  }
70
100
  main().catch((error) => {
71
101
  console.error(error instanceof Error ? error.message : String(error));
@@ -4,7 +4,7 @@ export function generateFiles(scan, score, force) {
4
4
  add("CLAUDE.md", generateClaudeMd(scan));
5
5
  add("CODEMAP.md", generateCodemap(scan));
6
6
  add(".aiignore", generateAiIgnore(scan));
7
- add(".claude/settings.json", generateClaudeSettings(scan));
7
+ add(".claude/settings.json", generateClaudeSettings());
8
8
  add(".claude/hooks/prevent-destructive.mjs", generatePreventDestructiveHook());
9
9
  add(".claude/hooks/protect-generated.mjs", generateProtectGeneratedHook());
10
10
  add(".claude/hooks/suggest-validation.mjs", generateSuggestValidationHook(scan));
@@ -15,7 +15,7 @@ export function generateFiles(scan, score, force) {
15
15
  add(`${subdir.dir}/CLAUDE.md`, subdir.content);
16
16
  }
17
17
  for (const skill of generateSkills(scan)) {
18
- add(`.agent-ready/skills/${skill.slug}/SKILL.md`, skill.content);
18
+ add(`.claude/skills/${skill.slug}/SKILL.md`, skill.content);
19
19
  }
20
20
  return files;
21
21
  function add(relativePath, content) {
@@ -114,7 +114,7 @@ function generateClaudeMd(scan) {
114
114
  if (scan.frameworks.includes("Next.js")) {
115
115
  lines.push("- Next.js detected: avoid hydration mismatches; access browser-only APIs only in client-safe code.");
116
116
  }
117
- lines.push("", "## Generated Skills", "See `.agent-ready/skills/*/SKILL.md` for task-specific instructions that should be loaded on demand, not copied wholesale into this file.", "");
117
+ lines.push("", "## Generated Skills", "See `.claude/skills/*/SKILL.md` for task-specific instructions that Claude Code can load on demand. Keep skills focused and avoid copying them wholesale into this file.", "");
118
118
  return lines.join("\n");
119
119
  }
120
120
  function commandLines(scan) {
@@ -132,6 +132,20 @@ function commandLines(scan) {
132
132
  lines.push("- No standard validation commands were detected. Add project-specific commands here.");
133
133
  return lines;
134
134
  }
135
+ // Flat one-command-per-rule list for the validation skill, e.g. "test: `npm run test`".
136
+ // Unlike commandLines (which nests for CLAUDE.md), this stays flat so it renders
137
+ // correctly as a SKILL.md bullet list.
138
+ function validationSkillRules(scan) {
139
+ const order = ["dev", "build", "test", "lint", "typecheck", "format"];
140
+ const rules = [];
141
+ for (const name of order) {
142
+ for (const command of (scan.commands[name] ?? []).slice(0, 8))
143
+ rules.push(`${name}: \`${command}\``);
144
+ }
145
+ if (!rules.length)
146
+ rules.push("No standard validation commands were detected. Add project-specific commands here.");
147
+ return rules;
148
+ }
135
149
  function generateCodemap(scan) {
136
150
  return [
137
151
  `# ${scan.name} — CODEMAP`,
@@ -196,33 +210,40 @@ function externalImportLines(scan) {
196
210
  function generateAiIgnore(scan) {
197
211
  const base = [
198
212
  "# Generated by agent-ready",
199
- "node_modules/",
200
- ".next/",
201
- ".nuxt/",
202
- "dist/",
203
- "build/",
204
213
  "coverage/",
205
- ".turbo/",
206
214
  ".cache/",
207
- ".venv/",
208
- "venv/",
209
- "__pycache__/",
210
- "vendor/",
211
- "target/",
212
- "bin/",
213
- "obj/",
214
215
  "*.log",
215
216
  "*.lock",
216
217
  "generated/",
217
218
  "**/*.generated.*",
218
219
  ];
220
+ const add = (...patterns) => {
221
+ for (const pattern of patterns)
222
+ if (!base.includes(pattern))
223
+ base.push(pattern);
224
+ };
225
+ if (scan.packageManager || scan.languages.some((language) => ["TypeScript", "JavaScript", "Svelte"].includes(language)))
226
+ add("node_modules/", "dist/", "build/", ".turbo/");
227
+ if (scan.frameworks.includes("Next.js"))
228
+ add(".next/");
229
+ if (scan.frameworks.includes("Nuxt"))
230
+ add(".nuxt/");
231
+ if (scan.languages.includes("Python"))
232
+ add(".venv/", "venv/", "__pycache__/");
233
+ if (scan.languages.includes("Rust"))
234
+ add("target/");
235
+ if (scan.languages.includes("Go"))
236
+ add("bin/");
237
+ if (scan.languages.includes("C#"))
238
+ add("bin/", "obj/");
239
+ if (scan.languages.includes("PHP") || scan.frameworks.some((framework) => framework.includes("Composer")))
240
+ add("vendor/");
219
241
  for (const noisy of scan.noisyPaths)
220
- if (!base.includes(`${noisy}/`))
221
- base.push(`${noisy}/`);
222
- return `${[...new Set(base)].join("\n")}\n`;
242
+ add(`${noisy}/`);
243
+ return `${base.join("\n")}\n`;
223
244
  }
224
- function generateClaudeSettings(scan) {
225
- const deny = [
245
+ function generateClaudeSettings() {
246
+ const deniedPathPatterns = [
226
247
  "node_modules/**",
227
248
  ".next/**",
228
249
  "dist/**",
@@ -233,6 +254,8 @@ function generateClaudeSettings(scan) {
233
254
  "generated/**",
234
255
  "**/*.generated.*",
235
256
  ];
257
+ const deniedWriteTools = ["Write", "Edit", "MultiEdit"];
258
+ const deny = deniedWriteTools.flatMap((tool) => deniedPathPatterns.map((pattern) => `${tool}(${pattern})`));
236
259
  const settings = {
237
260
  permissions: { deny: [...new Set(deny)] },
238
261
  hooks: {
@@ -245,7 +268,7 @@ function generateClaudeSettings(scan) {
245
268
  command: "node",
246
269
  args: ["${CLAUDE_PROJECT_DIR}/.claude/hooks/prevent-destructive.mjs"],
247
270
  timeout: 10,
248
- statusMessage: "Checking command safety",
271
+ statusMessage: "agent-ready: Checking command safety",
249
272
  },
250
273
  ],
251
274
  },
@@ -257,7 +280,7 @@ function generateClaudeSettings(scan) {
257
280
  command: "node",
258
281
  args: ["${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-generated.mjs"],
259
282
  timeout: 10,
260
- statusMessage: "Checking generated/noisy path safety",
283
+ statusMessage: "agent-ready: Checking generated/noisy path safety",
261
284
  },
262
285
  ],
263
286
  },
@@ -271,13 +294,12 @@ function generateClaudeSettings(scan) {
271
294
  command: "node",
272
295
  args: ["${CLAUDE_PROJECT_DIR}/.claude/hooks/suggest-validation.mjs"],
273
296
  timeout: 10,
274
- statusMessage: "Suggesting validation",
297
+ statusMessage: "agent-ready: Suggesting validation",
275
298
  },
276
299
  ],
277
300
  },
278
301
  ],
279
302
  },
280
- agentReady: { generatedBy: "agent-ready", project: scan.name },
281
303
  };
282
304
  return `${JSON.stringify(settings, null, 2)}\n`;
283
305
  }
@@ -459,7 +481,7 @@ function hookRecommendations(scan) {
459
481
  function generateSkills(scan) {
460
482
  const skills = [
461
483
  { slug: "codebase-navigation", content: skill("codebase-navigation", "Use when starting work in this repository or when a task spans unfamiliar directories.", ["Read CODEMAP.md first.", "Use narrow searches from the relevant directory before global search.", "Prefer symbol/reference search when LSP is available.", "Do not inspect ignored/generated directories unless explicitly needed."]) },
462
- { slug: "validation", content: skill("validation", "Use after code edits or before declaring a task complete.", [...commandLines(scan).map((line) => line.replace(/^[- ]+/, "")), "Run the narrowest relevant command first.", "If validation cannot be run, report the exact reason."]) },
484
+ { slug: "validation", content: skill("validation", "Use after code edits or before declaring a task complete.", [...validationSkillRules(scan), "Run the narrowest relevant command first.", "If validation cannot be run, report the exact reason."]) },
463
485
  ];
464
486
  if (scan.frameworks.includes("Next.js"))
465
487
  skills.push({ slug: "nextjs-hydration", content: skill("nextjs-hydration", "Use when editing Next.js/React components, routes, or client/server boundaries.", ["Do not read localStorage/sessionStorage/window/document during server render or initial state.", "Use useEffect or guarded client-only code for browser APIs.", "Avoid Math.random() or new Date() in render paths that must hydrate identically.", "Keep server/client boundaries explicit."]) });
@@ -472,7 +494,28 @@ function generateSkills(scan) {
472
494
  return skills;
473
495
  }
474
496
  function skill(name, description, rules) {
475
- return [`# ${name}`, "", `Description: ${description}`, "", "## Rules", ...rules.map((rule) => `- ${rule}`), ""].join("\n");
497
+ return [
498
+ "---",
499
+ `name: ${name}`,
500
+ `description: ${yamlScalar(description)}`,
501
+ "---",
502
+ "",
503
+ `# ${name}`,
504
+ "",
505
+ description,
506
+ "",
507
+ "## Rules",
508
+ ...rules.map((rule) => `- ${rule}`),
509
+ "",
510
+ ].join("\n");
511
+ }
512
+ // Claude Code parses SKILL.md frontmatter as YAML. Quote scalars that contain
513
+ // characters YAML would otherwise treat as structure (`:`, leading `#`, etc.).
514
+ function yamlScalar(value) {
515
+ if (/^[^\s].*[:#]|^[#&*!|>%@`"']|:\s|\s#/.test(value)) {
516
+ return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
517
+ }
518
+ return value;
476
519
  }
477
520
  function list(items) {
478
521
  return items.length ? items.join(", ") : "none detected";
@@ -10,6 +10,7 @@ const LANGUAGE_EXTS = {
10
10
  "C#": [".cs"],
11
11
  Go: [".go"],
12
12
  Rust: [".rs"],
13
+ Svelte: [".svelte"],
13
14
  "C/C++": [".c", ".cc", ".cpp", ".h", ".hpp"],
14
15
  };
15
16
  export async function scanProject(rootInput) {
@@ -263,12 +264,12 @@ async function harnessFileState(root, relativePath) {
263
264
  if (!exists)
264
265
  return { exists: false, generatedByAgentReady: false, countsAsMaintainerAuthored: false };
265
266
  const text = await readText(fullPath);
266
- const generatedByAgentReady = Boolean(text && /generated by `?agent-ready`?|agentReady|Generated by agent-ready/i.test(text));
267
+ const generatedByAgentReady = Boolean(text && /generated by `?agent-ready`?|agent-ready:|Generated by agent-ready/i.test(text));
267
268
  return { exists: true, generatedByAgentReady, countsAsMaintainerAuthored: !generatedByAgentReady };
268
269
  }
269
270
  async function analyzeCodeGraph(root, files, packages) {
270
271
  const sourceFiles = files
271
- .filter((file) => /\.(tsx?|jsx?|mjs|cjs|py|go|rs)$/.test(file))
272
+ .filter((file) => /\.(tsx?|jsx?|mjs|cjs|py|go|rs|svelte)$/.test(file))
272
273
  .filter((file) => !file.endsWith(".d.ts"))
273
274
  .filter((file) => !file.includes(`${path.sep}dist${path.sep}`) && !file.includes(`${path.sep}node_modules${path.sep}`) && !file.includes(`${path.sep}target${path.sep}`))
274
275
  .slice(0, 2000);
@@ -342,10 +343,6 @@ function detectEntryPoints(root, packages, sourceSet) {
342
343
  ["src/main.js", "application entry", "conventional app entry"],
343
344
  ["src/server.ts", "server entry", "conventional server entry"],
344
345
  ["src/server.js", "server entry", "conventional server entry"],
345
- ["app/page.tsx", "Next.js route", "App Router page"],
346
- ["app/layout.tsx", "Next.js layout", "App Router layout"],
347
- ["src/app/page.tsx", "Next.js route", "App Router page"],
348
- ["pages/index.tsx", "Next.js route", "Pages Router index"],
349
346
  ["main.py", "Python entry", "conventional Python entry"],
350
347
  ["app.py", "Python app", "conventional Python app entry"],
351
348
  ["src/main.py", "Python entry", "conventional Python entry"],
@@ -363,10 +360,9 @@ function detectEntryPoints(root, packages, sourceSet) {
363
360
  if (!source.startsWith(packageRoot))
364
361
  continue;
365
362
  const local = source.slice(packageRoot.length);
366
- if (/^pages\/api\/.+\.(tsx?|jsx?)$/.test(local))
367
- entries.set(source, { path: source, kind: "API route", reason: "Next.js API route" });
368
- if (/^app\/.+\/(page|route)\.(tsx?|jsx?)$/.test(local))
369
- entries.set(source, { path: source, kind: "Next.js route", reason: "App Router route/page" });
363
+ const frameworkEntry = frameworkEntryPoint(local);
364
+ if (frameworkEntry)
365
+ entries.set(source, { path: source, ...frameworkEntry });
370
366
  if (/^cmd\/[^/]+\/main\.go$/.test(local))
371
367
  entries.set(source, { path: source, kind: "Go command", reason: "cmd/*/main.go" });
372
368
  if (/^src\/bin\/[^/]+\.rs$/.test(local))
@@ -380,6 +376,34 @@ function detectEntryPoints(root, packages, sourceSet) {
380
376
  }
381
377
  return [...entries.values()].sort((a, b) => a.path.localeCompare(b.path)).slice(0, 40);
382
378
  }
379
+ function frameworkEntryPoint(localPath) {
380
+ if (/^(src\/)?app\/(page|layout|route)\.(tsx?|jsx?)$/.test(localPath)) {
381
+ const file = localPath.includes("/layout.") ? "layout" : localPath.includes("/route.") ? "route handler" : "page";
382
+ return { kind: `Next.js ${file}`, reason: "App Router root entry" };
383
+ }
384
+ if (/^(src\/)?app\/.+\/(page|layout|route|loading|error|not-found)\.(tsx?|jsx?)$/.test(localPath)) {
385
+ return { kind: "Next.js route", reason: "App Router route segment entry" };
386
+ }
387
+ if (/^(src\/)?pages\/index\.(tsx?|jsx?)$/.test(localPath))
388
+ return { kind: "Next.js route", reason: "Pages Router index" };
389
+ if (/^(src\/)?pages\/(api\/.+|.+)\.(tsx?|jsx?)$/.test(localPath))
390
+ return { kind: "Next.js route", reason: "Pages Router route/API entry" };
391
+ if (/^(src\/)?middleware\.(tsx?|jsx?)$/.test(localPath))
392
+ return { kind: "Next.js middleware", reason: "Next.js request middleware entry" };
393
+ if (/^next\.config\.(tsx?|jsx?|mjs|cjs)$/.test(localPath))
394
+ return { kind: "Next.js config", reason: "Next.js configuration entry" };
395
+ if (/^app\/(root|entry\.(client|server))\.(tsx?|jsx?)$/.test(localPath))
396
+ return { kind: "Remix entry", reason: "Remix root/client/server entry" };
397
+ if (/^app\/routes\/.+\.(tsx?|jsx?)$/.test(localPath))
398
+ return { kind: "Remix route", reason: "Remix route module" };
399
+ if (/^src\/routes\/(\+page|\+layout|\+server)\.(svelte|tsx?|jsx?)$/.test(localPath))
400
+ return { kind: "SvelteKit route", reason: "SvelteKit root route entry" };
401
+ if (/^src\/routes\/.+\/(\+page|\+layout|\+server)\.(svelte|tsx?|jsx?)$/.test(localPath))
402
+ return { kind: "SvelteKit route", reason: "SvelteKit route entry" };
403
+ if (/^src\/hooks(\.server)?\.(tsx?|jsx?)$/.test(localPath))
404
+ return { kind: "SvelteKit hook", reason: "SvelteKit lifecycle hook entry" };
405
+ return undefined;
406
+ }
383
407
  function extractImportSpecifiers(text, from) {
384
408
  const specifiers = new Set();
385
409
  const ext = path.posix.extname(from);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netanelyasi/agent-ready",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Generate an AI-agent harness for any codebase: CLAUDE.md, CODEMAP.md, skills, ignore rules, and readiness reports.",
5
5
  "type": "module",
6
6
  "bin": {