johankit 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/paste.js +15 -10
- package/dist/cli/commands/prompt.js +39 -7
- package/dist/cli/commands/sync.js +10 -6
- package/dist/cli/commands/three.js +106 -0
- package/dist/index.js +52 -34
- package/dist/services/JohankitService.js +59 -0
- package/dist/utils/createAsciiTree.js +46 -0
- package/johankit.yaml +2 -0
- package/package.json +2 -1
- package/src/cli/commands/paste.ts +15 -16
- package/src/cli/commands/prompt.ts +45 -7
- package/src/cli/commands/sync.ts +8 -6
- package/src/cli/commands/three.ts +117 -0
- package/src/core/scan.ts +1 -1
- package/src/index.ts +54 -37
- package/src/services/JohankitService.ts +70 -0
- package/src/types.ts +45 -0
- package/src/utils/createAsciiTree.ts +53 -0
- package/cli/commands/paste.js +0 -42
- package/cli/commands/paste.ts +0 -42
- package/cli/commands/prompt.js +0 -59
- package/cli/commands/prompt.ts +0 -57
- package/cli/commands/sync.js +0 -83
- package/cli/commands/sync.ts +0 -87
- package/commands/paste.js +0 -42
- package/commands/paste.ts +0 -45
- package/core/config.js +0 -44
- package/core/config.ts +0 -42
- package/core/schema.js +0 -30
- package/core/schema.ts +0 -29
- package/core/vm.js +0 -20
- package/core/vm.ts +0 -18
- package/dist/core/clean.js +0 -13
package/src/cli/commands/sync.ts
CHANGED
|
@@ -5,11 +5,10 @@ import { copyToClipboard } from "../../core/clipboard";
|
|
|
5
5
|
import { validatePatches } from "../../core/validation";
|
|
6
6
|
import { writeFiles } from "../../core/write";
|
|
7
7
|
|
|
8
|
-
export async function sync(dir: string) {
|
|
8
|
+
export async function sync(dir: string, diff = false) {
|
|
9
9
|
try {
|
|
10
10
|
const snapshotBefore = scanDir(dir);
|
|
11
11
|
|
|
12
|
-
// Gera prompt genérico para AI com snapshot atual
|
|
13
12
|
const template = `
|
|
14
13
|
You are an AI software engineer.
|
|
15
14
|
|
|
@@ -33,7 +32,7 @@ ${JSON.stringify(snapshotBefore, null, 2)}
|
|
|
33
32
|
YOUR TASK
|
|
34
33
|
Propose changes according to the user request.
|
|
35
34
|
|
|
36
|
-
Return ONLY a JSON array of
|
|
35
|
+
Return ONLY a JSON array of ${diff ? 'DiffPatch' : 'FileSnapshot'}.
|
|
37
36
|
|
|
38
37
|
PATCH FORMAT (STRICT)
|
|
39
38
|
{
|
|
@@ -53,7 +52,6 @@ USER REQUEST
|
|
|
53
52
|
await copyToClipboard(template.trim());
|
|
54
53
|
process.stdout.write("✔ Prompt with snapshot copied to clipboard\n");
|
|
55
54
|
|
|
56
|
-
// Aguarda entrada do usuário (resposta da AI) via stdin
|
|
57
55
|
const input = await readStdin();
|
|
58
56
|
|
|
59
57
|
let patches;
|
|
@@ -63,8 +61,12 @@ USER REQUEST
|
|
|
63
61
|
throw new Error("Invalid JSON input");
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
if (diff) {
|
|
65
|
+
const validated = validatePatches(patches);
|
|
66
|
+
applyDiff(dir, validated);
|
|
67
|
+
} else {
|
|
68
|
+
writeFiles(dir, patches as any, true);
|
|
69
|
+
}
|
|
68
70
|
|
|
69
71
|
const snapshotAfter = scanDir(dir);
|
|
70
72
|
await copyToClipboard(JSON.stringify(snapshotAfter, null, 2));
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { Project, SourceFile, SyntaxKind } from "ts-morph";
|
|
3
|
+
import {
|
|
4
|
+
FileTree,
|
|
5
|
+
ClassInfo,
|
|
6
|
+
FunctionInfo,
|
|
7
|
+
VariableInfo,
|
|
8
|
+
ExportInfo,
|
|
9
|
+
FileKind
|
|
10
|
+
} from "types";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Heurística simples para classificar arquivos
|
|
14
|
+
*/
|
|
15
|
+
function detectFileKind(filePath: string, exportsCount: number): FileKind {
|
|
16
|
+
if (filePath.includes("/cli/")) return "cli";
|
|
17
|
+
if (filePath.endsWith("index.ts") || filePath.endsWith("main.ts")) return "entry";
|
|
18
|
+
if (exportsCount === 0) return "util";
|
|
19
|
+
if (exportsCount > 3) return "domain";
|
|
20
|
+
return "unknown";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function three(dir: string): Promise<FileTree[]> {
|
|
24
|
+
const project = new Project({});
|
|
25
|
+
|
|
26
|
+
project.addSourceFilesAtPaths([
|
|
27
|
+
`${dir}/**/*.{ts,tsx,js,jsx}`,
|
|
28
|
+
`!${dir}/**/node_modules/**/*`,
|
|
29
|
+
`!${dir}/**/dist/**/*`,
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const tree: FileTree[] = [];
|
|
33
|
+
|
|
34
|
+
for (const file of project.getSourceFiles()) {
|
|
35
|
+
const absolutePath = file.getFilePath();
|
|
36
|
+
const filePath = path.relative(dir, absolutePath);
|
|
37
|
+
|
|
38
|
+
if (filePath.includes("node_modules") || filePath.includes("/dist/")) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ---------------- IMPORTS ---------------- */
|
|
43
|
+
const imports = file
|
|
44
|
+
.getImportDeclarations()
|
|
45
|
+
.map(i => i.getModuleSpecifierValue());
|
|
46
|
+
|
|
47
|
+
/* ---------------- CLASSES ---------------- */
|
|
48
|
+
const classes: ClassInfo[] = file.getClasses().map(cls => ({
|
|
49
|
+
name: cls.getName() || "<anonymous>",
|
|
50
|
+
methods: cls.getMethods().map(m => ({
|
|
51
|
+
name: m.getName(),
|
|
52
|
+
params: m.getParameters().map(p => p.getName()),
|
|
53
|
+
returnType: safeType(() => m.getReturnType().getText()),
|
|
54
|
+
scope: "class",
|
|
55
|
+
}))
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
/* ---------------- FUNCTIONS ---------------- */
|
|
59
|
+
const functions: FunctionInfo[] = file.getFunctions().map(fn => ({
|
|
60
|
+
name: fn.getName() || "<anonymous>",
|
|
61
|
+
params: fn.getParameters().map(p => p.getName()),
|
|
62
|
+
returnType: safeType(() => fn.getReturnType().getText()),
|
|
63
|
+
scope: "global",
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
/* ---------------- VARIABLES ---------------- */
|
|
67
|
+
const variables: VariableInfo[] = file.getVariableDeclarations().map(v => ({
|
|
68
|
+
name: v.getName(),
|
|
69
|
+
type: safeType(() => v.getType().getText()),
|
|
70
|
+
scope: v.getParent() instanceof SourceFile ? "global" : "local",
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
/* ---------------- EXPORTS ---------------- */
|
|
74
|
+
const exports: ExportInfo[] = [];
|
|
75
|
+
let mainExport: string | undefined;
|
|
76
|
+
|
|
77
|
+
file.getExportedDeclarations().forEach((decls, name) => {
|
|
78
|
+
decls.forEach(d => {
|
|
79
|
+
let kind: ExportInfo["kind"] = "unknown";
|
|
80
|
+
|
|
81
|
+
if (d.getKind() === SyntaxKind.ClassDeclaration) kind = "class";
|
|
82
|
+
if (d.getKind() === SyntaxKind.FunctionDeclaration) kind = "function";
|
|
83
|
+
if (d.getKind() === SyntaxKind.VariableDeclaration) kind = "variable";
|
|
84
|
+
if (d.getKind() === SyntaxKind.TypeAliasDeclaration) kind = "type";
|
|
85
|
+
|
|
86
|
+
exports.push({ name, kind });
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (exports.length === 1) {
|
|
91
|
+
mainExport = exports[0].name;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const kind = detectFileKind(filePath, exports.length);
|
|
95
|
+
|
|
96
|
+
tree.push({
|
|
97
|
+
path: filePath,
|
|
98
|
+
kind,
|
|
99
|
+
imports,
|
|
100
|
+
classes,
|
|
101
|
+
functions,
|
|
102
|
+
variables,
|
|
103
|
+
exports,
|
|
104
|
+
mainExport,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return tree;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function safeType(fn: () => string): string {
|
|
112
|
+
try {
|
|
113
|
+
return fn();
|
|
114
|
+
} catch {
|
|
115
|
+
return "unknown";
|
|
116
|
+
}
|
|
117
|
+
}
|
package/src/core/scan.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,61 +1,78 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { paste } from "./cli/commands/paste";
|
|
5
|
-
import { prompt } from "./cli/commands/prompt";
|
|
6
|
-
import { sync } from "./cli/commands/sync";
|
|
3
|
+
import { JohankitService } from "./services/JohankitService";
|
|
7
4
|
|
|
8
5
|
const [, , command, ...args] = process.argv;
|
|
6
|
+
const service = new JohankitService();
|
|
9
7
|
|
|
10
8
|
async function main() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
try {
|
|
10
|
+
switch (command) {
|
|
11
|
+
case "copy": {
|
|
12
|
+
const dir = args[0] ?? ".";
|
|
13
|
+
await service.copy(dir);
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
case "paste": {
|
|
18
|
+
const dir = args[0] ?? ".";
|
|
19
|
+
await service.paste(dir);
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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;
|
|
31
29
|
}
|
|
32
|
-
await prompt(dir, userPrompt);
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
case "sync": {
|
|
32
|
+
const dir = args[0] ?? ".";
|
|
33
|
+
await service.sync(dir);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
case "three": {
|
|
38
|
+
const dir = args[0] ?? ".";
|
|
39
|
+
const output = await service.three(dir);
|
|
40
|
+
console.log(output);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
41
43
|
|
|
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);
|
|
44
55
|
}
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
function
|
|
58
|
+
function showHelp() {
|
|
48
59
|
console.log(`
|
|
49
60
|
Usage:
|
|
50
|
-
johankit copy <dir>
|
|
61
|
+
johankit copy <dir>
|
|
51
62
|
johankit paste <dir>
|
|
52
|
-
johankit prompt <dir> "<user request>"
|
|
63
|
+
johankit prompt <dir> "<user request>" [--diff]
|
|
53
64
|
johankit sync <dir>
|
|
65
|
+
johankit three <dir>
|
|
54
66
|
|
|
55
67
|
Examples:
|
|
56
|
-
johankit
|
|
68
|
+
johankit copy src
|
|
69
|
+
johankit paste src
|
|
70
|
+
johankit prompt src "refactor to async/await"
|
|
57
71
|
johankit sync src
|
|
72
|
+
johankit three src
|
|
58
73
|
`);
|
|
59
74
|
}
|
|
60
75
|
|
|
61
|
-
main();
|
|
76
|
+
main();
|
|
77
|
+
|
|
78
|
+
export { JohankitService };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import createAsciiTree from "../utils/createAsciiTree";
|
|
2
|
+
import { copy } from "../cli/commands/copy";
|
|
3
|
+
import { paste } from "../cli/commands/paste";
|
|
4
|
+
import { prompt } from "../cli/commands/prompt";
|
|
5
|
+
import { three } from "../cli/commands/three";
|
|
6
|
+
import { scanDir } from "../core/scan";
|
|
7
|
+
import { applyDiff, DiffPatch } from "../core/diff";
|
|
8
|
+
import { validatePatches } from "../core/validation";
|
|
9
|
+
import { writeFiles } from "../core/write";
|
|
10
|
+
|
|
11
|
+
interface JohankitServiceOptions {
|
|
12
|
+
isolated?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class JohankitService {
|
|
16
|
+
private isolated: boolean;
|
|
17
|
+
private internalInput?: any;
|
|
18
|
+
|
|
19
|
+
constructor(options: JohankitServiceOptions = {}) {
|
|
20
|
+
this.isolated = options.isolated ?? false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
setInput(input: any) {
|
|
24
|
+
this.internalInput = input;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async copy(dir: string = ".") {
|
|
28
|
+
return copy(dir);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async paste(dir: string = ".") {
|
|
32
|
+
return paste(dir);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async prompt(dir: string, userPrompt: string, diff = false) {
|
|
36
|
+
if (!userPrompt) {
|
|
37
|
+
throw new Error("Missing user prompt");
|
|
38
|
+
}
|
|
39
|
+
return prompt(dir, userPrompt, diff);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async sync(dir: string = ".", diff = false) {
|
|
43
|
+
const snapshotBefore = scanDir(dir);
|
|
44
|
+
|
|
45
|
+
const input = this.isolated ? this.internalInput : undefined;
|
|
46
|
+
|
|
47
|
+
if (!this.isolated && input === undefined) {
|
|
48
|
+
throw new Error("sync() without isolation must be used via CLI (stdin)");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!input) {
|
|
52
|
+
throw new Error("No input provided for isolated sync");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (diff) {
|
|
56
|
+
const validated = validatePatches(input) as DiffPatch[];
|
|
57
|
+
applyDiff(dir, validated);
|
|
58
|
+
} else {
|
|
59
|
+
writeFiles(dir, input, true);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const snapshotAfter = scanDir(dir);
|
|
63
|
+
return { before: snapshotBefore, after: snapshotAfter };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async three(dir: string = ".") {
|
|
67
|
+
const tree = await three(dir);
|
|
68
|
+
return createAsciiTree(tree);
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -12,3 +12,48 @@ export interface ScanOptions {
|
|
|
12
12
|
export interface Config {
|
|
13
13
|
ignore: string[]; // Patterns de arquivos/diretórios a ignorar
|
|
14
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
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { FileTree } from "types";
|
|
2
|
+
|
|
3
|
+
export default function createAsciiTree(files: FileTree[]): string {
|
|
4
|
+
let out = "";
|
|
5
|
+
|
|
6
|
+
files.forEach((file, i) => {
|
|
7
|
+
const lastFile = i === files.length - 1;
|
|
8
|
+
const fPrefix = lastFile ? "└── " : "├── ";
|
|
9
|
+
const fIndent = lastFile ? " " : "│ ";
|
|
10
|
+
|
|
11
|
+
out += `${fPrefix}📄 ${file.path} (${file.kind})\n`;
|
|
12
|
+
|
|
13
|
+
/* -------- Imports -------- */
|
|
14
|
+
if (file.imports.length) {
|
|
15
|
+
out += `${fIndent}├── 📦 Imports [${file.imports.length}]\n`;
|
|
16
|
+
file.imports.forEach((imp, idx) => {
|
|
17
|
+
const last = idx === file.imports.length - 1;
|
|
18
|
+
out += `${fIndent}${last ? "└── " : "├── "}${imp}\n`;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* -------- Exports -------- */
|
|
23
|
+
if (file.exports.length) {
|
|
24
|
+
out += `${fIndent}├── 🔑 Exports\n`;
|
|
25
|
+
file.exports.forEach((e, idx) => {
|
|
26
|
+
const last = idx === file.exports.length - 1;
|
|
27
|
+
const main = e.name === file.mainExport ? " ⭐" : "";
|
|
28
|
+
out += `${fIndent}${last ? "└── " : "├── "}${e.name} (${e.kind})${main}\n`;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* -------- Variables -------- */
|
|
33
|
+
file.variables.forEach(v => {
|
|
34
|
+
out += `${fIndent}├── 💡 ${v.name}: ${v.type} (${v.scope})\n`;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/* -------- Functions -------- */
|
|
38
|
+
file.functions.forEach(fn => {
|
|
39
|
+
out += `${fIndent}├── ⚡ ${fn.name}(${fn.params.join(", ")}): ${fn.returnType}\n`;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/* -------- Classes -------- */
|
|
43
|
+
file.classes.forEach(cls => {
|
|
44
|
+
out += `${fIndent}├── ⚙️ Class ${cls.name}\n`;
|
|
45
|
+
cls.methods.forEach((m, mi) => {
|
|
46
|
+
const last = mi === cls.methods.length - 1;
|
|
47
|
+
out += `${fIndent}│ ${last ? "└── " : "├── "}➡️ ${m.name}(${m.params.join(", ")}): ${m.returnType}\n`;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return out;
|
|
53
|
+
}
|
package/cli/commands/paste.js
DELETED
|
@@ -1,42 +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 = 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;
|
package/cli/commands/paste.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
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
|
-
}
|
package/cli/commands/prompt.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
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;
|
package/cli/commands/prompt.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
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
|
-
}
|