@ivannikov-pro/ai-context-surgeon 1.0.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.
Files changed (87) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +372 -0
  3. package/bin/catalog.js +153 -0
  4. package/bin/cli.js +380 -0
  5. package/bin/installer.js +135 -0
  6. package/bin/prompts.js +371 -0
  7. package/checklists/phase-1-analysis.md +58 -0
  8. package/checklists/phase-2-planning.md +67 -0
  9. package/checklists/phase-3-restructuring.md +77 -0
  10. package/checklists/phase-4-documentation.md +111 -0
  11. package/checklists/phase-5-validation.md +91 -0
  12. package/examples/before-after/README.md +139 -0
  13. package/examples/ideal-monorepo/README.md +127 -0
  14. package/knowledge/agent-context-system/artifacts/guide.md +183 -0
  15. package/knowledge/agent-context-system/artifacts/knowledge.md +177 -0
  16. package/knowledge/agent-context-system/artifacts/skills.md +101 -0
  17. package/knowledge/agent-context-system/artifacts/workflows.md +143 -0
  18. package/knowledge/agent-context-system/metadata.json +26 -0
  19. package/knowledge/agent-context-system/timestamps.json +5 -0
  20. package/knowledge/agent-vulnerabilities/LICENSE +21 -0
  21. package/knowledge/agent-vulnerabilities/artifacts/stealth_injection.md +110 -0
  22. package/knowledge/agent-vulnerabilities/artifacts/vulnerabilities.md +232 -0
  23. package/knowledge/agent-vulnerabilities/metadata.json +14 -0
  24. package/knowledge/agent-vulnerabilities/timestamps.json +5 -0
  25. package/knowledge/power-words-dictionary/LICENSE +21 -0
  26. package/knowledge/power-words-dictionary/artifacts/dictionary.md +231 -0
  27. package/knowledge/power-words-dictionary/artifacts/prompt_amplifier.py +381 -0
  28. package/knowledge/power-words-dictionary/metadata.json +14 -0
  29. package/knowledge/power-words-dictionary/timestamps.json +5 -0
  30. package/package.json +77 -0
  31. package/roles/README.md +81 -0
  32. package/roles/architect.md +203 -0
  33. package/roles/inspector.md +232 -0
  34. package/roles/librarian.md +176 -0
  35. package/roles/scout.md +169 -0
  36. package/roles/surgeon.md +172 -0
  37. package/roles/tuner.md +204 -0
  38. package/skills/annotate-jsdoc/SKILL.md +262 -0
  39. package/skills/prompt-engineering/LICENSE +21 -0
  40. package/skills/prompt-engineering/SKILL.md +235 -0
  41. package/skills/prompt-engineering/scripts/extract_instructions.py +416 -0
  42. package/skills/prompt-engineering/scripts/prompt_amplifier.py +381 -0
  43. package/skills/prompt-engineering/scripts/prompt_diff_tracker.py +281 -0
  44. package/skills/prompt-engineering/scripts/prompt_dna_analyzer.py +692 -0
  45. package/skills/prompt-engineering/scripts/templates/code_review.md +47 -0
  46. package/skills/prompt-engineering/scripts/templates/dump_extraction.md +59 -0
  47. package/skills/prompt-engineering/scripts/templates/multi_agent_orchestration.md +100 -0
  48. package/skills/prompt-engineering/scripts/templates/prompt_audit.md +106 -0
  49. package/skills/prompt-engineering/scripts/templates/stealth_injection.md +110 -0
  50. package/skills/prompt-engineering/scripts/templates/task_automation.md +87 -0
  51. package/skills/prompt-engineering/workflows/amplify.md +36 -0
  52. package/skills/prompt-engineering/workflows/audit.md +55 -0
  53. package/skills/prompt-engineering/workflows/context-dump.md +90 -0
  54. package/skills/prompt-engineering/workflows/diff.md +44 -0
  55. package/strategy/bash-guide.md +134 -0
  56. package/strategy/context-exclusion.md +220 -0
  57. package/strategy/context-weight-theory.md +49 -0
  58. package/strategy/file-navigation-header.md +562 -0
  59. package/strategy/jsdoc-guide.md +596 -0
  60. package/strategy/monorepo_strategy.md +726 -0
  61. package/strategy/package-json-guide.md +541 -0
  62. package/templates/AGENTS.md.template +148 -0
  63. package/templates/antigravityignore.template +64 -0
  64. package/templates/cursorrules.template +7 -0
  65. package/templates/knowledge-item.template +44 -0
  66. package/templates/package-json-ideal.template +26 -0
  67. package/templates/package-readme.template +45 -0
  68. package/templates/publish-meta.template +34 -0
  69. package/templates/skill.template +50 -0
  70. package/templates/workflow.template +33 -0
  71. package/tools/analyze-package-json.sh +213 -0
  72. package/tools/analyze-structure.sh +101 -0
  73. package/tools/audit-jsdoc.sh +176 -0
  74. package/tools/check-fnh-freshness.sh +74 -0
  75. package/tools/detect-circular-deps.sh +147 -0
  76. package/tools/detect-god-files.sh +71 -0
  77. package/tools/enforce-god-files.sh +112 -0
  78. package/tools/enrich-package-json.js +311 -0
  79. package/tools/generate-jsdoc-headers.sh +109 -0
  80. package/tools/generate-source-map.sh +71 -0
  81. package/tools/lint-imports.sh +123 -0
  82. package/tools/measure-context-cost.sh +206 -0
  83. package/tools/scan-fnh.sh +174 -0
  84. package/tools/shared/config.sh +53 -0
  85. package/tools/validate-context-hygiene.sh +52 -0
  86. package/tools/validate-context-weight.sh +100 -0
  87. package/tools/validate-naming.sh +98 -0
package/bin/cli.js ADDED
@@ -0,0 +1,380 @@
1
+ #!/usr/bin/env node
2
+ /** Category: Tool — AI-native CLI entry point | COMMANDS: catalog, role, strategy, skill, prompt, template, checklist, knowledge, radar, godfiles, circular, context-weight, sourcemaps, init | DEPS: @clack/prompts, commander, ./catalog.js, ./prompts.js */
3
+
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { spawn } from "node:child_process";
8
+
9
+ import { intro, outro, select, spinner, text, isCancel, cancel } from "@clack/prompts";
10
+ import pc from "picocolors";
11
+ import { program } from "commander";
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+ const sourceDir = path.resolve(__dirname, "..");
16
+
17
+ // Read version from package.json
18
+ const pkg = JSON.parse(fs.readFileSync(path.join(sourceDir, "package.json"), "utf8"));
19
+
20
+ function runScript(scriptPath, args = []) {
21
+ return new Promise((resolve, reject) => {
22
+ const fullPath = path.join(sourceDir, scriptPath);
23
+ const child = spawn("bash", [fullPath, ...args], {
24
+ stdio: "inherit",
25
+ cwd: process.cwd(),
26
+ });
27
+ child.on("close", (code) => {
28
+ if (code !== 0) reject(new Error(`Script exited with code ${code}`));
29
+ else resolve();
30
+ });
31
+ child.on("error", (err) => reject(err));
32
+ });
33
+ }
34
+
35
+ program
36
+ .name("ai-context-surgeon")
37
+ .description("AI Context Surgeon — AI-native Knowledge API for monorepo optimization.\nAll content accessible on-demand. No install needed for tools.")
38
+ .version(pkg.version);
39
+
40
+ // ─── Tier 1: Discovery ─────────────────────────────────────────────
41
+
42
+ program
43
+ .command("catalog")
44
+ .description("List all available content: tools, roles, strategies, prompts, skills, templates, checklists, knowledge")
45
+ .action(async () => {
46
+ const { CATALOG, listCategory } = await import("./catalog.js");
47
+
48
+ const icons = {
49
+ tools: "🔧",
50
+ roles: "🎭",
51
+ strategies: "📖",
52
+ prompts: "💡",
53
+ skills: "🧩",
54
+ templates: "📝",
55
+ checklists: "📋",
56
+ knowledge: "🧠",
57
+ };
58
+
59
+ console.log(pc.cyan(`\n📋 ai-context-surgeon v${pkg.version} — AI Context Optimization Toolkit\n`));
60
+
61
+ // Tools
62
+ console.log(pc.bold(`${icons.tools} TOOLS (run directly):`));
63
+ for (const item of listCategory("tools")) {
64
+ console.log(` ${pc.green(item.key.padEnd(22))} ${item.description}`);
65
+ }
66
+
67
+ // Roles
68
+ console.log(pc.bold(`\n${icons.roles} ROLES (agent prompts):`));
69
+ for (const item of listCategory("roles")) {
70
+ console.log(` ${pc.green(item.key.padEnd(22))} ${item.description}`);
71
+ }
72
+
73
+ // Strategies
74
+ console.log(pc.bold(`\n${icons.strategies} STRATEGIES (reference docs):`));
75
+ for (const item of listCategory("strategies")) {
76
+ console.log(` ${pc.green(item.key.padEnd(22))} ${item.description}`);
77
+ }
78
+
79
+ // Prompts
80
+ console.log(pc.bold(`\n${icons.prompts} PROMPTS (condensed task instructions):`));
81
+ const { listPrompts } = await import("./prompts.js");
82
+ for (const item of listPrompts()) {
83
+ console.log(` ${pc.green(item.value.padEnd(22))} ${item.label}`);
84
+ }
85
+
86
+ // Skills
87
+ console.log(pc.bold(`\n${icons.skills} SKILLS (reusable procedures):`));
88
+ for (const item of listCategory("skills")) {
89
+ console.log(` ${pc.green(item.key.padEnd(22))} ${item.description}`);
90
+ }
91
+
92
+ // Templates
93
+ console.log(pc.bold(`\n${icons.templates} TEMPLATES (file seeds):`));
94
+ for (const item of listCategory("templates")) {
95
+ console.log(` ${pc.green(item.key.padEnd(22))} ${item.description}`);
96
+ }
97
+
98
+ // Checklists
99
+ console.log(pc.bold(`\n${icons.checklists} CHECKLISTS (phase tasks):`));
100
+ for (const item of listCategory("checklists")) {
101
+ console.log(` ${pc.green(item.key.padEnd(22))} ${item.description}`);
102
+ }
103
+
104
+ // Knowledge
105
+ console.log(pc.bold(`\n${icons.knowledge} KNOWLEDGE (reference items):`));
106
+ for (const item of listCategory("knowledge")) {
107
+ console.log(` ${pc.green(item.key.padEnd(22))} ${item.description}`);
108
+ }
109
+
110
+ console.log(pc.dim(`\nUsage: npx @ivannikov-pro/ai-context-surgeon@latest <category> <name>\n`));
111
+ });
112
+
113
+ // ─── Tier 2: Content Access ────────────────────────────────────────
114
+
115
+ function registerContentCommand(cmdName, categoryKey, description) {
116
+ program
117
+ .command(`${cmdName} [name]`)
118
+ .description(description)
119
+ .option("-o, --output <path>", "Save output to a file instead of stdout")
120
+ .action(async (name, options) => {
121
+ const { getContent, listCategory } = await import("./catalog.js");
122
+
123
+ if (!name) {
124
+ console.log(pc.cyan(`\nAvailable ${cmdName}s:\n`));
125
+ for (const item of listCategory(categoryKey)) {
126
+ console.log(` ${pc.green(item.key.padEnd(22))} ${item.description}`);
127
+ }
128
+ console.log(pc.dim(`\nUsage: npx @ivannikov-pro/ai-context-surgeon@latest ${cmdName} <name>\n`));
129
+ console.log(pc.dim(` Add --output <path> to save to a file (e.g. --output .agents/${cmdName}-<name>.md)\n`));
130
+ return;
131
+ }
132
+
133
+ const content = getContent(categoryKey, name);
134
+ if (!content) {
135
+ console.error(pc.red(`\n❌ Unknown ${cmdName}: "${name}"\n`));
136
+ const items = listCategory(categoryKey);
137
+ console.log("Available:", items.map((i) => i.key).join(", "));
138
+ process.exit(1);
139
+ }
140
+
141
+ // Save to file or output to stdout
142
+ if (options.output) {
143
+ const outputPath = path.resolve(process.cwd(), options.output);
144
+ const outputDir = path.dirname(outputPath);
145
+ if (!fs.existsSync(outputDir)) {
146
+ fs.mkdirSync(outputDir, { recursive: true });
147
+ }
148
+ fs.writeFileSync(outputPath, content);
149
+ console.log(pc.green(`✅ Saved ${cmdName} "${name}" → ${options.output}`));
150
+ } else {
151
+ console.log(content);
152
+ }
153
+ });
154
+ }
155
+
156
+ registerContentCommand("role", "roles", "Get an agent role prompt (scout, architect, surgeon, librarian, tuner, inspector)");
157
+ registerContentCommand("strategy", "strategies", "Get a strategy document (fnh, monorepo, jsdoc, package-json, context-exclusion, bash, context-weight)");
158
+ registerContentCommand("skill", "skills", "Get a skill procedure (annotate-jsdoc, prompt-engineering)");
159
+ registerContentCommand("template", "templates", "Get a file template (agents, readme, package-json, workflow, skill, knowledge, ...)");
160
+ registerContentCommand("checklist", "checklists", "Get a phase checklist (1-5)");
161
+ registerContentCommand("knowledge", "knowledge", "Get a knowledge item (agent-context, vulnerabilities, power-words)");
162
+
163
+ // Prompt command — uses the condensed prompt library
164
+ program
165
+ .command("prompt [task]")
166
+ .description("Get a ready-to-use prompt for a task (fnh, jsdoc, readme, agent-rules, context-exclusion, god-file)")
167
+ .option("-l, --list", "List all available prompts")
168
+ .option("-o, --output <path>", "Save output to a file instead of stdout")
169
+ .action(async (task, options) => {
170
+ const { PROMPTS, listPrompts, getPrompt } = await import("./prompts.js");
171
+
172
+ if (options.list || !task) {
173
+ console.log(pc.cyan("\n💡 Available prompts:\n"));
174
+ for (const item of listPrompts()) {
175
+ console.log(` ${pc.bold(pc.green(item.value.padEnd(22)))} ${item.label}`);
176
+ }
177
+ console.log(pc.dim(`\nUsage: npx @ivannikov-pro/ai-context-surgeon@latest prompt <task>\n`));
178
+ console.log(pc.dim(` Add --output <path> to save to a file (e.g. --output .agents/prompt-fnh.md)\n`));
179
+ return;
180
+ }
181
+
182
+ const prompt = getPrompt(task);
183
+ if (!prompt) {
184
+ console.error(pc.red(`\n❌ Unknown prompt: "${task}"\n`));
185
+ console.log("Available:", Object.keys(PROMPTS).join(", "));
186
+ process.exit(1);
187
+ }
188
+
189
+ if (options.output) {
190
+ const outputPath = path.resolve(process.cwd(), options.output);
191
+ const outputDir = path.dirname(outputPath);
192
+ if (!fs.existsSync(outputDir)) {
193
+ fs.mkdirSync(outputDir, { recursive: true });
194
+ }
195
+ fs.writeFileSync(outputPath, prompt.prompt);
196
+ console.log(pc.green(`✅ Saved prompt "${task}" → ${options.output}`));
197
+ } else {
198
+ console.log(prompt.prompt);
199
+ }
200
+ });
201
+
202
+ // ─── Tier 3: Tool Execution ────────────────────────────────────────
203
+
204
+ program
205
+ .command("radar")
206
+ .description("Run Radar Scan — generate semantic FNH map of the repository")
207
+ .option("-d, --depth <level>", "Limit scan depth (e.g. 1, 2, all)")
208
+ .option("--no-legend", "Output pure text map without format legend")
209
+ .action(async (options) => {
210
+ const args = ["."];
211
+ if (options.depth) args.push(`--depth=${options.depth}`);
212
+ if (!options.legend) args.push("--no-legend");
213
+ await runScript("tools/scan-fnh.sh", args);
214
+ });
215
+
216
+ program
217
+ .command("godfiles")
218
+ .description("Find excessively large files (detect-god-files.sh)")
219
+ .action(async () => await runScript("tools/detect-god-files.sh", ["."]));
220
+
221
+ program
222
+ .command("circular")
223
+ .description("Detect circular dependencies")
224
+ .action(async () => await runScript("tools/detect-circular-deps.sh", ["."]));
225
+
226
+ program
227
+ .command("sourcemaps")
228
+ .description("Generate AI-friendly source maps")
229
+ .action(async () => await runScript("tools/generate-source-map.sh", ["."]));
230
+
231
+ program
232
+ .command("context-weight")
233
+ .description("Identify files with high Context Weight (LOC + import penalties)")
234
+ .option("--internal-scope <scopes>", "Treat these scopes as local imports (comma-separated)")
235
+ .action(async (options) => {
236
+ const args = ["."];
237
+ if (options.internalScope) args.push(`--internal-scope=${options.internalScope}`);
238
+ await runScript("tools/validate-context-weight.sh", args);
239
+ });
240
+
241
+ // ─── Tier 4: Lightweight Init ──────────────────────────────────────
242
+
243
+ program
244
+ .command("init")
245
+ .description("Lightweight setup — create AGENTS.md, .antigravityignore, and CI scripts (no file copying)")
246
+ .action(async () => {
247
+ const { initProject } = await import("./installer.js");
248
+ initProject();
249
+ });
250
+
251
+ // ─── Interactive Wizard (no args) ──────────────────────────────────
252
+
253
+ if (process.argv.length === 2) {
254
+ (async () => {
255
+ console.clear();
256
+ intro(pc.bgCyan(pc.black(" ❤️ ai-context-surgeon ")));
257
+
258
+ const action = await select({
259
+ message: "What would you like to do?",
260
+ options: [
261
+ { value: "catalog", label: "📋 Browse full catalog (tools, roles, strategies, prompts, ...)" },
262
+ { value: "radar", label: "🗺️ Run Radar Scan (scan-fnh)" },
263
+ { value: "role", label: "🎭 Read a role prompt" },
264
+ { value: "strategy", label: "📖 Read a strategy document" },
265
+ { value: "prompt", label: "💡 Get a condensed prompt for a task" },
266
+ { value: "skill", label: "🧩 Read a skill procedure" },
267
+ { value: "godfiles", label: "⚔️ Detect God Files" },
268
+ { value: "context-weight", label: "⚖️ Analyze Context Weight" },
269
+ { value: "circular", label: "🔄 Detect Circular Dependencies" },
270
+ { value: "init", label: "🚀 Init project (AGENTS.md + CI scripts)" },
271
+ { value: "help", label: "📖 Help / About" },
272
+ { value: "quit", label: "❌ Exit" },
273
+ ],
274
+ });
275
+
276
+ if (isCancel(action) || action === "quit") {
277
+ cancel("Operation cancelled.");
278
+ process.exit(0);
279
+ }
280
+
281
+ try {
282
+ if (action === "catalog") {
283
+ // Re-run with catalog command
284
+ program.parse(["node", "cli.js", "catalog"]);
285
+ } else if (action === "role") {
286
+ const { listCategory, getContent } = await import("./catalog.js");
287
+ const items = listCategory("roles");
288
+ const selected = await select({
289
+ message: "Which role?",
290
+ options: items.map((i) => ({ value: i.key, label: `${i.name} — ${i.description}` })),
291
+ });
292
+ if (isCancel(selected)) { cancel("Cancelled"); process.exit(0); }
293
+ outro(pc.blue(`\n--- Role: ${selected} ---\n`));
294
+ console.log(getContent("roles", selected));
295
+ } else if (action === "strategy") {
296
+ const { listCategory, getContent } = await import("./catalog.js");
297
+ const items = listCategory("strategies");
298
+ const selected = await select({
299
+ message: "Which strategy?",
300
+ options: items.map((i) => ({ value: i.key, label: `${i.name} — ${i.description}` })),
301
+ });
302
+ if (isCancel(selected)) { cancel("Cancelled"); process.exit(0); }
303
+ outro(pc.blue(`\n--- Strategy: ${selected} ---\n`));
304
+ console.log(getContent("strategies", selected));
305
+ } else if (action === "prompt") {
306
+ const { listPrompts, getPrompt } = await import("./prompts.js");
307
+ const items = listPrompts();
308
+ const selected = await select({
309
+ message: "Which prompt?",
310
+ options: items.map((p) => ({ value: p.value, label: p.label })),
311
+ });
312
+ if (isCancel(selected)) { cancel("Cancelled"); process.exit(0); }
313
+ const prompt = getPrompt(selected);
314
+ outro(pc.blue(`\n--- Prompt: ${prompt.name} ---\n`));
315
+ console.log(prompt.prompt);
316
+ } else if (action === "skill") {
317
+ const { listCategory, getContent } = await import("./catalog.js");
318
+ const items = listCategory("skills");
319
+ const selected = await select({
320
+ message: "Which skill?",
321
+ options: items.map((i) => ({ value: i.key, label: `${i.name} — ${i.description}` })),
322
+ });
323
+ if (isCancel(selected)) { cancel("Cancelled"); process.exit(0); }
324
+ outro(pc.blue(`\n--- Skill: ${selected} ---\n`));
325
+ console.log(getContent("skills", selected));
326
+ } else if (action === "init") {
327
+ const s = spinner();
328
+ s.start("Initializing ai-context-surgeon...");
329
+ const { initProject } = await import("./installer.js");
330
+ initProject();
331
+ s.stop("Initialization complete!");
332
+ outro(pc.green("Project configured. Run 'catalog' to see what's available."));
333
+ } else if (action === "help") {
334
+ outro(
335
+ pc.cyan(`📖 ai-context-surgeon — AI-native Knowledge API for monorepo optimization\n
336
+ ${pc.bold("catalog")} — Browse all available content
337
+ ${pc.bold("role <name>")} — Read an agent role prompt (scout, architect, ...)
338
+ ${pc.bold("strategy <name>")} — Read a strategy document (fnh, monorepo, ...)
339
+ ${pc.bold("prompt <task>")} — Get a condensed task prompt (fnh, jsdoc, ...)
340
+ ${pc.bold("skill <name>")} — Read a skill procedure (annotate-jsdoc, ...)
341
+ ${pc.bold("template <name>")} — Get a file template (agents, readme, ...)
342
+ ${pc.bold("checklist <n>")} — Get a phase checklist (1-5)
343
+ ${pc.bold("knowledge <name>")} — Read a knowledge item (agent-context, ...)
344
+ ${pc.bold("radar")} — Quick repo map via FNH headers
345
+ ${pc.bold("godfiles")} — Find files > 300 LOC
346
+ ${pc.bold("context-weight")} — Find high cognitive-load files
347
+ ${pc.bold("circular")} — Detect inter-package cycles
348
+ ${pc.bold("init")} — Lightweight project setup (AGENTS.md + CI)
349
+
350
+ All content runs from npm cache — always latest version, no install needed.
351
+ Usage: npx -y @ivannikov-pro/ai-context-surgeon@latest <command>`),
352
+ );
353
+ } else if (action === "radar") {
354
+ const depth = await text({
355
+ message: 'Depth limit (optional, e.g., 1, 2) or Enter for "all"',
356
+ placeholder: "all",
357
+ });
358
+ if (isCancel(depth)) { cancel("Cancelled"); process.exit(0); }
359
+ const args = ["."];
360
+ if (depth && depth.trim() !== "all") args.push(`--depth=${depth.trim()}`);
361
+ outro(pc.blue(`Running scan-fnh.sh ...\n`));
362
+ await runScript("tools/scan-fnh.sh", args);
363
+ } else if (action === "godfiles") {
364
+ outro(pc.blue(`Running detect-god-files.sh ...\n`));
365
+ await runScript("tools/detect-god-files.sh", ["."]);
366
+ } else if (action === "circular") {
367
+ outro(pc.blue(`Running detect-circular-deps.sh ...\n`));
368
+ await runScript("tools/detect-circular-deps.sh", ["."]);
369
+ } else if (action === "context-weight") {
370
+ outro(pc.blue(`Running validate-context-weight.sh ...\n`));
371
+ await runScript("tools/validate-context-weight.sh", ["."]);
372
+ }
373
+ } catch (err) {
374
+ cancel(pc.red(`Execution failed: ${err.message}`));
375
+ process.exit(1);
376
+ }
377
+ })();
378
+ } else {
379
+ program.parse(process.argv);
380
+ }
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ /** Category: Tool — Lightweight project initializer | EXPORTS: initProject | DEPS: fs, path */
3
+
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ const sourceDir = path.resolve(__dirname, "..");
11
+ const targetDir = process.cwd();
12
+
13
+ /**
14
+ * Lightweight project initialization.
15
+ * Only creates essential config files — NO file copying.
16
+ * Tools, roles, strategies run from npm cache via `npx`.
17
+ */
18
+ export function initProject() {
19
+ console.log("❤️ Initializing ai-context-surgeon for your project...\n");
20
+
21
+ const created = [];
22
+ const skipped = [];
23
+
24
+ // 1. Create .agents/ directory
25
+ const agentsDir = path.join(targetDir, ".agents");
26
+ if (!fs.existsSync(agentsDir)) {
27
+ fs.mkdirSync(agentsDir, { recursive: true });
28
+ }
29
+
30
+ // 2. Create AGENTS.md from template
31
+ const agentsFile = path.join(agentsDir, "AGENTS.md");
32
+ const templatePath = path.join(sourceDir, "templates", "AGENTS.md.template");
33
+ if (!fs.existsSync(agentsFile) && fs.existsSync(templatePath)) {
34
+ const template = fs.readFileSync(templatePath, "utf8");
35
+ const content = template + `
36
+ ## AI Context Surgeon
37
+
38
+ This project uses [ai-context-surgeon](https://github.com/ivannikov-pro/ai-context-surgeon) for context optimization.
39
+
40
+ All tools, roles, and strategies are available on-demand via:
41
+ \`\`\`bash
42
+ npx @ivannikov-pro/ai-context-surgeon@latest catalog # See everything available
43
+ npx @ivannikov-pro/ai-context-surgeon@latest role scout # Get a role prompt
44
+ npx @ivannikov-pro/ai-context-surgeon@latest prompt fnh # Get task instructions
45
+ npx @ivannikov-pro/ai-context-surgeon@latest radar # Run analysis tools
46
+ \`\`\`
47
+ `;
48
+ fs.writeFileSync(agentsFile, content);
49
+ created.push(".agents/AGENTS.md");
50
+ } else {
51
+ skipped.push(".agents/AGENTS.md (already exists)");
52
+ }
53
+
54
+ // 3. Create .antigravityignore from template
55
+ const ignoreFile = path.join(targetDir, ".antigravityignore");
56
+ const ignoreTemplate = path.join(sourceDir, "templates", "antigravityignore.template");
57
+ if (!fs.existsSync(ignoreFile) && fs.existsSync(ignoreTemplate)) {
58
+ fs.copyFileSync(ignoreTemplate, ignoreFile);
59
+ created.push(".antigravityignore");
60
+ } else {
61
+ skipped.push(".antigravityignore (already exists)");
62
+ }
63
+
64
+ // 4. Create .cursorrules / .windsurfrules from template
65
+ const cursorTemplate = path.join(sourceDir, "templates", "cursorrules.template");
66
+ if (fs.existsSync(cursorTemplate)) {
67
+ const rulesContent = fs.readFileSync(cursorTemplate, "utf8");
68
+
69
+ for (const filename of [".cursorrules", ".windsurfrules"]) {
70
+ const rulesPath = path.join(targetDir, filename);
71
+ if (!fs.existsSync(rulesPath)) {
72
+ fs.writeFileSync(rulesPath, rulesContent);
73
+ created.push(filename);
74
+ } else {
75
+ skipped.push(`${filename} (already exists)`);
76
+ }
77
+ }
78
+ }
79
+
80
+ // 5. Inject CI scripts into package.json (if Node.js project)
81
+ const packageJsonPath = path.join(targetDir, "package.json");
82
+ if (fs.existsSync(packageJsonPath)) {
83
+ try {
84
+ const pkgContent = fs.readFileSync(packageJsonPath, "utf8");
85
+ const pkg = JSON.parse(pkgContent);
86
+
87
+ if (!pkg.scripts) pkg.scripts = {};
88
+
89
+ let modified = false;
90
+
91
+ // CI scripts use npx to run tools — no local install needed
92
+ const ciScripts = {
93
+ "check:hygiene": "npx @ivannikov-pro/ai-context-surgeon@latest radar --no-legend",
94
+ "check:god-files": "npx @ivannikov-pro/ai-context-surgeon@latest godfiles",
95
+ };
96
+
97
+ for (const [name, cmd] of Object.entries(ciScripts)) {
98
+ if (!pkg.scripts[name]) {
99
+ pkg.scripts[name] = cmd;
100
+ modified = true;
101
+ }
102
+ }
103
+
104
+ if (modified) {
105
+ fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
106
+ created.push("package.json CI scripts (check:hygiene, check:god-files)");
107
+ } else {
108
+ skipped.push("package.json CI scripts (already exist)");
109
+ }
110
+ } catch (err) {
111
+ console.error(`⚠️ Failed to update package.json: ${err.message}`);
112
+ }
113
+ }
114
+
115
+ // Summary
116
+ console.log("✅ Created:");
117
+ for (const item of created) console.log(` ${item}`);
118
+
119
+ if (skipped.length) {
120
+ console.log("\nℹ️ Skipped:");
121
+ for (const item of skipped) console.log(` ${item}`);
122
+ }
123
+
124
+ console.log(`
125
+ 🎯 Next steps:
126
+ 1. Fill {{PLACEHOLDER}} values in .agents/AGENTS.md
127
+ 2. Run: npx @ivannikov-pro/ai-context-surgeon@latest catalog
128
+ 3. Run: npx @ivannikov-pro/ai-context-surgeon@latest radar
129
+ `);
130
+ }
131
+
132
+ // Allow direct execution
133
+ if (process.argv[1] === __filename) {
134
+ initProject();
135
+ }