@akanjs/devkit 2.2.2-rc.0 → 2.2.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.
- package/aiEditor.ts +20 -76
- package/package.json +2 -2
package/aiEditor.ts
CHANGED
|
@@ -21,7 +21,7 @@ const MAX_ASK_TRY = 300;
|
|
|
21
21
|
|
|
22
22
|
const deepSeekLlmModels = ["deepseek-chat", "deepseek-reasoner"] as const;
|
|
23
23
|
|
|
24
|
-
const openAiLlmModels = ["gpt-5.5"
|
|
24
|
+
const openAiLlmModels = ["gpt-5.5"] as const;
|
|
25
25
|
|
|
26
26
|
export const supportedLlmModels = [...deepSeekLlmModels, ...openAiLlmModels] as const;
|
|
27
27
|
export type SupportedLlmModel = (typeof supportedLlmModels)[number];
|
|
@@ -60,44 +60,25 @@ export const parseTypescriptFileBlocks = (text: string): FileContent[] => {
|
|
|
60
60
|
return fileBlocks;
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
export const preserveTypescriptResponseContent = (
|
|
64
|
-
previousContent: string,
|
|
65
|
-
nextContent: string,
|
|
66
|
-
) => {
|
|
63
|
+
export const preserveTypescriptResponseContent = (previousContent: string, nextContent: string) => {
|
|
67
64
|
const previousWrites = parseTypescriptFileBlocks(previousContent);
|
|
68
65
|
const nextWrites = parseTypescriptFileBlocks(nextContent);
|
|
69
|
-
if (previousWrites.length > 0 && nextWrites.length === 0)
|
|
70
|
-
return previousContent;
|
|
66
|
+
if (previousWrites.length > 0 && nextWrites.length === 0) return previousContent;
|
|
71
67
|
return nextContent;
|
|
72
68
|
};
|
|
73
69
|
|
|
74
70
|
export class AiSession {
|
|
75
71
|
static #cacheDir = "node_modules/.cache/akan/aiSession";
|
|
76
72
|
static #chat: ChatDeepSeek | ChatOpenAI | null = null;
|
|
77
|
-
static async init({
|
|
78
|
-
temperature = 0,
|
|
79
|
-
useExisting = true,
|
|
80
|
-
}: {
|
|
81
|
-
temperature?: number;
|
|
82
|
-
useExisting?: boolean;
|
|
83
|
-
} = {}) {
|
|
73
|
+
static async init({ temperature = 0, useExisting = true }: { temperature?: number; useExisting?: boolean } = {}) {
|
|
84
74
|
if (useExisting) {
|
|
85
75
|
const llmConfig = await AiSession.getLlmConfig();
|
|
86
76
|
if (llmConfig) {
|
|
87
77
|
AiSession.#setChatModel(llmConfig.model, llmConfig.apiKey);
|
|
88
|
-
Logger.rawLog(
|
|
89
|
-
chalk.dim(
|
|
90
|
-
`🤖akan editor uses existing LLM config (${llmConfig.model})`,
|
|
91
|
-
),
|
|
92
|
-
);
|
|
78
|
+
Logger.rawLog(chalk.dim(`🤖akan editor uses existing LLM config (${llmConfig.model})`));
|
|
93
79
|
return AiSession;
|
|
94
80
|
}
|
|
95
|
-
} else
|
|
96
|
-
Logger.rawLog(
|
|
97
|
-
chalk.yellow(
|
|
98
|
-
"🤖akan-editor is not initialized. LLM configuration should be set first.",
|
|
99
|
-
),
|
|
100
|
-
);
|
|
81
|
+
} else Logger.rawLog(chalk.yellow("🤖akan-editor is not initialized. LLM configuration should be set first."));
|
|
101
82
|
|
|
102
83
|
const llmConfig = await AiSession.#requestLlmConfig();
|
|
103
84
|
const { model, apiKey } = llmConfig;
|
|
@@ -136,9 +117,7 @@ export class AiSession {
|
|
|
136
117
|
static async getLlmConfig() {
|
|
137
118
|
return await GlobalConfig.getLlmConfig();
|
|
138
119
|
}
|
|
139
|
-
static async setLlmConfig(
|
|
140
|
-
llmConfig: { model: SupportedLlmModel; apiKey: string } | null,
|
|
141
|
-
) {
|
|
120
|
+
static async setLlmConfig(llmConfig: { model: SupportedLlmModel; apiKey: string } | null) {
|
|
142
121
|
await GlobalConfig.setLlmConfig(llmConfig);
|
|
143
122
|
return AiSession;
|
|
144
123
|
}
|
|
@@ -206,10 +185,7 @@ export class AiSession {
|
|
|
206
185
|
}
|
|
207
186
|
async #saveCache() {
|
|
208
187
|
const cacheFilePath = `${AiSession.#cacheDir}/${this.sessionKey}.json`;
|
|
209
|
-
await this.workspace.writeJson(
|
|
210
|
-
cacheFilePath,
|
|
211
|
-
mapChatMessagesToStoredMessages(this.messageHistory),
|
|
212
|
-
);
|
|
188
|
+
await this.workspace.writeJson(cacheFilePath, mapChatMessagesToStoredMessages(this.messageHistory));
|
|
213
189
|
}
|
|
214
190
|
async ask(
|
|
215
191
|
question: string,
|
|
@@ -225,8 +201,7 @@ export class AiSession {
|
|
|
225
201
|
if (!AiSession.#chat) await AiSession.init();
|
|
226
202
|
if (this.#cacheLoadPromise) await this.#cacheLoadPromise;
|
|
227
203
|
|
|
228
|
-
if (!AiSession.#chat)
|
|
229
|
-
throw new Error("Failed to initialize the AI session");
|
|
204
|
+
if (!AiSession.#chat) throw new Error("Failed to initialize the AI session");
|
|
230
205
|
const loader = new Spinner(`${AiSession.#chat.model} is thinking...`, {
|
|
231
206
|
prefix: `🤖akan-editor`,
|
|
232
207
|
}).start();
|
|
@@ -237,13 +212,10 @@ export class AiSession {
|
|
|
237
212
|
let reasoningResponse = "",
|
|
238
213
|
fullResponse = "";
|
|
239
214
|
for await (const chunk of stream) {
|
|
240
|
-
if (loader.isSpinning())
|
|
241
|
-
loader.succeed(`${AiSession.#chat.model} responded`);
|
|
215
|
+
if (loader.isSpinning()) loader.succeed(`${AiSession.#chat.model} responded`);
|
|
242
216
|
|
|
243
217
|
if (!fullResponse.length) {
|
|
244
|
-
const reasoningContent =
|
|
245
|
-
(chunk.additional_kwargs as { reasoning_content?: string })
|
|
246
|
-
.reasoning_content ?? "";
|
|
218
|
+
const reasoningContent = (chunk.additional_kwargs as { reasoning_content?: string }).reasoning_content ?? "";
|
|
247
219
|
if (reasoningContent.length) {
|
|
248
220
|
reasoningResponse += reasoningContent;
|
|
249
221
|
onReasoning(reasoningContent);
|
|
@@ -271,14 +243,7 @@ export class AiSession {
|
|
|
271
243
|
}
|
|
272
244
|
async edit(
|
|
273
245
|
question: string,
|
|
274
|
-
{
|
|
275
|
-
onChunk,
|
|
276
|
-
onReasoning,
|
|
277
|
-
maxTry = MAX_ASK_TRY,
|
|
278
|
-
validate,
|
|
279
|
-
approve,
|
|
280
|
-
fallbackToPreviousTypescript,
|
|
281
|
-
}: EditOptions = {},
|
|
246
|
+
{ onChunk, onReasoning, maxTry = MAX_ASK_TRY, validate, approve, fallbackToPreviousTypescript }: EditOptions = {},
|
|
282
247
|
) {
|
|
283
248
|
for (let tryCount = 0; tryCount < maxTry; tryCount++) {
|
|
284
249
|
let response = await this.ask(question, { onChunk, onReasoning });
|
|
@@ -292,10 +257,7 @@ ${validate.map((v) => `- ${v}`).join("\n")}`;
|
|
|
292
257
|
response = {
|
|
293
258
|
...validateResponse,
|
|
294
259
|
content: fallbackToPreviousTypescript
|
|
295
|
-
? preserveTypescriptResponseContent(
|
|
296
|
-
response.content,
|
|
297
|
-
validateResponse.content,
|
|
298
|
-
)
|
|
260
|
+
? preserveTypescriptResponseContent(response.content, validateResponse.content)
|
|
299
261
|
: validateResponse.content,
|
|
300
262
|
};
|
|
301
263
|
}
|
|
@@ -333,17 +295,11 @@ ${validate.map((v) => `- ${v}`).join("\n")}`;
|
|
|
333
295
|
// const toolMessages = messages.map(
|
|
334
296
|
// (message) => new ToolMessage({ content: message.content, tool_call_id: message.type })
|
|
335
297
|
// );
|
|
336
|
-
const toolMessages = messages.map(
|
|
337
|
-
(message) => new HumanMessage(message.content),
|
|
338
|
-
);
|
|
298
|
+
const toolMessages = messages.map((message) => new HumanMessage(message.content));
|
|
339
299
|
this.messageHistory.push(...toolMessages);
|
|
340
300
|
return this;
|
|
341
301
|
}
|
|
342
|
-
async writeTypescripts(
|
|
343
|
-
question: string,
|
|
344
|
-
executor: Executor,
|
|
345
|
-
options: EditOptions = {},
|
|
346
|
-
) {
|
|
302
|
+
async writeTypescripts(question: string, executor: Executor, options: EditOptions = {}) {
|
|
347
303
|
const content = await this.edit(question, {
|
|
348
304
|
...options,
|
|
349
305
|
fallbackToPreviousTypescript: true,
|
|
@@ -353,15 +309,10 @@ ${validate.map((v) => `- ${v}`).join("\n")}`;
|
|
|
353
309
|
throw new Error(
|
|
354
310
|
"No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.",
|
|
355
311
|
);
|
|
356
|
-
for (const write of writes)
|
|
357
|
-
await executor.writeFile(write.filePath, write.content);
|
|
312
|
+
for (const write of writes) await executor.writeFile(write.filePath, write.content);
|
|
358
313
|
return await this.#tryFixTypescripts(writes, executor, options);
|
|
359
314
|
}
|
|
360
|
-
async #editTypescripts(
|
|
361
|
-
question: string,
|
|
362
|
-
options: EditOptions = {},
|
|
363
|
-
fallbackWrites?: FileContent[],
|
|
364
|
-
) {
|
|
315
|
+
async #editTypescripts(question: string, options: EditOptions = {}, fallbackWrites?: FileContent[]) {
|
|
365
316
|
const content = await this.edit(question, {
|
|
366
317
|
...options,
|
|
367
318
|
fallbackToPreviousTypescript: true,
|
|
@@ -374,11 +325,7 @@ ${validate.map((v) => `- ${v}`).join("\n")}`;
|
|
|
374
325
|
);
|
|
375
326
|
return writes;
|
|
376
327
|
}
|
|
377
|
-
async #tryFixTypescripts(
|
|
378
|
-
writes: FileContent[],
|
|
379
|
-
executor: Executor,
|
|
380
|
-
options: EditOptions = {},
|
|
381
|
-
) {
|
|
328
|
+
async #tryFixTypescripts(writes: FileContent[], executor: Executor, options: EditOptions = {}) {
|
|
382
329
|
const MAX_EDIT_TRY = 5;
|
|
383
330
|
for (let tryCount = 0; tryCount < MAX_EDIT_TRY; tryCount++) {
|
|
384
331
|
const loader = new Spinner(`Type checking and linting...`, {
|
|
@@ -396,9 +343,7 @@ ${validate.map((v) => `- ${v}`).join("\n")}`;
|
|
|
396
343
|
);
|
|
397
344
|
const hasAnyFix = fileChecks.some((fileCheck) => fileCheck.needFix);
|
|
398
345
|
if (hasAnyFix) {
|
|
399
|
-
loader.fail(
|
|
400
|
-
"Type checking and linting has some errors, try to fix them",
|
|
401
|
-
);
|
|
346
|
+
loader.fail("Type checking and linting has some errors, try to fix them");
|
|
402
347
|
fileChecks.forEach((fileCheck) => {
|
|
403
348
|
Logger.rawLog(
|
|
404
349
|
`TypeCheck Result \n${fileCheck.typeCheckResult.message}\nLint Result \n${fileCheck.lintResult.message}`,
|
|
@@ -417,8 +362,7 @@ ${validate.map((v) => `- ${v}`).join("\n")}`;
|
|
|
417
362
|
},
|
|
418
363
|
writes,
|
|
419
364
|
);
|
|
420
|
-
for (const write of writes)
|
|
421
|
-
await executor.writeFile(write.filePath, write.content);
|
|
365
|
+
for (const write of writes) await executor.writeFile(write.filePath, write.content);
|
|
422
366
|
} else {
|
|
423
367
|
loader.succeed("Type checking and linting has no errors");
|
|
424
368
|
return writes;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/devkit",
|
|
3
|
-
"version": "2.2.2
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@langchain/openai": "^1.4.6",
|
|
33
33
|
"@tailwindcss/node": "^4.3.0",
|
|
34
34
|
"@trapezedev/project": "^7.1.4",
|
|
35
|
-
"akanjs": "2.2.2
|
|
35
|
+
"akanjs": "2.2.2",
|
|
36
36
|
"chalk": "^5.6.2",
|
|
37
37
|
"commander": "^14.0.3",
|
|
38
38
|
"daisyui": "^5.5.20",
|