johankit 0.0.2 → 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.
Files changed (50) hide show
  1. package/README.md +0 -5
  2. package/Readme.md +56 -0
  3. package/dist/cli/commands/copy.js +3 -4
  4. package/dist/cli/commands/paste.js +19 -10
  5. package/dist/cli/commands/prompt.js +40 -8
  6. package/dist/cli/commands/sync.js +10 -6
  7. package/dist/cli/commands/three.js +106 -0
  8. package/dist/index.js +52 -34
  9. package/dist/services/JohankitService.js +59 -0
  10. package/dist/utils/cleanCodeBlock.js +12 -0
  11. package/dist/utils/createAsciiTree.js +46 -0
  12. package/johankit.yaml +2 -0
  13. package/package.json +5 -2
  14. package/src/cli/commands/copy.ts +3 -4
  15. package/src/cli/commands/paste.ts +16 -16
  16. package/src/cli/commands/prompt.ts +47 -9
  17. package/src/cli/commands/sync.ts +8 -6
  18. package/src/cli/commands/three.ts +117 -0
  19. package/src/core/scan.ts +1 -1
  20. package/src/index.ts +54 -37
  21. package/src/services/JohankitService.ts +70 -0
  22. package/src/types.ts +45 -0
  23. package/src/utils/cleanCodeBlock.ts +13 -0
  24. package/src/utils/createAsciiTree.ts +53 -0
  25. package/tsconfig.json +4 -0
  26. package/dist/tests/cli/commands/copy.test.js +0 -47
  27. package/dist/tests/cli/commands/paste.test.js +0 -41
  28. package/dist/tests/cli/commands/prompt.test.js +0 -37
  29. package/dist/tests/cli/commands/sync.test.js +0 -47
  30. package/dist/tests/core/clipboard.test.js +0 -20
  31. package/dist/tests/core/config.test.js +0 -23
  32. package/dist/tests/core/diff.test.js +0 -24
  33. package/dist/tests/core/git.test.js +0 -11
  34. package/dist/tests/core/scan.test.js +0 -16
  35. package/dist/tests/core/schema.test.js +0 -13
  36. package/dist/tests/core/validation.test.js +0 -13
  37. package/dist/tests/core/write.test.js +0 -41
  38. package/package-lock.json +0 -250
  39. package/src/tests/cli/commands/copy.test.ts +0 -26
  40. package/src/tests/cli/commands/paste.test.ts +0 -19
  41. package/src/tests/cli/commands/prompt.test.ts +0 -14
  42. package/src/tests/cli/commands/sync.test.ts +0 -26
  43. package/src/tests/core/clipboard.test.ts +0 -21
  44. package/src/tests/core/config.test.ts +0 -21
  45. package/src/tests/core/diff.test.ts +0 -22
  46. package/src/tests/core/git.test.ts +0 -11
  47. package/src/tests/core/scan.test.ts +0 -13
  48. package/src/tests/core/schema.test.ts +0 -13
  49. package/src/tests/core/validation.test.ts +0 -13
  50. package/src/tests/core/write.test.ts +0 -15
package/README.md CHANGED
@@ -8,11 +8,6 @@ Think of it as your personal **code snapshot toolkit**: lightweight, fast, and a
8
8
 
9
9
  ## Why JohanKit?
10
10
 
11
- The name combines:
12
-
13
- - **Johan** – a nod to the “creator” or developer archetype.
14
- - **Kit** – a set of practical tools.
15
-
16
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.
17
12
 
18
13
  ---
package/Readme.md ADDED
@@ -0,0 +1,56 @@
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`.
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.copy = void 0;
4
4
  const scan_1 = require("../../core/scan");
5
5
  const clipboard_1 = require("../../core/clipboard");
6
- function copy(input) {
6
+ async function copy(input) {
7
7
  let snapshot;
8
8
  if (Array.isArray(input)) {
9
9
  snapshot = input.map(path => {
@@ -15,10 +15,9 @@ function copy(input) {
15
15
  });
16
16
  }
17
17
  else {
18
- const stat = (0, scan_1.scanDir)(input);
19
- snapshot = stat.length === 1 ? stat : (0, scan_1.scanDir)(input);
18
+ snapshot = (0, scan_1.scanDir)(input);
20
19
  }
21
20
  const clipboardJSON = JSON.stringify(snapshot, null, 2); // <- garante JSON válido
22
- (0, clipboard_1.copyToClipboard)(clipboardJSON);
21
+ await (0, clipboard_1.copyToClipboard)(clipboardJSON);
23
22
  }
24
23
  exports.copy = copy;
@@ -1,32 +1,41 @@
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
6
  exports.paste = void 0;
4
7
  // src/cli/commands/paste.ts
5
8
  const write_1 = require("../../core/write");
6
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");
7
13
  async function paste(dir) {
8
14
  try {
9
15
  const content = await (0, clipboard_1.readClipboard)();
10
16
  if (!content) {
11
17
  throw new Error("Clipboard empty or inaccessible");
12
18
  }
13
- let files;
19
+ let parsed;
14
20
  try {
15
- const cleanContent = content.replace(/^\uFEFF/, "").trim();
16
- files = JSON.parse(cleanContent);
21
+ const { cleaned } = (0, cleanCodeBlock_1.default)(content);
22
+ parsed = JSON.parse(cleaned);
17
23
  }
18
24
  catch (e) {
19
25
  throw new Error("Clipboard content is not valid JSON");
20
26
  }
21
- if (!Array.isArray(files)) {
22
- 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);
23
30
  }
24
- // Validação simples do snapshot
25
- const isValidSnapshot = files.every(f => typeof f.path === 'string' && typeof f.content === 'string');
26
- if (!isValidSnapshot) {
27
- 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");
28
38
  }
29
- (0, write_1.writeFiles)(dir, files, true);
30
39
  process.stdout.write("✔ Pasted from clipboard\n");
31
40
  }
32
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
- - Return ONLY valid JSON inside the "\`\`\`"
42
- - Always return within a Markdown Code Block (with "\`\`\`json" syntax highlighting)")
73
+ - Return ONLY valid JSON inside the \"\`\`\`\"
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,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function cleanCodeBlock(content) {
4
+ const langMatch = content.match(/^```(\w+)?/);
5
+ const lang = langMatch ? langMatch[1] : null;
6
+ let cleaned = content.replace(/^\uFEFF/, '');
7
+ cleaned = cleaned.replace(/^```(\w+)?\s*/, '').replace(/```$/, '');
8
+ cleaned = cleaned.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F]/g, '');
9
+ cleaned = cleaned.trim();
10
+ return { lang, cleaned };
11
+ }
12
+ exports.default = cleanCodeBlock;
@@ -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.2",
3
+ "version": "0.0.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -25,6 +25,9 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "clipboardy": "^5.0.2",
28
- "commander": "^14.0.2"
28
+ "commander": "^14.0.2",
29
+ "js-yaml": "^4.1.1",
30
+ "ts-morph": "^27.0.2",
31
+ "vm2": "^3.10.0"
29
32
  }
30
33
  }
@@ -1,7 +1,7 @@
1
1
  import { scanDir } from "../../core/scan";
2
2
  import { copyToClipboard } from "../../core/clipboard";
3
3
 
4
- export function copy(input: string | string[]) {
4
+ export async function copy(input: string | string[]) {
5
5
  let snapshot;
6
6
 
7
7
  if (Array.isArray(input)) {
@@ -13,10 +13,9 @@ export function copy(input: string | string[]) {
13
13
  return fileSnapshot[0];
14
14
  });
15
15
  } else {
16
- const stat = scanDir(input);
17
- snapshot = stat.length === 1 ? stat : scanDir(input);
16
+ snapshot = scanDir(input);
18
17
  }
19
18
 
20
19
  const clipboardJSON = JSON.stringify(snapshot, null, 2); // <- garante JSON válido
21
- copyToClipboard(clipboardJSON);
20
+ await copyToClipboard(clipboardJSON);
22
21
  }