johankit 0.0.2 → 0.0.3
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/README.md +0 -5
- package/Readme.md +56 -0
- package/cli/commands/paste.js +42 -0
- package/cli/commands/paste.ts +42 -0
- package/cli/commands/prompt.js +59 -0
- package/cli/commands/prompt.ts +57 -0
- package/cli/commands/sync.js +83 -0
- package/cli/commands/sync.ts +87 -0
- package/commands/paste.js +42 -0
- package/commands/paste.ts +45 -0
- package/core/config.js +44 -0
- package/core/config.ts +42 -0
- package/core/schema.js +30 -0
- package/core/schema.ts +29 -0
- package/core/vm.js +20 -0
- package/core/vm.ts +18 -0
- package/dist/cli/commands/copy.js +3 -4
- package/dist/cli/commands/paste.js +6 -2
- package/dist/cli/commands/prompt.js +2 -2
- package/dist/core/clean.js +13 -0
- package/dist/utils/cleanCodeBlock.js +12 -0
- package/package.json +4 -2
- package/src/cli/commands/copy.ts +3 -4
- package/src/cli/commands/paste.ts +3 -2
- package/src/cli/commands/prompt.ts +3 -3
- package/src/utils/cleanCodeBlock.ts +13 -0
- package/tsconfig.json +4 -0
- package/dist/tests/cli/commands/copy.test.js +0 -47
- package/dist/tests/cli/commands/paste.test.js +0 -41
- package/dist/tests/cli/commands/prompt.test.js +0 -37
- package/dist/tests/cli/commands/sync.test.js +0 -47
- package/dist/tests/core/clipboard.test.js +0 -20
- package/dist/tests/core/config.test.js +0 -23
- package/dist/tests/core/diff.test.js +0 -24
- package/dist/tests/core/git.test.js +0 -11
- package/dist/tests/core/scan.test.js +0 -16
- package/dist/tests/core/schema.test.js +0 -13
- package/dist/tests/core/validation.test.js +0 -13
- package/dist/tests/core/write.test.js +0 -41
- package/package-lock.json +0 -250
- package/src/tests/cli/commands/copy.test.ts +0 -26
- package/src/tests/cli/commands/paste.test.ts +0 -19
- package/src/tests/cli/commands/prompt.test.ts +0 -14
- package/src/tests/cli/commands/sync.test.ts +0 -26
- package/src/tests/core/clipboard.test.ts +0 -21
- package/src/tests/core/config.test.ts +0 -21
- package/src/tests/core/diff.test.ts +0 -22
- package/src/tests/core/git.test.ts +0 -11
- package/src/tests/core/scan.test.ts +0 -13
- package/src/tests/core/schema.test.ts +0 -13
- package/src/tests/core/validation.test.ts +0 -13
- 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`.
|
|
@@ -0,0 +1,42 @@
|
|
|
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 = void 0;
|
|
7
|
+
const write_1 = require("../../core/write");
|
|
8
|
+
const clipboard_1 = require("../../core/clipboard");
|
|
9
|
+
const cleanCodeBlock_1 = __importDefault(require("../../utils/cleanCodeBlock"));
|
|
10
|
+
async function paste(dir) {
|
|
11
|
+
try {
|
|
12
|
+
const content = await (0, clipboard_1.readClipboard)();
|
|
13
|
+
if (!content) {
|
|
14
|
+
throw new Error("Clipboard empty or inaccessible");
|
|
15
|
+
}
|
|
16
|
+
let files;
|
|
17
|
+
try {
|
|
18
|
+
const { lang, cleaned } = (0, cleanCodeBlock_1.default)(content);
|
|
19
|
+
files = JSON.parse(cleaned);
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
throw new Error("Clipboard content is not valid JSON");
|
|
23
|
+
}
|
|
24
|
+
if (!Array.isArray(files)) {
|
|
25
|
+
throw new Error("Clipboard content is not a JSON array");
|
|
26
|
+
}
|
|
27
|
+
const isValidSnapshot = files.every(f => typeof f.path === 'string' && typeof f.content === 'string');
|
|
28
|
+
if (!isValidSnapshot) {
|
|
29
|
+
throw new Error("JSON does not match FileSnapshot structure {path, content}");
|
|
30
|
+
}
|
|
31
|
+
(0, write_1.writeFiles)(dir, files, true);
|
|
32
|
+
process.stdout.write("✔ Pasted from clipboard\n");
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
process.stderr.write("✖ Paste failed\n");
|
|
36
|
+
if (error instanceof Error) {
|
|
37
|
+
process.stderr.write(`${error.message}\n`);
|
|
38
|
+
}
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.paste = paste;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { writeFiles } from "../../core/write";
|
|
2
|
+
import { readClipboard } from "../../core/clipboard";
|
|
3
|
+
import cleanCodeBlock from "../../utils/cleanCodeBlock";
|
|
4
|
+
|
|
5
|
+
export async function paste(dir: string) {
|
|
6
|
+
try {
|
|
7
|
+
const content = await readClipboard();
|
|
8
|
+
|
|
9
|
+
if (!content) {
|
|
10
|
+
throw new Error("Clipboard empty or inaccessible");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let files;
|
|
14
|
+
try {
|
|
15
|
+
const { lang, cleaned } = cleanCodeBlock(content);
|
|
16
|
+
files = JSON.parse(cleaned);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
throw new Error("Clipboard content is not valid JSON");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!Array.isArray(files)) {
|
|
22
|
+
throw new Error("Clipboard content is not a JSON array");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const isValidSnapshot = files.every(f =>
|
|
26
|
+
typeof f.path === 'string' && typeof f.content === 'string'
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (!isValidSnapshot) {
|
|
30
|
+
throw new Error("JSON does not match FileSnapshot structure {path, content}");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
writeFiles(dir, files, true);
|
|
34
|
+
process.stdout.write("✔ Pasted from clipboard\n");
|
|
35
|
+
} catch (error) {
|
|
36
|
+
process.stderr.write("✖ Paste failed\n");
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
process.stderr.write(`${error.message}\n`);
|
|
39
|
+
}
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prompt = void 0;
|
|
4
|
+
// src/cli/commands/prompt.ts
|
|
5
|
+
const scan_1 = require("../../core/scan");
|
|
6
|
+
const clipboard_1 = require("../../core/clipboard");
|
|
7
|
+
const vm_1 = require("../../core/vm");
|
|
8
|
+
async function prompt(dir, userPrompt) {
|
|
9
|
+
const snapshot = (0, scan_1.scanDir)(dir);
|
|
10
|
+
const template = `
|
|
11
|
+
You are an AI software engineer.
|
|
12
|
+
|
|
13
|
+
You will receive a JSON array representing a snapshot of a codebase.
|
|
14
|
+
Each item has the following structure:
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
"path": "relative/path/to/file.ext",
|
|
18
|
+
"content": "full file content"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
SNAPSHOT
|
|
24
|
+
${JSON.stringify(snapshot, null, 2)}
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
YOUR TASK
|
|
29
|
+
Propose changes according to the user request.
|
|
30
|
+
|
|
31
|
+
Return ONLY a JSON array of patches.
|
|
32
|
+
|
|
33
|
+
PATCH FORMAT (STRICT)
|
|
34
|
+
{
|
|
35
|
+
"path": "relative/path/to/file.ext",
|
|
36
|
+
"content": "FULL updated file content (omit for delete)"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
IMPORTANT RULES
|
|
40
|
+
- Do NOT return explanations
|
|
41
|
+
- Do NOT return markdown
|
|
42
|
+
- Return ONLY valid JSON inside the \"\`\`\`\"
|
|
43
|
+
- Always return within a Markdown Code Block (with \"\`\`\`json\" syntax highlighting)\")
|
|
44
|
+
|
|
45
|
+
USER REQUEST
|
|
46
|
+
${userPrompt}
|
|
47
|
+
`;
|
|
48
|
+
try {
|
|
49
|
+
(0, vm_1.runInVm)(`module.exports = async function() { return ${JSON.stringify(template.trim())}; }`)();
|
|
50
|
+
await (0, clipboard_1.copyToClipboard)(template.trim());
|
|
51
|
+
process.stdout.write(template.trim());
|
|
52
|
+
process.stdout.write("\n\n✔ Prompt + Snapshot copied to clipboard\n");
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
process.stdout.write(template.trim());
|
|
56
|
+
process.stderr.write("\n✖ Failed to execute in VM or copy to clipboard\n");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.prompt = prompt;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/cli/commands/prompt.ts
|
|
2
|
+
import { scanDir } from "../../core/scan";
|
|
3
|
+
import { copyToClipboard } from "../../core/clipboard";
|
|
4
|
+
import { runInVm } from "../../core/vm";
|
|
5
|
+
|
|
6
|
+
export async function prompt(dir: string, userPrompt: string) {
|
|
7
|
+
const snapshot = scanDir(dir);
|
|
8
|
+
|
|
9
|
+
const template = `
|
|
10
|
+
You are an AI software engineer.
|
|
11
|
+
|
|
12
|
+
You will receive a JSON array representing a snapshot of a codebase.
|
|
13
|
+
Each item has the following structure:
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
"path": "relative/path/to/file.ext",
|
|
17
|
+
"content": "full file content"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
SNAPSHOT
|
|
23
|
+
${JSON.stringify(snapshot, null, 2)}
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
YOUR TASK
|
|
28
|
+
Propose changes according to the user request.
|
|
29
|
+
|
|
30
|
+
Return ONLY a JSON array of patches.
|
|
31
|
+
|
|
32
|
+
PATCH FORMAT (STRICT)
|
|
33
|
+
{
|
|
34
|
+
"path": "relative/path/to/file.ext",
|
|
35
|
+
"content": "FULL updated file content (omit for delete)"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
IMPORTANT RULES
|
|
39
|
+
- Do NOT return explanations
|
|
40
|
+
- Do NOT return markdown
|
|
41
|
+
- Return ONLY valid JSON inside the \"\`\`\`\"
|
|
42
|
+
- Always return within a Markdown Code Block (with \"\`\`\`json\" syntax highlighting)\")
|
|
43
|
+
|
|
44
|
+
USER REQUEST
|
|
45
|
+
${userPrompt}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
runInVm(`module.exports = async function() { return ${JSON.stringify(template.trim())}; }`)();
|
|
50
|
+
await copyToClipboard(template.trim());
|
|
51
|
+
process.stdout.write(template.trim());
|
|
52
|
+
process.stdout.write("\n\n✔ Prompt + Snapshot copied to clipboard\n");
|
|
53
|
+
} catch (e) {
|
|
54
|
+
process.stdout.write(template.trim());
|
|
55
|
+
process.stderr.write("\n✖ Failed to execute in VM or copy to clipboard\n");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sync = void 0;
|
|
4
|
+
// src/cli/commands/sync.ts
|
|
5
|
+
const scan_1 = require("../../core/scan");
|
|
6
|
+
const diff_1 = require("../../core/diff");
|
|
7
|
+
const clipboard_1 = require("../../core/clipboard");
|
|
8
|
+
const validation_1 = require("../../core/validation");
|
|
9
|
+
const vm_1 = require("../../core/vm");
|
|
10
|
+
async function sync(dir) {
|
|
11
|
+
try {
|
|
12
|
+
const snapshotBefore = (0, scan_1.scanDir)(dir);
|
|
13
|
+
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
|
+
${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 patches.
|
|
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
|
+
`;
|
|
52
|
+
await (0, clipboard_1.copyToClipboard)(template.trim());
|
|
53
|
+
process.stdout.write("✔ Prompt with snapshot copied to clipboard\n");
|
|
54
|
+
const input = await readStdin();
|
|
55
|
+
let patches;
|
|
56
|
+
try {
|
|
57
|
+
patches = (0, vm_1.runInVm)(`module.exports = function() { return ${input}; }`)();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new Error("Invalid JSON input or execution in VM failed");
|
|
61
|
+
}
|
|
62
|
+
const validated = (0, validation_1.validatePatches)(patches);
|
|
63
|
+
(0, diff_1.applyDiff)(dir, validated);
|
|
64
|
+
const snapshotAfter = (0, scan_1.scanDir)(dir);
|
|
65
|
+
await (0, clipboard_1.copyToClipboard)(JSON.stringify(snapshotAfter, null, 2));
|
|
66
|
+
process.stdout.write("✔ Sync applied and new snapshot copied to clipboard\n");
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
process.stderr.write("✖ Sync failed\n");
|
|
70
|
+
if (error instanceof Error) {
|
|
71
|
+
process.stderr.write(`${error.message}\n`);
|
|
72
|
+
}
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.sync = sync;
|
|
77
|
+
function readStdin() {
|
|
78
|
+
return new Promise(resolve => {
|
|
79
|
+
let data = "";
|
|
80
|
+
process.stdin.on("data", c => (data += c));
|
|
81
|
+
process.stdin.on("end", () => resolve(data));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/cli/commands/sync.ts
|
|
2
|
+
import { scanDir } from "../../core/scan";
|
|
3
|
+
import { applyDiff } from "../../core/diff";
|
|
4
|
+
import { copyToClipboard } from "../../core/clipboard";
|
|
5
|
+
import { validatePatches } from "../../core/validation";
|
|
6
|
+
import { writeFiles } from "../../core/write";
|
|
7
|
+
import { runInVm } from "../../core/vm";
|
|
8
|
+
|
|
9
|
+
export async function sync(dir: string) {
|
|
10
|
+
try {
|
|
11
|
+
const snapshotBefore = scanDir(dir);
|
|
12
|
+
|
|
13
|
+
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
|
+
${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 patches.
|
|
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
|
+
`;
|
|
52
|
+
|
|
53
|
+
await copyToClipboard(template.trim());
|
|
54
|
+
process.stdout.write("✔ Prompt with snapshot copied to clipboard\n");
|
|
55
|
+
|
|
56
|
+
const input = await readStdin();
|
|
57
|
+
|
|
58
|
+
let patches;
|
|
59
|
+
try {
|
|
60
|
+
patches = runInVm(`module.exports = function() { return ${input}; }`)();
|
|
61
|
+
} catch {
|
|
62
|
+
throw new Error("Invalid JSON input or execution in VM failed");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const validated = validatePatches(patches);
|
|
66
|
+
applyDiff(dir, validated);
|
|
67
|
+
|
|
68
|
+
const snapshotAfter = scanDir(dir);
|
|
69
|
+
await copyToClipboard(JSON.stringify(snapshotAfter, null, 2));
|
|
70
|
+
|
|
71
|
+
process.stdout.write("✔ Sync applied and new snapshot copied to clipboard\n");
|
|
72
|
+
} catch (error) {
|
|
73
|
+
process.stderr.write("✖ Sync failed\n");
|
|
74
|
+
if (error instanceof Error) {
|
|
75
|
+
process.stderr.write(`${error.message}\n`);
|
|
76
|
+
}
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readStdin(): Promise<string> {
|
|
82
|
+
return new Promise(resolve => {
|
|
83
|
+
let data = "";
|
|
84
|
+
process.stdin.on("data", c => (data += c));
|
|
85
|
+
process.stdin.on("end", () => resolve(data));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.paste = void 0;
|
|
4
|
+
// src/cli/commands/paste.ts
|
|
5
|
+
const write_1 = require("../../core/write");
|
|
6
|
+
const clipboard_1 = require("../../core/clipboard");
|
|
7
|
+
async function paste(dir) {
|
|
8
|
+
try {
|
|
9
|
+
const content = await (0, clipboard_1.readClipboard)();
|
|
10
|
+
if (!content) {
|
|
11
|
+
throw new Error("Clipboard empty or inaccessible");
|
|
12
|
+
}
|
|
13
|
+
let files;
|
|
14
|
+
try {
|
|
15
|
+
let cleanContent = content.replace(/^\uFEFF/, "").trim();
|
|
16
|
+
// Remove code block markers ``` ou ````` caso existam
|
|
17
|
+
cleanContent = cleanContent.replace(/```+\s*json?/g, "").replace(/```+/g, "");
|
|
18
|
+
files = JSON.parse(cleanContent);
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
throw new Error("Clipboard content is not valid JSON");
|
|
22
|
+
}
|
|
23
|
+
if (!Array.isArray(files)) {
|
|
24
|
+
throw new Error("Clipboard content is not a JSON array");
|
|
25
|
+
}
|
|
26
|
+
// Validação simples do snapshot
|
|
27
|
+
const isValidSnapshot = files.every(f => typeof f.path === 'string' && typeof f.content === 'string');
|
|
28
|
+
if (!isValidSnapshot) {
|
|
29
|
+
throw new Error("JSON does not match FileSnapshot structure {path, content}");
|
|
30
|
+
}
|
|
31
|
+
(0, write_1.writeFiles)(dir, files, true);
|
|
32
|
+
process.stdout.write("✔ Pasted from clipboard\n");
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
process.stderr.write("✖ Paste failed\n");
|
|
36
|
+
if (error instanceof Error) {
|
|
37
|
+
process.stderr.write(`${error.message}\n`);
|
|
38
|
+
}
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.paste = paste;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/cli/commands/paste.ts
|
|
2
|
+
import { writeFiles } from "../../core/write";
|
|
3
|
+
import { readClipboard } from "../../core/clipboard";
|
|
4
|
+
|
|
5
|
+
export async function paste(dir: string) {
|
|
6
|
+
try {
|
|
7
|
+
const content = await readClipboard();
|
|
8
|
+
|
|
9
|
+
if (!content) {
|
|
10
|
+
throw new Error("Clipboard empty or inaccessible");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let files;
|
|
14
|
+
try {
|
|
15
|
+
let cleanContent = content.replace(/^\uFEFF/, "").trim();
|
|
16
|
+
// Remove code block markers ``` ou ````` caso existam
|
|
17
|
+
cleanContent = cleanContent.replace(/```+\s*json?/g, "").replace(/```+/g, "");
|
|
18
|
+
files = JSON.parse(cleanContent);
|
|
19
|
+
} catch (e) {
|
|
20
|
+
throw new Error("Clipboard content is not valid JSON");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!Array.isArray(files)) {
|
|
24
|
+
throw new Error("Clipboard content is not a JSON array");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Validação simples do snapshot
|
|
28
|
+
const isValidSnapshot = files.every(f =>
|
|
29
|
+
typeof f.path === 'string' && typeof f.content === 'string'
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (!isValidSnapshot) {
|
|
33
|
+
throw new Error("JSON does not match FileSnapshot structure {path, content}");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
writeFiles(dir, files, true);
|
|
37
|
+
process.stdout.write("✔ Pasted from clipboard\n");
|
|
38
|
+
} catch (error) {
|
|
39
|
+
process.stderr.write("✖ Paste failed\n");
|
|
40
|
+
if (error instanceof Error) {
|
|
41
|
+
process.stderr.write(`${error.message}\n`);
|
|
42
|
+
}
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
package/core/config.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
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.loadConfig = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const js_yaml_1 = require("js-yaml");
|
|
10
|
+
const CONFIG_FILENAME = "johankit.yaml";
|
|
11
|
+
const DEFAULT_IGNORE = [
|
|
12
|
+
".git",
|
|
13
|
+
"node_modules",
|
|
14
|
+
"dist",
|
|
15
|
+
"build",
|
|
16
|
+
"coverage",
|
|
17
|
+
"tmp",
|
|
18
|
+
"temp",
|
|
19
|
+
];
|
|
20
|
+
function loadConfig(basePath) {
|
|
21
|
+
const configPath = path_1.default.join(basePath, CONFIG_FILENAME);
|
|
22
|
+
try {
|
|
23
|
+
const content = (0, fs_1.readFileSync)(configPath, "utf8");
|
|
24
|
+
const loadedConfig = (0, js_yaml_1.load)(content);
|
|
25
|
+
return {
|
|
26
|
+
ignore: [
|
|
27
|
+
...DEFAULT_IGNORE,
|
|
28
|
+
...(loadedConfig.ignore || []),
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (error instanceof Error && error.code === "ENOENT") {
|
|
34
|
+
return {
|
|
35
|
+
ignore: DEFAULT_IGNORE,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
console.warn(`[johankit] Aviso: Falha ao carregar ${CONFIG_FILENAME}. Usando defaults.`, error);
|
|
39
|
+
return {
|
|
40
|
+
ignore: DEFAULT_IGNORE,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.loadConfig = loadConfig;
|
package/core/config.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { load } from "js-yaml";
|
|
4
|
+
import { Config } from "../types";
|
|
5
|
+
|
|
6
|
+
const CONFIG_FILENAME = "johankit.yaml";
|
|
7
|
+
const DEFAULT_IGNORE = [
|
|
8
|
+
".git",
|
|
9
|
+
"node_modules",
|
|
10
|
+
"dist",
|
|
11
|
+
"build",
|
|
12
|
+
"coverage",
|
|
13
|
+
"tmp",
|
|
14
|
+
"temp",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export function loadConfig(basePath: string): Config {
|
|
18
|
+
const configPath = path.join(basePath, CONFIG_FILENAME);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const content = readFileSync(configPath, "utf8");
|
|
22
|
+
const loadedConfig = load(content) as Partial<Config>;
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
ignore: [
|
|
26
|
+
...DEFAULT_IGNORE,
|
|
27
|
+
...(loadedConfig.ignore || []),
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error instanceof Error && (error as any).code === "ENOENT") {
|
|
32
|
+
return {
|
|
33
|
+
ignore: DEFAULT_IGNORE,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.warn(`[johankit] Aviso: Falha ao carregar ${CONFIG_FILENAME}. Usando defaults.`, error);
|
|
38
|
+
return {
|
|
39
|
+
ignore: DEFAULT_IGNORE,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
package/core/schema.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validatePatches = void 0;
|
|
4
|
+
function isValidPatch(patch) {
|
|
5
|
+
if (typeof patch !== "object" || patch === null)
|
|
6
|
+
return false;
|
|
7
|
+
if (typeof patch.path !== "string" || patch.path.length === 0)
|
|
8
|
+
return false;
|
|
9
|
+
const validTypes = ["modify", "create", "delete"];
|
|
10
|
+
if (!validTypes.includes(patch.type))
|
|
11
|
+
return false;
|
|
12
|
+
if (patch.type === "delete") {
|
|
13
|
+
return patch.content === undefined;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
return typeof patch.content === "string";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function validatePatches(patches) {
|
|
20
|
+
if (!Array.isArray(patches)) {
|
|
21
|
+
throw new Error("O patch deve ser um array JSON válido");
|
|
22
|
+
}
|
|
23
|
+
for (const [index, patch] of patches.entries()) {
|
|
24
|
+
if (!isValidPatch(patch)) {
|
|
25
|
+
throw new Error(`Patch inválido no índice ${index}: ${JSON.stringify(patch, null, 2)}.\nEsperado: { type: 'modify'|'create'|'delete', path: string, content?: string }`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return patches;
|
|
29
|
+
}
|
|
30
|
+
exports.validatePatches = validatePatches;
|
package/core/schema.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DiffPatch } from "./diff";
|
|
2
|
+
|
|
3
|
+
function isValidPatch(patch: any): boolean {
|
|
4
|
+
if (typeof patch !== "object" || patch === null) return false;
|
|
5
|
+
if (typeof patch.path !== "string" || patch.path.length === 0) return false;
|
|
6
|
+
|
|
7
|
+
const validTypes = ["modify", "create", "delete"];
|
|
8
|
+
if (!validTypes.includes(patch.type)) return false;
|
|
9
|
+
|
|
10
|
+
if (patch.type === "delete") {
|
|
11
|
+
return patch.content === undefined;
|
|
12
|
+
} else {
|
|
13
|
+
return typeof patch.content === "string";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function validatePatches(patches: any): DiffPatch[] {
|
|
18
|
+
if (!Array.isArray(patches)) {
|
|
19
|
+
throw new Error("O patch deve ser um array JSON válido");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (const [index, patch] of patches.entries()) {
|
|
23
|
+
if (!isValidPatch(patch)) {
|
|
24
|
+
throw new Error(`Patch inválido no índice ${index}: ${JSON.stringify(patch, null, 2)}.\nEsperado: { type: 'modify'|'create'|'delete', path: string, content?: string }`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return patches as DiffPatch[];
|
|
29
|
+
}
|