johankit 0.1.3 → 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 (62) hide show
  1. package/README.md +45 -62
  2. package/dist/cli/commands/sync.js +38 -59
  3. package/dist/core/validation.js +7 -20
  4. package/dist/core/write.js +10 -9
  5. package/dist/src/cli/commands/copy.js +11 -0
  6. package/dist/src/cli/commands/paste.js +128 -0
  7. package/dist/src/cli/commands/prompt.js +54 -0
  8. package/dist/src/cli/commands/sync.js +166 -0
  9. package/dist/src/core/clipboard.js +64 -0
  10. package/dist/src/core/config.js +41 -0
  11. package/dist/{core → src/core}/diff.js +10 -14
  12. package/dist/{core → src/core}/git.js +1 -2
  13. package/dist/src/core/scan.js +75 -0
  14. package/dist/src/core/schema.js +18 -0
  15. package/dist/src/core/validation.js +11 -0
  16. package/dist/src/core/write.js +24 -0
  17. package/dist/src/index.js +39 -0
  18. package/dist/src/tests/cleanCodeBlock.test.js +23 -0
  19. package/dist/src/tests/scan.test.js +35 -0
  20. package/dist/src/tests/schema.test.js +22 -0
  21. package/dist/src/utils/cleanCodeBlock.js +13 -0
  22. package/dist/types.js +1 -0
  23. package/johankit.yml +6 -0
  24. package/package.json +20 -10
  25. package/src/cli/commands/copy.ts +6 -19
  26. package/src/cli/commands/paste.ts +79 -31
  27. package/src/cli/commands/prompt.ts +24 -64
  28. package/src/cli/commands/sync.ts +112 -71
  29. package/src/core/clipboard.ts +46 -80
  30. package/src/core/config.ts +22 -32
  31. package/src/core/diff.ts +10 -21
  32. package/src/core/scan.ts +52 -43
  33. package/src/core/schema.ts +17 -34
  34. package/src/core/validation.ts +8 -27
  35. package/src/core/write.ts +11 -17
  36. package/src/index.ts +43 -77
  37. package/src/tests/cleanCodeBlock.test.ts +21 -0
  38. package/src/tests/scan.test.ts +33 -0
  39. package/src/tests/schema.test.ts +24 -0
  40. package/src/types.ts +4 -50
  41. package/src/utils/cleanCodeBlock.ts +12 -12
  42. package/tsconfig.json +14 -5
  43. package/Readme.md +0 -56
  44. package/dist/cli/commands/copy.js +0 -29
  45. package/dist/cli/commands/paste.js +0 -49
  46. package/dist/cli/commands/prompt.js +0 -89
  47. package/dist/cli/commands/three.js +0 -106
  48. package/dist/cli/commands/tree.js +0 -107
  49. package/dist/core/clipboard.js +0 -89
  50. package/dist/core/config.js +0 -52
  51. package/dist/core/scan.js +0 -67
  52. package/dist/core/schema.js +0 -41
  53. package/dist/index.js +0 -72
  54. package/dist/services/JohankitService.js +0 -59
  55. package/dist/utils/cleanCodeBlock.js +0 -12
  56. package/dist/utils/createAsciiTree.js +0 -46
  57. package/johankit.yaml +0 -2
  58. package/src/cli/commands/tree.ts +0 -119
  59. package/src/services/JohankitService.ts +0 -70
  60. package/src/utils/createAsciiTree.ts +0 -53
  61. package/types.ts +0 -11
  62. /package/{types.js → dist/src/types.js} +0 -0
package/README.md CHANGED
@@ -1,89 +1,81 @@
1
1
  # JohanKit
2
2
 
3
- **JohanKit** is a developer-friendly CLI designed to supercharge your **vibe-coding** flow. It helps you capture, restore, and manipulate snapshots of your codebase, making it effortless to experiment, refactor, and collaboratewithout locking you into a specific framework or workflow.
3
+ **JohanKit** is a developer-friendly CLI for capturing, restoring, and applying codebase snapshotsperfect for vibe-coding with AI.
4
4
 
5
- Think of it as your personal **code snapshot toolkit**: lightweight, fast, and agnostic.
6
-
7
- ---
8
-
9
- ## Why JohanKit?
10
-
11
- JohanKit is a “kit for developers,” crafted to streamline coding sessions, prototype features quickly, and integrate seamlessly with AI-assisted refactoring or review tools.
12
-
13
- ---
5
+ Lightweight, fast, platform-agnostic.
14
6
 
15
7
  ## Features
16
8
 
17
9
  ### copy
18
-
19
- Take a snapshot of files in a directory and copy it to your clipboard as JSON.
10
+ Capture directory snapshot as JSON array to clipboard.
20
11
 
21
12
  ```bash
22
- johankit copy <dir> [exts]
23
- ````
24
-
25
- * `dir`: Directory to scan (default: current)
26
- * `exts`: Comma-separated list of extensions (e.g., `ts,js`)
13
+ johankit copy [dir] [exts]
14
+ ```
27
15
 
28
- ---
16
+ **Example:**
17
+ ```bash
18
+ johankit copy src ts,tsx,js,jsx
19
+ ```
29
20
 
30
21
  ### paste
31
-
32
- Restore files from a JSON snapshot stored in your clipboard.
22
+ Apply patches from JSON clipboard content.
33
23
 
34
24
  ```bash
35
- johankit paste <dir>
25
+ johankit paste [dir] [--run] [--diff] [--dry-run] [-y]
36
26
  ```
37
27
 
38
- * `dir`: Directory where files will be created (default: current)
39
-
40
- ---
28
+ **Examples:**
29
+ ```bash
30
+ johankit paste src --dry-run # Preview only
31
+ johankit paste src --diff # Interactive diff review
32
+ johankit paste src --run -y # Auto-apply + run commands
33
+ ```
41
34
 
42
35
  ### prompt
43
-
44
- Generate a ready-to-use AI prompt including a snapshot of your codebase.
36
+ Generate AI-ready prompt with snapshot + your request.
45
37
 
46
38
  ```bash
47
- johankit prompt <dir> "<user request>"
39
+ johankit prompt [dir] "<request>"
48
40
  ```
49
41
 
50
- * `dir`: Directory to scan (default: current)
51
- * `<user request>`: Instruction for AI to apply on the snapshot
52
-
53
- ---
42
+ **Example:**
43
+ ```bash
44
+ johankit prompt src "Add Zod validation to all API routes"
45
+ ```
54
46
 
55
47
  ### sync
56
-
57
- Apply JSON patches to your codebase and update the clipboard with the new snapshot.
48
+ Iterative AI workflow: snapshot → paste patch → new snapshot.
58
49
 
59
50
  ```bash
60
- johankit sync <dir>
51
+ johankit sync [dir] [--run] [--diff] [--dry-run] [--watch] [--auto]
61
52
  ```
62
53
 
63
- * `dir`: Target directory (default: current)
64
-
65
- ---
54
+ **Examples:**
55
+ ```bash
56
+ johankit sync src --auto # Watch clipboard + auto-apply
57
+ johankit sync src --diff --run # Interactive + execute commands
58
+ ```
66
59
 
67
- ## Example Workflow
60
+ ## Quick Workflow Example
68
61
 
69
62
  ```bash
70
- # Copy snapshot of your current src
71
- johankit copy src
63
+ # 1. Generate prompt
64
+ johankit prompt src "Refactor auth middleware to use JWT"
72
65
 
73
- # Ask AI to refactor your codebase
74
- johankit prompt src "Convert all callbacks to async/await"
66
+ # 2. Paste into LLM copy JSON patch back to clipboard
75
67
 
76
- # Apply AI-suggested changes and update snapshot
77
- johankit sync src
68
+ # 3. Apply with review
69
+ johankit paste src --diff
78
70
  ```
79
71
 
80
- With just a few commands, JohanKit lets you **capture ideas, experiment safely, and sync changes instantly**.
81
-
82
- ---
83
-
84
- ## Configuration
72
+ Or fast loop:
73
+ ```bash
74
+ johankit sync src --auto
75
+ ```
85
76
 
86
- You can configure defaults with a `johankit.yaml` file in the project root:
77
+ ## Config
78
+ Create `johankit.yml` in project root:
87
79
 
88
80
  ```yaml
89
81
  ignore:
@@ -92,19 +84,10 @@ ignore:
92
84
  - .git
93
85
  ```
94
86
 
95
- This ensures you never accidentally snapshot unnecessary files.
96
-
97
- ---
98
-
99
- ## Requirements
87
+ Also respects `.gitignore`.
100
88
 
101
- * Node.js >= 14
102
- * Clipboard-compatible OS (`xclip`, `pbcopy`, or `clip` on Windows)
103
-
104
- ---
105
-
106
- ## Installation
89
+ ## Install
107
90
 
108
91
  ```bash
109
92
  npm install -g johankit
110
- ```
93
+ ```
@@ -1,84 +1,63 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sync = void 0;
6
+ exports.sync = sync;
4
7
  // src/cli/commands/sync.ts
5
8
  const scan_1 = require("../../core/scan");
9
+ const schema_1 = require("../../core/schema");
6
10
  const diff_1 = require("../../core/diff");
7
11
  const clipboard_1 = require("../../core/clipboard");
8
- const validation_1 = require("../../core/validation");
9
- const write_1 = require("../../core/write");
10
- async function sync(dir, diff = false) {
12
+ const child_process_1 = require("child_process");
13
+ const cleanCodeBlock_1 = __importDefault(require("../../utils/cleanCodeBlock"));
14
+ const readline_1 = __importDefault(require("readline"));
15
+ async function confirm(msg) {
16
+ const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
17
+ return new Promise(resolve => {
18
+ rl.question(`${msg} (y/N): `, (ans) => {
19
+ rl.close();
20
+ resolve(ans.toLowerCase() === 'y');
21
+ });
22
+ });
23
+ }
24
+ async function sync(dir, runAll = false) {
25
+ const autoAccept = process.argv.includes("-y");
11
26
  try {
12
27
  const snapshotBefore = (0, scan_1.scanDir)(dir);
13
28
  const template = `
14
- You are an AI software engineer.
15
-
16
- You will receive a JSON array representing a snapshot of a codebase.
17
- Each item has the following structure:
18
-
19
- \`\`\`json
20
- {
21
- "path": "relative/path/to/file.ext",
22
- "content": "full file content"
23
- }
24
- \`\`\`
25
-
26
- ---
27
-
28
- SNAPSHOT
29
+ // ... (template mantido) ...
29
30
  ${JSON.stringify(snapshotBefore, null, 2)}
30
-
31
- ---
32
-
33
- YOUR TASK
34
- Propose changes according to the user request.
35
-
36
- Return ONLY a JSON array of ${diff ? 'DiffPatch' : 'FileSnapshot'}.
37
-
38
- PATCH FORMAT (STRICT)
39
- {
40
- \"path\": \"relative/path/to/file.ext\",
41
- \"content\": \"FULL updated file content (omit for delete)\"
42
- }
43
-
44
- IMPORTANT RULES
45
- - Do NOT return explanations
46
- - Do NOT return markdown
47
- - Return ONLY valid JSON
48
-
49
- USER REQUEST
50
- <Replace this with the user request>
51
31
  `;
52
32
  await (0, clipboard_1.copyToClipboard)(template.trim());
53
- process.stdout.write("✔ Prompt with snapshot copied to clipboard\n");
33
+ process.stdout.write("✔ Prompt with snapshot copied to clipboard. Paste the response here and press Enter (Ctrl+D to finish):\n");
54
34
  const input = await readStdin();
55
- let patches;
56
- try {
57
- patches = JSON.parse(input);
58
- }
59
- catch {
60
- throw new Error("Invalid JSON input");
61
- }
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);
35
+ const { cleaned } = (0, cleanCodeBlock_1.default)(input);
36
+ const patches = (0, schema_1.validatePatches)(JSON.parse(cleaned));
37
+ for (const patch of patches) {
38
+ if (patch.type === 'console' && patch.command) {
39
+ if (runAll) {
40
+ const shouldRun = autoAccept || await confirm(`> Execute: ${patch.command}`);
41
+ if (shouldRun)
42
+ (0, child_process_1.execSync)(patch.command, { stdio: 'inherit', cwd: dir });
43
+ }
44
+ else {
45
+ console.log(`> Skipped command: ${patch.command} (use --run)`);
46
+ }
47
+ }
48
+ else if (patch.path) {
49
+ (0, diff_1.applyDiff)(dir, [patch]);
50
+ }
68
51
  }
69
52
  const snapshotAfter = (0, scan_1.scanDir)(dir);
70
53
  await (0, clipboard_1.copyToClipboard)(JSON.stringify(snapshotAfter, null, 2));
71
54
  process.stdout.write("✔ Sync applied and new snapshot copied to clipboard\n");
72
55
  }
73
56
  catch (error) {
74
- process.stderr.write("✖ Sync failed\n");
75
- if (error instanceof Error) {
76
- process.stderr.write(`${error.message}\n`);
77
- }
57
+ process.stderr.write(`✖ Sync failed: ${error.message}\n`);
78
58
  process.exit(1);
79
59
  }
80
60
  }
81
- exports.sync = sync;
82
61
  function readStdin() {
83
62
  return new Promise(resolve => {
84
63
  let data = "";
@@ -1,24 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validatePatches = void 0;
3
+ exports.validatePatches = validatePatches;
4
+ // src/core/validation.ts
5
+ const schema_1 = require("./schema");
6
+ /**
7
+ * @deprecated Use validatePatches from core/schema instead.
8
+ */
4
9
  function validatePatches(json) {
5
- if (!Array.isArray(json)) {
6
- throw new Error("Validation Error: Input is not a JSON array.");
7
- }
8
- return json.map((item, index) => {
9
- if (typeof item !== "object" || item === null) {
10
- throw new Error(`Validation Error: Item at index ${index} is not an object.`);
11
- }
12
- if (!["modify", "create", "delete"].includes(item.type)) {
13
- throw new Error(`Validation Error: Invalid type '${item.type}' at index ${index}.`);
14
- }
15
- if (typeof item.path !== "string" || !item.path.trim()) {
16
- throw new Error(`Validation Error: Invalid or missing path at index ${index}.`);
17
- }
18
- if (item.type !== "delete" && typeof item.content !== "string") {
19
- throw new Error(`Validation Error: Missing content for '${item.type}' at index ${index}.`);
20
- }
21
- return item;
22
- });
10
+ return (0, schema_1.validatePatches)(json);
23
11
  }
24
- exports.validatePatches = validatePatches;
@@ -3,22 +3,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.writeFiles = void 0;
6
+ exports.writeFiles = writeFiles;
7
7
  // src/core/write.ts
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const git_1 = require("./git");
11
+ /**
12
+ * @deprecated Use applyDiff from core/diff for more flexibility (supports deletes and console commands).
13
+ */
11
14
  function writeFiles(basePath, files, commit = true) {
12
- if (commit) {
13
- (0, git_1.ensureGitCommit)("johankit: before paste");
15
+ if (commit && files.length > 0) {
16
+ (0, git_1.ensureGitCommit)("johankit: before write");
14
17
  }
15
18
  for (const file of files) {
19
+ if (!file.path)
20
+ continue;
16
21
  const fullPath = path_1.default.join(basePath, file.path);
17
- const dir = path_1.default.dirname(fullPath);
18
- if (!fs_1.default.existsSync(dir)) {
19
- fs_1.default.mkdirSync(dir, { recursive: true });
20
- }
21
- fs_1.default.writeFileSync(fullPath, file.content, "utf8");
22
+ fs_1.default.mkdirSync(path_1.default.dirname(fullPath), { recursive: true });
23
+ fs_1.default.writeFileSync(fullPath, file.content || "", "utf8");
22
24
  }
23
25
  }
24
- exports.writeFiles = writeFiles;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.copy = copy;
4
+ const scan_1 = require("../../core/scan");
5
+ const clipboard_1 = require("../../core/clipboard");
6
+ async function copy(dir, extensions) {
7
+ const snapshot = (0, scan_1.scanDir)(dir, { extensions });
8
+ const clipboardJSON = JSON.stringify(snapshot);
9
+ await (0, clipboard_1.copyToClipboard)(clipboardJSON);
10
+ process.stdout.write(`✔ Snapshot de ${dir} copiado (${(clipboardJSON.length / 1024).toFixed(2)} KB)\n`);
11
+ }
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.paste = paste;
40
+ // src/cli/commands/paste.ts
41
+ const diff_1 = require("../../core/diff");
42
+ const clipboard_1 = require("../../core/clipboard");
43
+ const schema_1 = require("../../core/schema");
44
+ const cleanCodeBlock_1 = __importDefault(require("../../utils/cleanCodeBlock"));
45
+ const child_process_1 = require("child_process");
46
+ const readline_1 = __importDefault(require("readline"));
47
+ const fs_1 = __importDefault(require("fs"));
48
+ const path_1 = __importDefault(require("path"));
49
+ const diff = __importStar(require("diff"));
50
+ require("colors");
51
+ async function confirm(msg) {
52
+ const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
53
+ return new Promise(resolve => {
54
+ rl.question(`${msg} (y/N): `, (ans) => {
55
+ rl.close();
56
+ resolve(ans.toLowerCase() === 'y');
57
+ });
58
+ });
59
+ }
60
+ function showDiff(filename, oldContent, newContent) {
61
+ console.log(`\n--- DIFF FOR: ${filename.bold} ---`);
62
+ // Otimização: Usar diffLines apenas se o conteúdo for diferente para evitar processamento inútil
63
+ if (oldContent === newContent) {
64
+ console.log("No changes detected.".gray);
65
+ return;
66
+ }
67
+ const patches = diff.diffLines(oldContent, newContent);
68
+ patches.forEach((part) => {
69
+ const color = part.added ? 'green' : part.removed ? 'red' : 'gray';
70
+ const prefix = part.added ? '+' : part.removed ? '-' : ' ';
71
+ const value = part.value.endsWith('\n') ? part.value : part.value + '\n';
72
+ process.stdout.write((value.split('\n').map(line => line ? `${prefix}${line}` : '').join('\n'))[color]);
73
+ });
74
+ console.log('\n-----------------------');
75
+ }
76
+ async function paste(dir, runAll = false, dryRun = false, interactiveDiff = false) {
77
+ const autoAccept = process.argv.includes("-y");
78
+ try {
79
+ const content = await (0, clipboard_1.readClipboard)();
80
+ if (!content)
81
+ throw new Error("Clipboard empty");
82
+ const { cleaned } = (0, cleanCodeBlock_1.default)(content);
83
+ const items = (0, schema_1.validatePatches)(JSON.parse(cleaned));
84
+ if (dryRun)
85
+ process.stdout.write("--- DRY RUN MODE ---\n");
86
+ // Otimização: Agrupar patches de arquivos para aplicar de uma vez se não for interativo
87
+ const filePatches = [];
88
+ for (const item of items) {
89
+ if (item.type === 'console' && item.command) {
90
+ if (dryRun) {
91
+ process.stdout.write(`[DRY-RUN] Would execute: ${item.command}\n`);
92
+ }
93
+ else if (runAll) {
94
+ if (autoAccept || await confirm(`> Execute: ${item.command}`)) {
95
+ (0, child_process_1.execSync)(item.command, { stdio: 'inherit', cwd: dir });
96
+ }
97
+ }
98
+ }
99
+ else if (item.path) {
100
+ if (interactiveDiff && item.content !== null) {
101
+ const fullPath = path_1.default.join(dir, item.path);
102
+ const oldContent = fs_1.default.existsSync(fullPath) ? fs_1.default.readFileSync(fullPath, 'utf8') : "";
103
+ showDiff(item.path, oldContent, item.content || "");
104
+ if (await confirm(`Apply changes to ${item.path}?`)) {
105
+ (0, diff_1.applyDiff)(dir, [item]);
106
+ }
107
+ else {
108
+ console.log(`Skipped: ${item.path}`);
109
+ }
110
+ }
111
+ else if (dryRun) {
112
+ process.stdout.write(`[DRY-RUN] Would ${item.content === null ? 'Delete' : 'Write'}: ${item.path}\n`);
113
+ }
114
+ else {
115
+ filePatches.push(item);
116
+ }
117
+ }
118
+ }
119
+ if (filePatches.length > 0) {
120
+ (0, diff_1.applyDiff)(dir, filePatches);
121
+ }
122
+ process.stdout.write("✔ Operation completed\n");
123
+ }
124
+ catch (error) {
125
+ process.stderr.write(`✘ Failed: ${error.message}\n`);
126
+ process.exit(1);
127
+ }
128
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prompt = prompt;
4
+ // src/cli/commands/prompt.ts
5
+ const scan_1 = require("../../core/scan");
6
+ const clipboard_1 = require("../../core/clipboard");
7
+ async function prompt(dir, userPrompt) {
8
+ const snapshot = (0, scan_1.scanDir)(dir);
9
+ const template = `
10
+ You are an AI software engineer.
11
+ Your goal is to help the user with their codebase using a specific JSON patch format.
12
+
13
+ Maintain 100% of previous functionality for full compatibility.
14
+ Always submit the complete code; never use comments.
15
+ Use "git add {files} && git commit -m {message}" after each change via the console.
16
+ ---
17
+
18
+ ### CAPABILITIES
19
+ 1. **File Updates**: You can create, update, or delete files.
20
+ 2. **Console Commands**: You can execute shell commands (e.g., npm install, mkdir, rm, vitest).
21
+
22
+ ### RESPONSE FORMAT
23
+ Return ONLY a JSON array. No conversational text. No explanations.
24
+ Wrap the JSON in a markdown code block: \`\`\`json [your_array] \`\`\`
25
+
26
+ ### PATCH TYPES
27
+ - **File Patch**: { "path": "src/file.ts", "content": "full code" }
28
+ - **Delete File**: { "path": "src/old-file.ts", "content": null }
29
+ - **Console**: { "type": "console", "command": "npm install lodash" }
30
+
31
+ ### STRATEGY
32
+ If the user request requires a new library, include the "npm install" command in the array before the file updates.
33
+ If the user wants to refactor and ensure it works, you can include a command to run tests.
34
+
35
+ ---
36
+
37
+ SNAPSHOT
38
+ ${JSON.stringify(snapshot, null, 2)}
39
+
40
+ ---
41
+
42
+ USER REQUEST
43
+ ${userPrompt}
44
+ `;
45
+ try {
46
+ await (0, clipboard_1.copyToClipboard)(template.trim());
47
+ process.stdout.write(template.trim());
48
+ process.stdout.write("\n\n✔ Prompt + Snapshot copied to clipboard\n");
49
+ }
50
+ catch (e) {
51
+ process.stdout.write(template.trim());
52
+ process.stderr.write("\n✖ Failed to copy to clipboard (output only)\n");
53
+ }
54
+ }