@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.
- package/LICENSE +22 -0
- package/README.md +372 -0
- package/bin/catalog.js +153 -0
- package/bin/cli.js +380 -0
- package/bin/installer.js +135 -0
- package/bin/prompts.js +371 -0
- package/checklists/phase-1-analysis.md +58 -0
- package/checklists/phase-2-planning.md +67 -0
- package/checklists/phase-3-restructuring.md +77 -0
- package/checklists/phase-4-documentation.md +111 -0
- package/checklists/phase-5-validation.md +91 -0
- package/examples/before-after/README.md +139 -0
- package/examples/ideal-monorepo/README.md +127 -0
- package/knowledge/agent-context-system/artifacts/guide.md +183 -0
- package/knowledge/agent-context-system/artifacts/knowledge.md +177 -0
- package/knowledge/agent-context-system/artifacts/skills.md +101 -0
- package/knowledge/agent-context-system/artifacts/workflows.md +143 -0
- package/knowledge/agent-context-system/metadata.json +26 -0
- package/knowledge/agent-context-system/timestamps.json +5 -0
- package/knowledge/agent-vulnerabilities/LICENSE +21 -0
- package/knowledge/agent-vulnerabilities/artifacts/stealth_injection.md +110 -0
- package/knowledge/agent-vulnerabilities/artifacts/vulnerabilities.md +232 -0
- package/knowledge/agent-vulnerabilities/metadata.json +14 -0
- package/knowledge/agent-vulnerabilities/timestamps.json +5 -0
- package/knowledge/power-words-dictionary/LICENSE +21 -0
- package/knowledge/power-words-dictionary/artifacts/dictionary.md +231 -0
- package/knowledge/power-words-dictionary/artifacts/prompt_amplifier.py +381 -0
- package/knowledge/power-words-dictionary/metadata.json +14 -0
- package/knowledge/power-words-dictionary/timestamps.json +5 -0
- package/package.json +77 -0
- package/roles/README.md +81 -0
- package/roles/architect.md +203 -0
- package/roles/inspector.md +232 -0
- package/roles/librarian.md +176 -0
- package/roles/scout.md +169 -0
- package/roles/surgeon.md +172 -0
- package/roles/tuner.md +204 -0
- package/skills/annotate-jsdoc/SKILL.md +262 -0
- package/skills/prompt-engineering/LICENSE +21 -0
- package/skills/prompt-engineering/SKILL.md +235 -0
- package/skills/prompt-engineering/scripts/extract_instructions.py +416 -0
- package/skills/prompt-engineering/scripts/prompt_amplifier.py +381 -0
- package/skills/prompt-engineering/scripts/prompt_diff_tracker.py +281 -0
- package/skills/prompt-engineering/scripts/prompt_dna_analyzer.py +692 -0
- package/skills/prompt-engineering/scripts/templates/code_review.md +47 -0
- package/skills/prompt-engineering/scripts/templates/dump_extraction.md +59 -0
- package/skills/prompt-engineering/scripts/templates/multi_agent_orchestration.md +100 -0
- package/skills/prompt-engineering/scripts/templates/prompt_audit.md +106 -0
- package/skills/prompt-engineering/scripts/templates/stealth_injection.md +110 -0
- package/skills/prompt-engineering/scripts/templates/task_automation.md +87 -0
- package/skills/prompt-engineering/workflows/amplify.md +36 -0
- package/skills/prompt-engineering/workflows/audit.md +55 -0
- package/skills/prompt-engineering/workflows/context-dump.md +90 -0
- package/skills/prompt-engineering/workflows/diff.md +44 -0
- package/strategy/bash-guide.md +134 -0
- package/strategy/context-exclusion.md +220 -0
- package/strategy/context-weight-theory.md +49 -0
- package/strategy/file-navigation-header.md +562 -0
- package/strategy/jsdoc-guide.md +596 -0
- package/strategy/monorepo_strategy.md +726 -0
- package/strategy/package-json-guide.md +541 -0
- package/templates/AGENTS.md.template +148 -0
- package/templates/antigravityignore.template +64 -0
- package/templates/cursorrules.template +7 -0
- package/templates/knowledge-item.template +44 -0
- package/templates/package-json-ideal.template +26 -0
- package/templates/package-readme.template +45 -0
- package/templates/publish-meta.template +34 -0
- package/templates/skill.template +50 -0
- package/templates/workflow.template +33 -0
- package/tools/analyze-package-json.sh +213 -0
- package/tools/analyze-structure.sh +101 -0
- package/tools/audit-jsdoc.sh +176 -0
- package/tools/check-fnh-freshness.sh +74 -0
- package/tools/detect-circular-deps.sh +147 -0
- package/tools/detect-god-files.sh +71 -0
- package/tools/enforce-god-files.sh +112 -0
- package/tools/enrich-package-json.js +311 -0
- package/tools/generate-jsdoc-headers.sh +109 -0
- package/tools/generate-source-map.sh +71 -0
- package/tools/lint-imports.sh +123 -0
- package/tools/measure-context-cost.sh +206 -0
- package/tools/scan-fnh.sh +174 -0
- package/tools/shared/config.sh +53 -0
- package/tools/validate-context-hygiene.sh +52 -0
- package/tools/validate-context-weight.sh +100 -0
- 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
|
+
}
|
package/bin/installer.js
ADDED
|
@@ -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
|
+
}
|