johankit 0.1.5 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +26 -58
  2. package/dist/cli/commands/sync.js +37 -57
  3. package/dist/core/validation.js +6 -18
  4. package/dist/core/write.js +9 -7
  5. package/dist/src/cli/commands/copy.js +11 -0
  6. package/dist/src/cli/commands/paste.js +120 -0
  7. package/dist/src/cli/commands/prompt.js +54 -0
  8. package/dist/src/cli/commands/sync.js +173 -0
  9. package/dist/src/core/clipboard.js +64 -0
  10. package/dist/src/core/config.js +39 -0
  11. package/dist/{core → src/core}/diff.js +9 -12
  12. package/dist/src/core/scan.js +70 -0
  13. package/dist/src/core/schema.js +18 -0
  14. package/dist/src/core/validation.js +11 -0
  15. package/dist/src/core/write.js +24 -0
  16. package/dist/src/index.js +34 -0
  17. package/dist/src/tests/cleanCodeBlock.test.js +23 -0
  18. package/dist/src/tests/scan.test.js +35 -0
  19. package/dist/src/tests/schema.test.js +22 -0
  20. package/dist/src/utils/cleanCodeBlock.js +21 -0
  21. package/dist/types.js +1 -0
  22. package/johankit.yml +6 -0
  23. package/package.json +20 -10
  24. package/src/cli/commands/copy.ts +6 -19
  25. package/src/cli/commands/paste.ts +70 -31
  26. package/src/cli/commands/prompt.ts +24 -64
  27. package/src/cli/commands/sync.ts +121 -73
  28. package/src/core/clipboard.ts +46 -80
  29. package/src/core/config.ts +20 -32
  30. package/src/core/diff.ts +10 -21
  31. package/src/core/scan.ts +43 -40
  32. package/src/core/schema.ts +17 -34
  33. package/src/core/validation.ts +8 -27
  34. package/src/core/write.ts +11 -17
  35. package/src/index.ts +38 -77
  36. package/src/types.ts +4 -50
  37. package/src/utils/cleanCodeBlock.ts +17 -8
  38. package/tsconfig.json +14 -6
  39. package/Readme.md +0 -56
  40. package/dist/cli/commands/copy.js +0 -21
  41. package/dist/cli/commands/paste.js +0 -48
  42. package/dist/cli/commands/prompt.js +0 -88
  43. package/dist/cli/commands/three.js +0 -106
  44. package/dist/cli/commands/tree.js +0 -106
  45. package/dist/core/clipboard.js +0 -88
  46. package/dist/core/config.js +0 -51
  47. package/dist/core/scan.js +0 -66
  48. package/dist/core/schema.js +0 -40
  49. package/dist/index.js +0 -72
  50. package/dist/services/JohankitService.js +0 -59
  51. package/dist/utils/cleanCodeBlock.js +0 -12
  52. package/dist/utils/createAsciiTree.js +0 -46
  53. package/johankit.yaml +0 -2
  54. package/src/cli/commands/tree.ts +0 -119
  55. package/src/services/JohankitService.ts +0 -70
  56. package/src/utils/createAsciiTree.ts +0 -53
  57. package/types.ts +0 -11
  58. /package/dist/{core → src/core}/git.js +0 -0
  59. /package/{types.js → dist/src/types.js} +0 -0
package/src/core/scan.ts CHANGED
@@ -1,70 +1,73 @@
1
- // src/core/scan.ts
2
1
  import fs from "fs";
3
2
  import path from "path";
4
- import { FileSnapshot, ScanOptions } from "../types";
3
+ import { FileSnapshot, ScanOptions, Config } from "../types";
4
+ import { loadConfig } from "./config";
5
5
 
6
- export function scanDir(
7
- basePath: string,
8
- options: ScanOptions = {}
9
- ): FileSnapshot[] {
6
+ export function scanDir(basePath: string, options: ScanOptions = {}): FileSnapshot[] {
10
7
  const result: FileSnapshot[] = [];
11
8
  const base = path.resolve(basePath);
9
+ const exts = options.extensions?.map(e => e.startsWith('.') ? e : `.${e}`);
12
10
 
13
- const exts = options.extensions?.map(e =>
14
- e.startsWith(".") ? e : `.${e}`
15
- );
11
+ const config: Config = loadConfig(base);
12
+ const ignorePatterns = new Set(config.ignore);
16
13
 
17
- // Default ignores
18
- const ignoreSet = new Set([
19
- "node_modules", ".git", "dist", "build", ".DS_Store", "coverage", ".env", "yarn.lock",
20
- ]);
21
-
22
- // Read .gitignore if exists
23
- const gitignorePath = path.join(base, ".gitignore");
14
+ // .gitignore e adiciona ao Set
15
+ const gitignorePath = path.join(base, '.gitignore');
24
16
  if (fs.existsSync(gitignorePath)) {
25
17
  try {
26
- const lines = fs.readFileSync(gitignorePath, "utf8").split("\n");
27
- for (const line of lines) {
28
- const trimmed = line.trim();
29
- if (trimmed && !trimmed.startsWith("#")) {
30
- ignoreSet.add(trimmed.replace(/^\//, "").replace(/\/$/, ""));
31
- }
32
- }
33
- } catch (e) {
34
- // ignore read errors
35
- }
18
+ fs.readFileSync(gitignorePath, 'utf8')
19
+ .split('\n')
20
+ .forEach(line => {
21
+ const trimmed = line.trim();
22
+ if (trimmed && !trimmed.startsWith('#')) {
23
+ // Normaliza o caminho do gitignore para o padrão do scanner
24
+ ignorePatterns.add(trimmed.replace(/^\//, '').replace(/\/$/, ''));
25
+ }
26
+ });
27
+ } catch { }
36
28
  }
37
29
 
38
- function shouldIgnore(name: string): boolean {
39
- if (ignoreSet.has(name)) return true;
40
- for (const pattern of ignoreSet) {
41
- if (pattern.startsWith("*") && name.endsWith(pattern.slice(1))) return true;
42
- if (name.startsWith(pattern + "/")) return true;
43
- }
44
- return false;
45
- }
30
+ // PERFORMANCE: Converte o Set em Array UMA VEZ antes de iniciar o loop
31
+ const finalIgnoreList = Array.from(ignorePatterns);
46
32
 
47
33
  function loop(currentPath: string) {
48
34
  const entries = fs.readdirSync(currentPath, { withFileTypes: true });
49
35
 
50
36
  for (const entry of entries) {
51
- if (shouldIgnore(entry.name)) continue;
37
+ const isBinary = entry.name.match(/\.(png|jpg|jpeg|gif|pdf|zip|exe|dll|so|db)$/i);
38
+ if (isBinary) continue;
52
39
 
53
40
  const fullPath = path.join(currentPath, entry.name);
41
+ const relPath = path.relative(base, fullPath).replace(/\\/g, '/');
42
+
43
+ // PERFORMANCE: O some() agora opera sobre um array fixo, sem Array.from() interno
44
+ const shouldIgnore = finalIgnoreList.some(p =>
45
+ relPath === p || relPath.startsWith(p + '/')
46
+ );
47
+
48
+ if (shouldIgnore) continue;
54
49
 
55
50
  if (entry.isDirectory()) {
56
51
  loop(fullPath);
57
52
  } else {
58
53
  if (exts && !exts.includes(path.extname(entry.name))) continue;
59
54
 
60
- result.push({
61
- path: path.relative(base, fullPath).replace(/\\/g, "/"),
62
- content: fs.readFileSync(fullPath, "utf8"),
63
- });
55
+ try {
56
+ // PERFORMANCE: Pular arquivos muito grandes evita travar o clipboard
57
+ const stats = fs.statSync(fullPath);
58
+ if (stats.size > 1024 * 300) continue; // Reduzi para 300KB para ser mais seguro
59
+
60
+ result.push({
61
+ path: relPath,
62
+ content: fs.readFileSync(fullPath, 'utf8')
63
+ });
64
+ } catch (e) {
65
+ continue;
66
+ }
64
67
  }
65
68
  }
66
69
  }
67
70
 
68
71
  loop(base);
69
72
  return result;
70
- }
73
+ }
@@ -1,41 +1,24 @@
1
1
  // src/core/schema.ts
2
- import { DiffPatch } from "./diff";
3
2
 
4
- /**
5
- * Valida se um objeto se parece com um Patch de DiffPatch válido.
6
- * Não faz validação completa de esquema (JSON Schema), mas verifica a estrutura básica.
7
- */
8
- function isValidPatch(patch: any): boolean {
9
- if (typeof patch !== "object" || patch === null) return false;
10
- if (typeof patch.path !== "string" || patch.path.length === 0) return false;
11
-
12
- const validTypes = ["modify", "create", "delete"];
13
- if (!validTypes.includes(patch.type)) return false;
14
-
15
- if (patch.type === "delete") {
16
- return patch.content === undefined;
17
- } else {
18
- return typeof patch.content === "string";
19
- }
3
+ export interface PatchItem {
4
+ path?: string;
5
+ content?: string | null;
6
+ type?: 'console';
7
+ command?: string;
20
8
  }
21
9
 
22
- /**
23
- * Valida um array de patches de diff (DiffPatch[]).
24
- * @param patches O array a ser validado.
25
- * @returns O array de patches se for válido.
26
- * @throws Um erro se a validação falhar.
27
- */
28
- export function validatePatches(patches: any): DiffPatch[] {
29
- if (!Array.isArray(patches)) {
30
- throw new Error("O patch deve ser um array JSON válido");
10
+ export function validatePatches(json: any): PatchItem[] {
11
+ if (!Array.isArray(json)) {
12
+ throw new Error("Input must be a valid JSON array");
31
13
  }
14
+ // Validação permissiva: ou tem path (arquivo) ou tem type console + command
15
+ return json.map((item, index) => {
16
+ const isFile = typeof item.path === 'string';
17
+ const isCommand = item.type === 'console' && typeof item.command === 'string';
32
18
 
33
- for (const [index, patch] of patches.entries()) {
34
- if (!isValidPatch(patch)) {
35
- throw new Error(`Patch inválido no índice ${index}: ${JSON.stringify(patch, null, 2)}.\nEsperado: { type: 'modify'|'create'|'delete', path: string, content?: string }`);
19
+ if (!isFile && !isCommand) {
20
+ throw new Error(`Item at index ${index} is invalid. Must have 'path' or 'type: console'`);
36
21
  }
37
- }
38
-
39
- // Assume que o array validado está no formato correto de DiffPatch[]
40
- return patches as DiffPatch[];
41
- }
22
+ return item as PatchItem;
23
+ });
24
+ }
@@ -1,28 +1,9 @@
1
1
  // src/core/validation.ts
2
- import { DiffPatch } from "./diff";
3
-
4
- export function validatePatches(json: any): DiffPatch[] {
5
- if (!Array.isArray(json)) {
6
- throw new Error("Validation Error: Input is not a JSON array.");
7
- }
8
-
9
- return json.map((item, index) => {
10
- if (typeof item !== "object" || item === null) {
11
- throw new Error(`Validation Error: Item at index ${index} is not an object.`);
12
- }
13
-
14
- if (!["modify", "create", "delete"].includes(item.type)) {
15
- throw new Error(`Validation Error: Invalid type '${item.type}' at index ${index}.`);
16
- }
17
-
18
- if (typeof item.path !== "string" || !item.path.trim()) {
19
- throw new Error(`Validation Error: Invalid or missing path at index ${index}.`);
20
- }
21
-
22
- if (item.type !== "delete" && typeof item.content !== "string") {
23
- throw new Error(`Validation Error: Missing content for '${item.type}' at index ${index}.`);
24
- }
25
-
26
- return item as DiffPatch;
27
- });
28
- }
2
+ import { validatePatches as sharedValidate } from "./schema";
3
+
4
+ /**
5
+ * @deprecated Use validatePatches from core/schema instead.
6
+ */
7
+ export function validatePatches(json: any): any[] {
8
+ return sharedValidate(json);
9
+ }
package/src/core/write.ts CHANGED
@@ -1,26 +1,20 @@
1
1
  // src/core/write.ts
2
2
  import fs from "fs";
3
3
  import path from "path";
4
- import { FileSnapshot } from "../types";
5
4
  import { ensureGitCommit } from "./git";
6
5
 
7
- export function writeFiles(
8
- basePath: string,
9
- files: FileSnapshot[],
10
- commit = true
11
- ) {
12
- if (commit) {
13
- ensureGitCommit("johankit: before paste");
14
- }
6
+ /**
7
+ * @deprecated Use applyDiff from core/diff for more flexibility (supports deletes and console commands).
8
+ */
9
+ export function writeFiles(basePath: string, files: any, commit = true) {
10
+ // if (commit && files.length > 0) {
11
+ // ensureGitCommit("johankit: before write");
12
+ // }
15
13
 
16
14
  for (const file of files) {
15
+ if (!file.path) continue;
17
16
  const fullPath = path.join(basePath, file.path);
18
- const dir = path.dirname(fullPath);
19
-
20
- if (!fs.existsSync(dir)) {
21
- fs.mkdirSync(dir, { recursive: true });
22
- }
23
-
24
- fs.writeFileSync(fullPath, file.content, "utf8");
17
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
18
+ fs.writeFileSync(fullPath, file.content || "", "utf8");
25
19
  }
26
- }
20
+ }
package/src/index.ts CHANGED
@@ -1,78 +1,39 @@
1
1
  #!/usr/bin/env node
2
-
3
- import { JohankitService } from "./services/JohankitService";
4
-
5
- const [, , command, ...args] = process.argv;
6
- const service = new JohankitService();
7
-
8
- async function main() {
9
- try {
10
- switch (command) {
11
- case "copy": {
12
- const dir = args[0] ?? ".";
13
- await service.copy(dir);
14
- break;
15
- }
16
-
17
- case "paste": {
18
- const dir = args[0] ?? ".";
19
- await service.paste(dir);
20
- break;
21
- }
22
-
23
- case "prompt": {
24
- const dir = args[0] ?? ".";
25
- const diff = args.includes("--diff");
26
- const userPrompt = args.filter(a => a !== "--diff").slice(1).join(" ");
27
- await service.prompt(dir, userPrompt, diff);
28
- break;
29
- }
30
-
31
- case "sync": {
32
- const dir = args[0] ?? ".";
33
- await service.sync(dir);
34
- break;
35
- }
36
-
37
- case "tree": {
38
- const dir = args[0] ?? ".";
39
- const output = await service.tree(dir);
40
- console.log(output);
41
- break;
42
- }
43
-
44
- default:
45
- showHelp();
46
- break;
47
- }
48
- } catch (error) {
49
- if (error instanceof Error) {
50
- console.error("Error:", error.message);
51
- } else {
52
- console.error("Unexpected error:", error);
53
- }
54
- process.exit(1);
55
- }
56
- }
57
-
58
- function showHelp() {
59
- console.log(`
60
- Usage:
61
- johankit copy <dir>
62
- johankit paste <dir>
63
- johankit prompt <dir> "<user request>" [--diff]
64
- johankit sync <dir>
65
- johankit tree <dir>
66
-
67
- Examples:
68
- johankit copy src
69
- johankit paste src
70
- johankit prompt src "refactor to async/await"
71
- johankit sync src
72
- johankit tree src
73
- `);
74
- }
75
-
76
- main();
77
-
78
- export { JohankitService };
2
+ import { Command } from "commander";
3
+ import { copy } from "./cli/commands/copy";
4
+ import { paste } from "./cli/commands/paste";
5
+ import { prompt } from "./cli/commands/prompt";
6
+ import { sync } from "./cli/commands/sync";
7
+
8
+ const program = new Command();
9
+
10
+ program
11
+ .name('johankit')
12
+ .description('Developer-friendly CLI for codebase snapshots and AI vibe-coding')
13
+ .version('0.0.3');
14
+
15
+ program
16
+ .command('copy [dir] [exts]')
17
+ .action((dir = '.', exts) => copy(dir, exts?.split(',')));
18
+
19
+ program
20
+ .command('paste [dir]')
21
+ .option('--run', 'execute console commands')
22
+ .option('-y', 'auto accept commands without confirmation')
23
+ .option('--dry-run', 'list changes without applying them')
24
+ .option('--diff', 'show diff and ask for confirmation for each file')
25
+ .action((dir = '.', opts) => paste(dir, !!opts.run, !!opts.dryRun, !!opts.diff));
26
+
27
+ program
28
+ .command('prompt [dir] <request...>')
29
+ .action((dir = '.', request) => prompt(dir, request.join(' ')));
30
+
31
+ program
32
+ .command('sync [dir]')
33
+ .option('--run', 'execute console commands')
34
+ .option('-y', 'auto accept commands without confirmation')
35
+ .option('--dry-run', 'list changes without applying them')
36
+ .option('--diff', 'show diff and ask for confirmation for each file')
37
+ .action((dir = '.', opts) => sync(dir, !!opts.run, !!opts.dryRun, !!opts.diff));
38
+
39
+ program.parse();
package/src/types.ts CHANGED
@@ -1,59 +1,13 @@
1
1
  // src/types.ts
2
2
  export interface FileSnapshot {
3
3
  path: string;
4
- content: string;
4
+ content: string | null;
5
5
  }
6
6
 
7
7
  export interface ScanOptions {
8
- extensions?: string[]; // ['.js', '.ts', '.css']
9
- // A opção 'ignore' foi movida para o arquivo de configuração e 'loadConfig'
8
+ extensions?: string[];
10
9
  }
11
10
 
12
11
  export interface Config {
13
- ignore: string[]; // Patterns de arquivos/diretórios a ignorar
14
- }
15
-
16
- export type FileKind =
17
- | "entry"
18
- | "cli"
19
- | "domain"
20
- | "infra"
21
- | "util"
22
- | "unknown";
23
-
24
- export interface VariableInfo {
25
- name: string;
26
- type?: string;
27
- scope: "global" | "local";
28
- }
29
-
30
- export interface FunctionInfo {
31
- name: string;
32
- params: string[];
33
- returnType?: string;
34
- scope: "global" | "class";
35
- }
36
-
37
- export interface ClassInfo {
38
- name: string;
39
- methods: FunctionInfo[];
40
- }
41
-
42
- export interface ExportInfo {
43
- name: string;
44
- kind: "function" | "class" | "variable" | "type" | "unknown";
45
- }
46
-
47
- export interface FileTree {
48
- path: string;
49
- kind: FileKind;
50
-
51
- imports: string[];
52
-
53
- classes: ClassInfo[];
54
- functions: FunctionInfo[];
55
- variables: VariableInfo[];
56
-
57
- exports: ExportInfo[];
58
- mainExport?: string;
59
- }
12
+ ignore: string[];
13
+ }
@@ -1,13 +1,22 @@
1
+ // src/utils/cleanCodeBlock.ts
1
2
  export default function cleanCodeBlock(content: string) {
2
- const langMatch = content.match(/^```(\w+)?/);
3
- const lang = langMatch ? langMatch[1] : null;
3
+ // Regex robusta para capturar o primeiro array JSON dentro ou fora de blocos de código markdown
4
+ const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)\s*```/;
5
+ const match = content.match(codeBlockRegex);
4
6
 
5
- let cleaned = content.replace(/^\uFEFF/, '');
7
+ let cleaned = match ? match[1] : content;
6
8
 
7
- cleaned = cleaned.replace(/^```(\w+)?\s*/, '').replace(/```$/, '');
9
+ // Remove caracteres invisíveis como BOM (Byte Order Mark) e espaços extras
10
+ cleaned = cleaned.trim().replace(/\uFEFF/g, "");
8
11
 
9
- cleaned = cleaned.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F]/g, '');
10
- cleaned = cleaned.trim();
12
+ // Se ainda assim parecer que tem texto antes do array, tenta encontrar o início do array
13
+ if (!cleaned.startsWith("[")) {
14
+ const arrayStart = cleaned.indexOf("[");
15
+ const arrayEnd = cleaned.lastIndexOf("]");
16
+ if (arrayStart !== -1 && arrayEnd !== -1) {
17
+ cleaned = cleaned.substring(arrayStart, arrayEnd + 1);
18
+ }
19
+ }
11
20
 
12
- return { lang, cleaned };
13
- }
21
+ return { cleaned };
22
+ }
package/tsconfig.json CHANGED
@@ -1,17 +1,25 @@
1
1
  {
2
- "exclude": ["types.ts"],
3
2
  "compilerOptions": {
4
3
  "target": "ES2020",
5
4
  "module": "NodeNext",
6
5
  "moduleResolution": "NodeNext",
7
- "baseUrl": "./src",
6
+ "baseUrl": ".",
8
7
  "paths": {
9
- "*": ["*", "core/*"]
8
+ "*": ["src/*", "src/core/core/*", "cli/*"]
10
9
  },
11
10
  "outDir": "dist",
12
- "rootDir": "src",
11
+ "rootDir": ".",
13
12
  "esModuleInterop": true,
14
13
  "strict": true,
15
14
  "skipLibCheck": true
16
- }
17
- }
15
+ },
16
+ "include": [
17
+ "src/**/*",
18
+ "src/core/core/**/*",
19
+ "cli/**/*"
20
+ ],
21
+ "exclude": [
22
+ "node_modules",
23
+ "dist"
24
+ ]
25
+ }
package/Readme.md DELETED
@@ -1,56 +0,0 @@
1
- # JohanKit CLI
2
-
3
- JohanKit é uma ferramenta para copiar, colar e sincronizar snapshots de código de forma inteligente usando AI.
4
-
5
- ## Comandos
6
-
7
- ### Copy
8
- Copia um diretório ou arquivos específicos para o clipboard em formato JSON.
9
-
10
- ```bash
11
- johankit copy <dir> [exts]
12
- ```
13
-
14
- - `<dir>`: Diretório a copiar (padrão `.`)
15
- - `[exts]`: Extensões separadas por vírgula (ex: `ts,js`)
16
-
17
- ### Paste
18
- Aplica o conteúdo do clipboard em um diretório.
19
-
20
- ```bash
21
- johankit paste <dir>
22
- ```
23
-
24
- - `<dir>`: Diretório de destino (padrão `.`)
25
-
26
- ### Prompt
27
- Gera um prompt com o snapshot atual do diretório para AI, copiando para o clipboard.
28
-
29
- ```bash
30
- johankit prompt <dir> "<user request>"
31
- ```
32
-
33
- ### Sync
34
- Aplica patches gerados pela AI em um diretório e atualiza o clipboard com o novo snapshot.
35
-
36
- ```bash
37
- johankit sync <dir>
38
- ```
39
-
40
- ## Exemplo de Uso
41
-
42
- ```bash
43
- johankit prompt src "refatorar para async/await"
44
- johankit sync src
45
- ```
46
-
47
- ## Configuração
48
- Crie um arquivo `johankit.yaml` na raiz do projeto para customizar os diretórios e arquivos a ignorar.
49
-
50
- ```yaml
51
- ignore:
52
- - dist
53
- - node_modules
54
- ```
55
-
56
- Por padrão, os seguintes diretórios já são ignorados: `.git`, `node_modules`, `dist`, `build`, `coverage`, `tmp`, `temp`.
@@ -1,21 +0,0 @@
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(input) {
7
- let snapshot = [];
8
- const paths = Array.isArray(input) ? input : [input];
9
- for (const p of paths) {
10
- const scanned = (0, scan_1.scanDir)(p);
11
- snapshot.push(...scanned);
12
- }
13
- const clipboardJSON = JSON.stringify(snapshot, null, 2);
14
- try {
15
- await (0, clipboard_1.copyToClipboard)(clipboardJSON);
16
- }
17
- catch (err) {
18
- console.warn("Clipboard unavailable:", err);
19
- }
20
- return clipboardJSON;
21
- }
@@ -1,48 +0,0 @@
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.paste = paste;
7
- // src/cli/commands/paste.ts
8
- const write_1 = require("../../core/write");
9
- const clipboard_1 = require("../../core/clipboard");
10
- const cleanCodeBlock_1 = __importDefault(require("../../utils/cleanCodeBlock"));
11
- const diff_1 = require("../../core/diff");
12
- const validation_1 = require("../../core/validation");
13
- async function paste(dir) {
14
- try {
15
- const content = await (0, clipboard_1.readClipboard)();
16
- if (!content) {
17
- throw new Error("Clipboard empty or inaccessible");
18
- }
19
- let parsed;
20
- try {
21
- const { cleaned } = (0, cleanCodeBlock_1.default)(content);
22
- parsed = JSON.parse(cleaned);
23
- }
24
- catch (e) {
25
- throw new Error("Clipboard content is not valid JSON");
26
- }
27
- if (Array.isArray(parsed) && parsed.every(f => f.path && f.content)) {
28
- // Caso seja snapshot
29
- (0, write_1.writeFiles)(dir, parsed, true);
30
- }
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");
38
- }
39
- process.stdout.write("✔ Pasted from clipboard\n");
40
- }
41
- catch (error) {
42
- process.stderr.write("✖ Paste failed\n");
43
- if (error instanceof Error) {
44
- process.stderr.write(`${error.message}\n`);
45
- }
46
- process.exit(1);
47
- }
48
- }