johankit 0.0.3 → 0.0.4

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.
@@ -8,29 +8,34 @@ exports.paste = void 0;
8
8
  const write_1 = require("../../core/write");
9
9
  const clipboard_1 = require("../../core/clipboard");
10
10
  const cleanCodeBlock_1 = __importDefault(require("../../utils/cleanCodeBlock"));
11
+ const diff_1 = require("../../core/diff");
12
+ const validation_1 = require("../../core/validation");
11
13
  async function paste(dir) {
12
14
  try {
13
15
  const content = await (0, clipboard_1.readClipboard)();
14
16
  if (!content) {
15
17
  throw new Error("Clipboard empty or inaccessible");
16
18
  }
17
- let files;
19
+ let parsed;
18
20
  try {
19
- const { lang, cleaned } = (0, cleanCodeBlock_1.default)(content);
20
- files = JSON.parse(cleaned);
21
+ const { cleaned } = (0, cleanCodeBlock_1.default)(content);
22
+ parsed = JSON.parse(cleaned);
21
23
  }
22
24
  catch (e) {
23
25
  throw new Error("Clipboard content is not valid JSON");
24
26
  }
25
- if (!Array.isArray(files)) {
26
- throw new Error("Clipboard content is not a JSON array");
27
+ if (Array.isArray(parsed) && parsed.every(f => f.path && f.content)) {
28
+ // Caso seja snapshot
29
+ (0, write_1.writeFiles)(dir, parsed, true);
27
30
  }
28
- // Validação simples do snapshot
29
- const isValidSnapshot = files.every(f => typeof f.path === 'string' && typeof f.content === 'string');
30
- if (!isValidSnapshot) {
31
- throw new Error("JSON does not match FileSnapshot structure {path, content}");
31
+ else if (Array.isArray(parsed) && parsed.every(f => f.type && f.path)) {
32
+ // Caso seja DiffPatch
33
+ const validated = (0, validation_1.validatePatches)(parsed);
34
+ (0, diff_1.applyDiff)(dir, validated);
35
+ }
36
+ else {
37
+ throw new Error("JSON is neither FileSnapshot array nor DiffPatch array");
32
38
  }
33
- (0, write_1.writeFiles)(dir, files, true);
34
39
  process.stdout.write("✔ Pasted from clipboard\n");
35
40
  }
36
41
  catch (error) {
@@ -4,14 +4,43 @@ exports.prompt = void 0;
4
4
  // src/cli/commands/prompt.ts
5
5
  const scan_1 = require("../../core/scan");
6
6
  const clipboard_1 = require("../../core/clipboard");
7
- async function prompt(dir, userPrompt) {
7
+ async function prompt(dir, userPrompt, diff = false) {
8
8
  const snapshot = (0, scan_1.scanDir)(dir);
9
+ const llmExamples = [
10
+ {
11
+ type: 'FileSnapshot',
12
+ example: [
13
+ {
14
+ path: 'src/example.ts',
15
+ content: 'export const x = 42;'
16
+ }
17
+ ]
18
+ },
19
+ {
20
+ type: 'DiffPatch',
21
+ example: [
22
+ {
23
+ type: 'modify',
24
+ path: 'src/example.ts',
25
+ content: 'export const x = 43;'
26
+ },
27
+ {
28
+ type: 'create',
29
+ path: 'src/newFile.ts',
30
+ content: 'export const newFile = true;'
31
+ },
32
+ {
33
+ type: 'delete',
34
+ path: 'src/oldFile.ts'
35
+ }
36
+ ]
37
+ }
38
+ ];
9
39
  const template = `
10
40
  You are an AI software engineer.
11
41
 
12
42
  You will receive a JSON array representing a snapshot of a codebase.
13
43
  Each item has the following structure:
14
-
15
44
  {
16
45
  "path": "relative/path/to/file.ext",
17
46
  "content": "full file content"
@@ -27,19 +56,22 @@ ${JSON.stringify(snapshot, null, 2)}
27
56
  YOUR TASK
28
57
  Propose changes according to the user request.
29
58
 
30
- Return ONLY a JSON array of patches.
59
+ Return ONLY a JSON array of ${diff ? 'DiffPatch' : 'FileSnapshot'}.
31
60
 
32
61
  PATCH FORMAT (STRICT)
33
62
  {
34
- "path": "relative/path/to/file.ext",
35
- "content": "FULL updated file content (omit for delete)"
63
+ \"path\": \"relative/path/to/file.ext\",
64
+ \"content\": \"FULL updated file content (omit for delete)\"
36
65
  }
37
66
 
67
+ EXAMPLE RESPONSE FROM LLM:
68
+ ${JSON.stringify(diff ? llmExamples.find(e => e.type === 'DiffPatch')?.example : llmExamples.find(e => e.type === 'FileSnapshot')?.example, null, 2)}
69
+
38
70
  IMPORTANT RULES
39
71
  - Do NOT return explanations
40
72
  - Do NOT return markdown
41
73
  - Return ONLY valid JSON inside the \"\`\`\`\"
42
- - Always return within a Markdown Code Block (with \"\`\`\`json\" syntax highlighting)\")
74
+ - Always return within a Markdown Code Block.
43
75
 
44
76
  USER REQUEST
45
77
  ${userPrompt}
@@ -47,7 +79,7 @@ ${userPrompt}
47
79
  try {
48
80
  await (0, clipboard_1.copyToClipboard)(template.trim());
49
81
  process.stdout.write(template.trim());
50
- process.stdout.write("\n\n✔ Prompt + Snapshot copied to clipboard\n");
82
+ process.stdout.write("\n\n✔ Prompt + Snapshot + Example copied to clipboard\n");
51
83
  }
52
84
  catch (e) {
53
85
  process.stdout.write(template.trim());
@@ -6,10 +6,10 @@ const scan_1 = require("../../core/scan");
6
6
  const diff_1 = require("../../core/diff");
7
7
  const clipboard_1 = require("../../core/clipboard");
8
8
  const validation_1 = require("../../core/validation");
9
- async function sync(dir) {
9
+ const write_1 = require("../../core/write");
10
+ async function sync(dir, diff = false) {
10
11
  try {
11
12
  const snapshotBefore = (0, scan_1.scanDir)(dir);
12
- // Gera prompt genérico para AI com snapshot atual
13
13
  const template = `
14
14
  You are an AI software engineer.
15
15
 
@@ -33,7 +33,7 @@ ${JSON.stringify(snapshotBefore, null, 2)}
33
33
  YOUR TASK
34
34
  Propose changes according to the user request.
35
35
 
36
- Return ONLY a JSON array of patches.
36
+ Return ONLY a JSON array of ${diff ? 'DiffPatch' : 'FileSnapshot'}.
37
37
 
38
38
  PATCH FORMAT (STRICT)
39
39
  {
@@ -51,7 +51,6 @@ USER REQUEST
51
51
  `;
52
52
  await (0, clipboard_1.copyToClipboard)(template.trim());
53
53
  process.stdout.write("✔ Prompt with snapshot copied to clipboard\n");
54
- // Aguarda entrada do usuário (resposta da AI) via stdin
55
54
  const input = await readStdin();
56
55
  let patches;
57
56
  try {
@@ -60,8 +59,13 @@ USER REQUEST
60
59
  catch {
61
60
  throw new Error("Invalid JSON input");
62
61
  }
63
- const validated = (0, validation_1.validatePatches)(patches);
64
- (0, diff_1.applyDiff)(dir, validated);
62
+ if (diff) {
63
+ const validated = (0, validation_1.validatePatches)(patches);
64
+ (0, diff_1.applyDiff)(dir, validated);
65
+ }
66
+ else {
67
+ (0, write_1.writeFiles)(dir, patches, true);
68
+ }
65
69
  const snapshotAfter = (0, scan_1.scanDir)(dir);
66
70
  await (0, clipboard_1.copyToClipboard)(JSON.stringify(snapshotAfter, null, 2));
67
71
  process.stdout.write("✔ Sync applied and new snapshot copied to clipboard\n");
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.three = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const ts_morph_1 = require("ts-morph");
9
+ /**
10
+ * Heurística simples para classificar arquivos
11
+ */
12
+ function detectFileKind(filePath, exportsCount) {
13
+ if (filePath.includes("/cli/"))
14
+ return "cli";
15
+ if (filePath.endsWith("index.ts") || filePath.endsWith("main.ts"))
16
+ return "entry";
17
+ if (exportsCount === 0)
18
+ return "util";
19
+ if (exportsCount > 3)
20
+ return "domain";
21
+ return "unknown";
22
+ }
23
+ async function three(dir) {
24
+ const project = new ts_morph_1.Project({});
25
+ project.addSourceFilesAtPaths([
26
+ `${dir}/**/*.{ts,tsx,js,jsx}`,
27
+ `!${dir}/**/node_modules/**/*`,
28
+ `!${dir}/**/dist/**/*`,
29
+ ]);
30
+ const tree = [];
31
+ for (const file of project.getSourceFiles()) {
32
+ const absolutePath = file.getFilePath();
33
+ const filePath = path_1.default.relative(dir, absolutePath);
34
+ if (filePath.includes("node_modules") || filePath.includes("/dist/")) {
35
+ continue;
36
+ }
37
+ /* ---------------- IMPORTS ---------------- */
38
+ const imports = file
39
+ .getImportDeclarations()
40
+ .map(i => i.getModuleSpecifierValue());
41
+ /* ---------------- CLASSES ---------------- */
42
+ const classes = file.getClasses().map(cls => ({
43
+ name: cls.getName() || "<anonymous>",
44
+ methods: cls.getMethods().map(m => ({
45
+ name: m.getName(),
46
+ params: m.getParameters().map(p => p.getName()),
47
+ returnType: safeType(() => m.getReturnType().getText()),
48
+ scope: "class",
49
+ }))
50
+ }));
51
+ /* ---------------- FUNCTIONS ---------------- */
52
+ const functions = file.getFunctions().map(fn => ({
53
+ name: fn.getName() || "<anonymous>",
54
+ params: fn.getParameters().map(p => p.getName()),
55
+ returnType: safeType(() => fn.getReturnType().getText()),
56
+ scope: "global",
57
+ }));
58
+ /* ---------------- VARIABLES ---------------- */
59
+ const variables = file.getVariableDeclarations().map(v => ({
60
+ name: v.getName(),
61
+ type: safeType(() => v.getType().getText()),
62
+ scope: v.getParent() instanceof ts_morph_1.SourceFile ? "global" : "local",
63
+ }));
64
+ /* ---------------- EXPORTS ---------------- */
65
+ const exports = [];
66
+ let mainExport;
67
+ file.getExportedDeclarations().forEach((decls, name) => {
68
+ decls.forEach(d => {
69
+ let kind = "unknown";
70
+ if (d.getKind() === ts_morph_1.SyntaxKind.ClassDeclaration)
71
+ kind = "class";
72
+ if (d.getKind() === ts_morph_1.SyntaxKind.FunctionDeclaration)
73
+ kind = "function";
74
+ if (d.getKind() === ts_morph_1.SyntaxKind.VariableDeclaration)
75
+ kind = "variable";
76
+ if (d.getKind() === ts_morph_1.SyntaxKind.TypeAliasDeclaration)
77
+ kind = "type";
78
+ exports.push({ name, kind });
79
+ });
80
+ });
81
+ if (exports.length === 1) {
82
+ mainExport = exports[0].name;
83
+ }
84
+ const kind = detectFileKind(filePath, exports.length);
85
+ tree.push({
86
+ path: filePath,
87
+ kind,
88
+ imports,
89
+ classes,
90
+ functions,
91
+ variables,
92
+ exports,
93
+ mainExport,
94
+ });
95
+ }
96
+ return tree;
97
+ }
98
+ exports.three = three;
99
+ function safeType(fn) {
100
+ try {
101
+ return fn();
102
+ }
103
+ catch {
104
+ return "unknown";
105
+ }
106
+ }
package/dist/index.js CHANGED
@@ -1,54 +1,72 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const copy_1 = require("./cli/commands/copy");
5
- const paste_1 = require("./cli/commands/paste");
6
- const prompt_1 = require("./cli/commands/prompt");
7
- const sync_1 = require("./cli/commands/sync");
4
+ exports.JohankitService = void 0;
5
+ const JohankitService_1 = require("./services/JohankitService");
6
+ Object.defineProperty(exports, "JohankitService", { enumerable: true, get: function () { return JohankitService_1.JohankitService; } });
8
7
  const [, , command, ...args] = process.argv;
8
+ const service = new JohankitService_1.JohankitService();
9
9
  async function main() {
10
- switch (command) {
11
- case "copy": {
12
- const dir = args[0] ?? ".";
13
- const exts = args[1]?.split(",");
14
- await (0, copy_1.copy)(dir);
15
- break;
16
- }
17
- case "paste": {
18
- const dir = args[0] ?? ".";
19
- await (0, paste_1.paste)(dir);
20
- break;
21
- }
22
- case "prompt": {
23
- const dir = args[0] ?? ".";
24
- const userPrompt = args.slice(1).join(" ");
25
- if (!userPrompt) {
26
- console.error("Missing user prompt");
27
- process.exit(1);
10
+ try {
11
+ switch (command) {
12
+ case "copy": {
13
+ const dir = args[0] ?? ".";
14
+ await service.copy(dir);
15
+ break;
16
+ }
17
+ case "paste": {
18
+ const dir = args[0] ?? ".";
19
+ await service.paste(dir);
20
+ break;
21
+ }
22
+ case "prompt": {
23
+ const dir = args[0] ?? ".";
24
+ const diff = args.includes("--diff");
25
+ const userPrompt = args.filter(a => a !== "--diff").slice(1).join(" ");
26
+ await service.prompt(dir, userPrompt, diff);
27
+ break;
28
28
  }
29
- await (0, prompt_1.prompt)(dir, userPrompt);
30
- break;
29
+ case "sync": {
30
+ const dir = args[0] ?? ".";
31
+ await service.sync(dir);
32
+ break;
33
+ }
34
+ case "three": {
35
+ const dir = args[0] ?? ".";
36
+ const output = await service.three(dir);
37
+ console.log(output);
38
+ break;
39
+ }
40
+ default:
41
+ showHelp();
42
+ break;
43
+ }
44
+ }
45
+ catch (error) {
46
+ if (error instanceof Error) {
47
+ console.error("Error:", error.message);
31
48
  }
32
- case "sync": {
33
- const dir = args[0] ?? ".";
34
- await (0, sync_1.sync)(dir);
35
- break;
49
+ else {
50
+ console.error("Unexpected error:", error);
36
51
  }
37
- default:
38
- help();
52
+ process.exit(1);
39
53
  }
40
54
  }
41
- function help() {
55
+ function showHelp() {
42
56
  console.log(`
43
57
  Usage:
44
- johankit copy <dir> [exts]
58
+ johankit copy <dir>
45
59
  johankit paste <dir>
46
- johankit prompt <dir> "<user request>"
60
+ johankit prompt <dir> "<user request>" [--diff]
47
61
  johankit sync <dir>
62
+ johankit three <dir>
48
63
 
49
64
  Examples:
50
- johankit prompt src "refatorar para async/await"
65
+ johankit copy src
66
+ johankit paste src
67
+ johankit prompt src "refactor to async/await"
51
68
  johankit sync src
69
+ johankit three src
52
70
  `);
53
71
  }
54
72
  main();
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.JohankitService = void 0;
7
+ const createAsciiTree_1 = __importDefault(require("../utils/createAsciiTree"));
8
+ const copy_1 = require("../cli/commands/copy");
9
+ const paste_1 = require("../cli/commands/paste");
10
+ const prompt_1 = require("../cli/commands/prompt");
11
+ const three_1 = require("../cli/commands/three");
12
+ const scan_1 = require("../core/scan");
13
+ const diff_1 = require("../core/diff");
14
+ const validation_1 = require("../core/validation");
15
+ const write_1 = require("../core/write");
16
+ class JohankitService {
17
+ constructor(options = {}) {
18
+ this.isolated = options.isolated ?? false;
19
+ }
20
+ setInput(input) {
21
+ this.internalInput = input;
22
+ }
23
+ async copy(dir = ".") {
24
+ return (0, copy_1.copy)(dir);
25
+ }
26
+ async paste(dir = ".") {
27
+ return (0, paste_1.paste)(dir);
28
+ }
29
+ async prompt(dir, userPrompt, diff = false) {
30
+ if (!userPrompt) {
31
+ throw new Error("Missing user prompt");
32
+ }
33
+ return (0, prompt_1.prompt)(dir, userPrompt, diff);
34
+ }
35
+ async sync(dir = ".", diff = false) {
36
+ const snapshotBefore = (0, scan_1.scanDir)(dir);
37
+ const input = this.isolated ? this.internalInput : undefined;
38
+ if (!this.isolated && input === undefined) {
39
+ throw new Error("sync() without isolation must be used via CLI (stdin)");
40
+ }
41
+ if (!input) {
42
+ throw new Error("No input provided for isolated sync");
43
+ }
44
+ if (diff) {
45
+ const validated = (0, validation_1.validatePatches)(input);
46
+ (0, diff_1.applyDiff)(dir, validated);
47
+ }
48
+ else {
49
+ (0, write_1.writeFiles)(dir, input, true);
50
+ }
51
+ const snapshotAfter = (0, scan_1.scanDir)(dir);
52
+ return { before: snapshotBefore, after: snapshotAfter };
53
+ }
54
+ async three(dir = ".") {
55
+ const tree = await (0, three_1.three)(dir);
56
+ return (0, createAsciiTree_1.default)(tree);
57
+ }
58
+ }
59
+ exports.JohankitService = JohankitService;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function createAsciiTree(files) {
4
+ let out = "";
5
+ files.forEach((file, i) => {
6
+ const lastFile = i === files.length - 1;
7
+ const fPrefix = lastFile ? "└── " : "├── ";
8
+ const fIndent = lastFile ? " " : "│ ";
9
+ out += `${fPrefix}📄 ${file.path} (${file.kind})\n`;
10
+ /* -------- Imports -------- */
11
+ if (file.imports.length) {
12
+ out += `${fIndent}├── 📦 Imports [${file.imports.length}]\n`;
13
+ file.imports.forEach((imp, idx) => {
14
+ const last = idx === file.imports.length - 1;
15
+ out += `${fIndent}${last ? "└── " : "├── "}${imp}\n`;
16
+ });
17
+ }
18
+ /* -------- Exports -------- */
19
+ if (file.exports.length) {
20
+ out += `${fIndent}├── 🔑 Exports\n`;
21
+ file.exports.forEach((e, idx) => {
22
+ const last = idx === file.exports.length - 1;
23
+ const main = e.name === file.mainExport ? " ⭐" : "";
24
+ out += `${fIndent}${last ? "└── " : "├── "}${e.name} (${e.kind})${main}\n`;
25
+ });
26
+ }
27
+ /* -------- Variables -------- */
28
+ file.variables.forEach(v => {
29
+ out += `${fIndent}├── 💡 ${v.name}: ${v.type} (${v.scope})\n`;
30
+ });
31
+ /* -------- Functions -------- */
32
+ file.functions.forEach(fn => {
33
+ out += `${fIndent}├── ⚡ ${fn.name}(${fn.params.join(", ")}): ${fn.returnType}\n`;
34
+ });
35
+ /* -------- Classes -------- */
36
+ file.classes.forEach(cls => {
37
+ out += `${fIndent}├── ⚙️ Class ${cls.name}\n`;
38
+ cls.methods.forEach((m, mi) => {
39
+ const last = mi === cls.methods.length - 1;
40
+ out += `${fIndent}│ ${last ? "└── " : "├── "}➡️ ${m.name}(${m.params.join(", ")}): ${m.returnType}\n`;
41
+ });
42
+ });
43
+ });
44
+ return out;
45
+ }
46
+ exports.default = createAsciiTree;
package/johankit.yaml ADDED
@@ -0,0 +1,2 @@
1
+ ignore:
2
+ - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "johankit",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -27,6 +27,7 @@
27
27
  "clipboardy": "^5.0.2",
28
28
  "commander": "^14.0.2",
29
29
  "js-yaml": "^4.1.1",
30
+ "ts-morph": "^27.0.2",
30
31
  "vm2": "^3.10.0"
31
32
  }
32
33
  }
@@ -2,6 +2,8 @@
2
2
  import { writeFiles } from "../../core/write";
3
3
  import { readClipboard } from "../../core/clipboard";
4
4
  import cleanCodeBlock from "../../utils/cleanCodeBlock";
5
+ import { applyDiff } from "../../core/diff";
6
+ import { validatePatches } from "../../core/validation";
5
7
 
6
8
  export async function paste(dir: string) {
7
9
  try {
@@ -11,28 +13,25 @@ export async function paste(dir: string) {
11
13
  throw new Error("Clipboard empty or inaccessible");
12
14
  }
13
15
 
14
- let files;
16
+ let parsed;
15
17
  try {
16
- const { lang, cleaned } = cleanCodeBlock(content)
17
- files = JSON.parse(cleaned);
18
+ const { cleaned } = cleanCodeBlock(content);
19
+ parsed = JSON.parse(cleaned);
18
20
  } catch (e) {
19
21
  throw new Error("Clipboard content is not valid JSON");
20
22
  }
21
23
 
22
- if (!Array.isArray(files)) {
23
- throw new Error("Clipboard content is not a JSON array");
24
+ if (Array.isArray(parsed) && parsed.every(f => f.path && f.content)) {
25
+ // Caso seja snapshot
26
+ writeFiles(dir, parsed, true);
27
+ } else if (Array.isArray(parsed) && parsed.every(f => f.type && f.path)) {
28
+ // Caso seja DiffPatch
29
+ const validated = validatePatches(parsed);
30
+ applyDiff(dir, validated);
31
+ } else {
32
+ throw new Error("JSON is neither FileSnapshot array nor DiffPatch array");
24
33
  }
25
34
 
26
- // Validação simples do snapshot
27
- const isValidSnapshot = files.every(f =>
28
- typeof f.path === 'string' && typeof f.content === 'string'
29
- );
30
-
31
- if (!isValidSnapshot) {
32
- throw new Error("JSON does not match FileSnapshot structure {path, content}");
33
- }
34
-
35
- writeFiles(dir, files, true);
36
35
  process.stdout.write("✔ Pasted from clipboard\n");
37
36
  } catch (error) {
38
37
  process.stderr.write("✖ Paste failed\n");
@@ -41,4 +40,4 @@ export async function paste(dir: string) {
41
40
  }
42
41
  process.exit(1);
43
42
  }
44
- }
43
+ }
@@ -2,15 +2,50 @@
2
2
  import { scanDir } from "../../core/scan";
3
3
  import { copyToClipboard } from "../../core/clipboard";
4
4
 
5
- export async function prompt(dir: string, userPrompt: string) {
5
+ interface LLMResponseExample {
6
+ type: 'FileSnapshot' | 'DiffPatch';
7
+ example: any;
8
+ }
9
+
10
+ export async function prompt(dir: string, userPrompt: string, diff = false) {
6
11
  const snapshot = scanDir(dir);
7
12
 
13
+ const llmExamples: LLMResponseExample[] = [
14
+ {
15
+ type: 'FileSnapshot',
16
+ example: [
17
+ {
18
+ path: 'src/example.ts',
19
+ content: 'export const x = 42;'
20
+ }
21
+ ]
22
+ },
23
+ {
24
+ type: 'DiffPatch',
25
+ example: [
26
+ {
27
+ type: 'modify',
28
+ path: 'src/example.ts',
29
+ content: 'export const x = 43;'
30
+ },
31
+ {
32
+ type: 'create',
33
+ path: 'src/newFile.ts',
34
+ content: 'export const newFile = true;'
35
+ },
36
+ {
37
+ type: 'delete',
38
+ path: 'src/oldFile.ts'
39
+ }
40
+ ]
41
+ }
42
+ ];
43
+
8
44
  const template = `
9
45
  You are an AI software engineer.
10
46
 
11
47
  You will receive a JSON array representing a snapshot of a codebase.
12
48
  Each item has the following structure:
13
-
14
49
  {
15
50
  "path": "relative/path/to/file.ext",
16
51
  "content": "full file content"
@@ -26,19 +61,22 @@ ${JSON.stringify(snapshot, null, 2)}
26
61
  YOUR TASK
27
62
  Propose changes according to the user request.
28
63
 
29
- Return ONLY a JSON array of patches.
64
+ Return ONLY a JSON array of ${diff ? 'DiffPatch' : 'FileSnapshot'}.
30
65
 
31
66
  PATCH FORMAT (STRICT)
32
67
  {
33
- "path": "relative/path/to/file.ext",
34
- "content": "FULL updated file content (omit for delete)"
68
+ \"path\": \"relative/path/to/file.ext\",
69
+ \"content\": \"FULL updated file content (omit for delete)\"
35
70
  }
36
71
 
72
+ EXAMPLE RESPONSE FROM LLM:
73
+ ${JSON.stringify(diff ? llmExamples.find(e => e.type === 'DiffPatch')?.example : llmExamples.find(e => e.type === 'FileSnapshot')?.example, null, 2)}
74
+
37
75
  IMPORTANT RULES
38
76
  - Do NOT return explanations
39
77
  - Do NOT return markdown
40
78
  - Return ONLY valid JSON inside the \"\`\`\`\"
41
- - Always return within a Markdown Code Block (with \"\`\`\`json\" syntax highlighting)\")
79
+ - Always return within a Markdown Code Block.
42
80
 
43
81
  USER REQUEST
44
82
  ${userPrompt}
@@ -47,7 +85,7 @@ ${userPrompt}
47
85
  try {
48
86
  await copyToClipboard(template.trim());
49
87
  process.stdout.write(template.trim());
50
- process.stdout.write("\n\n✔ Prompt + Snapshot copied to clipboard\n");
88
+ process.stdout.write("\n\n✔ Prompt + Snapshot + Example copied to clipboard\n");
51
89
  } catch (e) {
52
90
  process.stdout.write(template.trim());
53
91
  process.stderr.write("\n✖ Failed to copy to clipboard (output only)\n");