micro-models-agent 0.20.1 → 0.21.0
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/main.js +81 -302
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2303,6 +2303,8 @@ Command: {command}`,
|
|
|
2303
2303
|
"tool.friendly.process_list": "Listing background processes",
|
|
2304
2304
|
"tool.friendly.process_log": "Process output",
|
|
2305
2305
|
"tool.friendly.process_kill": "Stopping process",
|
|
2306
|
+
"plan.no_steps": "No plan steps specified. Provide concrete steps with files and commands.",
|
|
2307
|
+
"plan.bad_steps": "Plan step quality unacceptable. Each step must describe concrete deliverables (files, packages, commands). Minimum 20 chars per step. Errors: {errors}",
|
|
2306
2308
|
"plan.created": "Plan created: {title} ({steps} steps)",
|
|
2307
2309
|
"plan.step_done": "Step {n}/{total}: {description} ✓",
|
|
2308
2310
|
"plan.complete": "Task complete: {summary}",
|
|
@@ -2800,6 +2802,8 @@ var init_ru = __esm(() => {
|
|
|
2800
2802
|
"tool.friendly.process_list": "Список фоновых процессов",
|
|
2801
2803
|
"tool.friendly.process_log": "Вывод процесса",
|
|
2802
2804
|
"tool.friendly.process_kill": "Остановка процесса",
|
|
2805
|
+
"plan.no_steps": "Не указаны шаги плана. Укажите конкретные шаги с файлами и командами.",
|
|
2806
|
+
"plan.bad_steps": "Качество шагов плана неприемлемо. Каждый шаг должен описывать конкретные deliverables (файлы, пакеты, команды). Минимум 20 символов на шаг. Ошибки: {errors}",
|
|
2803
2807
|
"plan.created": "План создан: {title} ({steps} шагов)",
|
|
2804
2808
|
"plan.step_done": "Шаг {n}/{total}: {description} ✓",
|
|
2805
2809
|
"plan.complete": "Задача выполнена: {summary}",
|
|
@@ -10115,285 +10119,6 @@ var init_web_browse = __esm(() => {
|
|
|
10115
10119
|
};
|
|
10116
10120
|
});
|
|
10117
10121
|
|
|
10118
|
-
// src/tools/user-input.ts
|
|
10119
|
-
import * as readline from "readline";
|
|
10120
|
-
function parseSelection(input, optionCount, multiple) {
|
|
10121
|
-
const trimmed = input.trim();
|
|
10122
|
-
if (!trimmed)
|
|
10123
|
-
return null;
|
|
10124
|
-
const parts = trimmed.split(",").map((p) => p.trim());
|
|
10125
|
-
if (!multiple && parts.length > 1)
|
|
10126
|
-
return null;
|
|
10127
|
-
const indexes = [];
|
|
10128
|
-
for (const part of parts) {
|
|
10129
|
-
if (!/^\d+$/.test(part))
|
|
10130
|
-
return null;
|
|
10131
|
-
const idx = Number(part) - 1;
|
|
10132
|
-
if (idx < 0 || idx >= optionCount)
|
|
10133
|
-
return null;
|
|
10134
|
-
if (indexes.includes(idx))
|
|
10135
|
-
return null;
|
|
10136
|
-
indexes.push(idx);
|
|
10137
|
-
}
|
|
10138
|
-
return indexes.length ? indexes : null;
|
|
10139
|
-
}
|
|
10140
|
-
function formatMenu(question, options, opts = {}) {
|
|
10141
|
-
const lines = [question];
|
|
10142
|
-
options.forEach((opt, i) => {
|
|
10143
|
-
lines.push(` [${i + 1}] ${opt.label} — ${opt.description}`);
|
|
10144
|
-
});
|
|
10145
|
-
if (opts.allowCustom) {
|
|
10146
|
-
lines.push(` [${options.length + 1}] ${t("tool.user_input.custom_option")}`);
|
|
10147
|
-
}
|
|
10148
|
-
return lines.join(`
|
|
10149
|
-
`);
|
|
10150
|
-
}
|
|
10151
|
-
function choicePrompt(optionCount, multiple) {
|
|
10152
|
-
return multiple ? t("tool.user_input.choice_multiple") : t("tool.user_input.choice_single", { max: optionCount });
|
|
10153
|
-
}
|
|
10154
|
-
function createRl() {
|
|
10155
|
-
return readline.createInterface({
|
|
10156
|
-
input: process.stdin,
|
|
10157
|
-
output: process.stdout
|
|
10158
|
-
});
|
|
10159
|
-
}
|
|
10160
|
-
function promptLine(rl, prompt) {
|
|
10161
|
-
return new Promise((resolve11) => {
|
|
10162
|
-
rl.question(prompt, (answer) => resolve11(answer));
|
|
10163
|
-
});
|
|
10164
|
-
}
|
|
10165
|
-
async function askText(question) {
|
|
10166
|
-
const rl = createRl();
|
|
10167
|
-
try {
|
|
10168
|
-
const answer = await promptLine(rl, `${question} `);
|
|
10169
|
-
return answer.trim();
|
|
10170
|
-
} finally {
|
|
10171
|
-
rl.close();
|
|
10172
|
-
}
|
|
10173
|
-
}
|
|
10174
|
-
async function askChoice(question, options, opts = {}) {
|
|
10175
|
-
const multiple = opts.multiple ?? false;
|
|
10176
|
-
const entryCount = options.length + (opts.allowCustom ? 1 : 0);
|
|
10177
|
-
const customEntry = options.length;
|
|
10178
|
-
const rl = createRl();
|
|
10179
|
-
try {
|
|
10180
|
-
console.log(formatMenu(question, options, opts));
|
|
10181
|
-
for (;; ) {
|
|
10182
|
-
const answer = await promptLine(rl, choicePrompt(entryCount, multiple));
|
|
10183
|
-
const parsed = parseSelection(answer, entryCount, multiple);
|
|
10184
|
-
if (parsed) {
|
|
10185
|
-
return parsed.map((i) => opts.allowCustom && i === customEntry ? CUSTOM_INDEX : i);
|
|
10186
|
-
}
|
|
10187
|
-
console.log(t("tool.user_input.invalid"));
|
|
10188
|
-
}
|
|
10189
|
-
} finally {
|
|
10190
|
-
rl.close();
|
|
10191
|
-
}
|
|
10192
|
-
}
|
|
10193
|
-
async function askUser(question, opts = {}) {
|
|
10194
|
-
const header = [opts.progress, opts.header].filter(Boolean).join(" — ");
|
|
10195
|
-
const text = header ? `${header}
|
|
10196
|
-
${question}` : question;
|
|
10197
|
-
if (!opts.options || opts.options.length === 0) {
|
|
10198
|
-
const answer = await askText(text);
|
|
10199
|
-
return answer ? [answer] : [];
|
|
10200
|
-
}
|
|
10201
|
-
const allowCustom = opts.custom ?? true;
|
|
10202
|
-
const indexes = await askChoice(text, opts.options, {
|
|
10203
|
-
multiple: opts.multiple,
|
|
10204
|
-
allowCustom
|
|
10205
|
-
});
|
|
10206
|
-
const labels = [];
|
|
10207
|
-
for (const idx of indexes) {
|
|
10208
|
-
if (idx === CUSTOM_INDEX) {
|
|
10209
|
-
const custom = await askText(t("tool.user_input.custom_prompt"));
|
|
10210
|
-
if (custom)
|
|
10211
|
-
labels.push(custom);
|
|
10212
|
-
} else {
|
|
10213
|
-
labels.push(opts.options[idx].label);
|
|
10214
|
-
}
|
|
10215
|
-
}
|
|
10216
|
-
return labels;
|
|
10217
|
-
}
|
|
10218
|
-
var CUSTOM_INDEX = -1;
|
|
10219
|
-
var init_user_input = __esm(() => {
|
|
10220
|
-
init_i18n();
|
|
10221
|
-
});
|
|
10222
|
-
|
|
10223
|
-
// src/tools/question.ts
|
|
10224
|
-
function isOption(val) {
|
|
10225
|
-
if (!val || typeof val !== "object")
|
|
10226
|
-
return false;
|
|
10227
|
-
const opt = val;
|
|
10228
|
-
return typeof opt.label === "string" && typeof opt.description === "string";
|
|
10229
|
-
}
|
|
10230
|
-
function normalizeQuestions(args) {
|
|
10231
|
-
const raw = args.questions;
|
|
10232
|
-
if (Array.isArray(raw)) {
|
|
10233
|
-
const specs = [];
|
|
10234
|
-
for (const item of raw) {
|
|
10235
|
-
if (!item || typeof item !== "object")
|
|
10236
|
-
continue;
|
|
10237
|
-
const q = item;
|
|
10238
|
-
if (typeof q.question !== "string" || !q.question.trim())
|
|
10239
|
-
continue;
|
|
10240
|
-
const options = Array.isArray(q.options) ? q.options.filter(isOption) : undefined;
|
|
10241
|
-
if (Array.isArray(q.options) && options.length === 0)
|
|
10242
|
-
continue;
|
|
10243
|
-
specs.push({
|
|
10244
|
-
question: q.question,
|
|
10245
|
-
header: typeof q.header === "string" ? q.header : undefined,
|
|
10246
|
-
options,
|
|
10247
|
-
multiple: q.multiple === true,
|
|
10248
|
-
custom: q.custom === false ? false : undefined
|
|
10249
|
-
});
|
|
10250
|
-
}
|
|
10251
|
-
return specs;
|
|
10252
|
-
}
|
|
10253
|
-
if (typeof args.question === "string" && args.question.trim()) {
|
|
10254
|
-
return [{ question: args.question }];
|
|
10255
|
-
}
|
|
10256
|
-
return [];
|
|
10257
|
-
}
|
|
10258
|
-
var DESCRIPTION = `Use this tool when you need to ask the user questions during execution. This allows you to:
|
|
10259
|
-
1. Gather user preferences or requirements
|
|
10260
|
-
2. Clarify ambiguous instructions
|
|
10261
|
-
3. Get decisions on implementation choices as you work
|
|
10262
|
-
4. Offer choices to the user about what direction to take.
|
|
10263
|
-
|
|
10264
|
-
Usage notes:
|
|
10265
|
-
- When "custom" is enabled (default), a "Type your own answer" option is added automatically; don't include "Other" or catch-all options
|
|
10266
|
-
- Answers are returned as arrays of labels; set "multiple": true to allow selecting more than one
|
|
10267
|
-
- If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
|
|
10268
|
-
- Execution blocks until the user answers every question
|
|
10269
|
-
- Omit "options" for a free-text question`, questionTool;
|
|
10270
|
-
var init_question = __esm(() => {
|
|
10271
|
-
init_i18n();
|
|
10272
|
-
init_user_input();
|
|
10273
|
-
questionTool = {
|
|
10274
|
-
name: "question",
|
|
10275
|
-
description: DESCRIPTION,
|
|
10276
|
-
tags: ["core"],
|
|
10277
|
-
interactive: true,
|
|
10278
|
-
parameters: {
|
|
10279
|
-
type: "object",
|
|
10280
|
-
properties: {
|
|
10281
|
-
questions: {
|
|
10282
|
-
type: "array",
|
|
10283
|
-
description: "Questions to ask",
|
|
10284
|
-
items: {
|
|
10285
|
-
type: "object",
|
|
10286
|
-
properties: {
|
|
10287
|
-
question: { type: "string", description: "Complete question" },
|
|
10288
|
-
header: {
|
|
10289
|
-
type: "string",
|
|
10290
|
-
description: "Very short label (max 30 chars)"
|
|
10291
|
-
},
|
|
10292
|
-
options: {
|
|
10293
|
-
type: "array",
|
|
10294
|
-
description: "Available choices; omit for a free-text question",
|
|
10295
|
-
items: {
|
|
10296
|
-
type: "object",
|
|
10297
|
-
properties: {
|
|
10298
|
-
label: {
|
|
10299
|
-
type: "string",
|
|
10300
|
-
description: "Display text (1-5 words, concise)"
|
|
10301
|
-
},
|
|
10302
|
-
description: {
|
|
10303
|
-
type: "string",
|
|
10304
|
-
description: "Explanation of choice"
|
|
10305
|
-
}
|
|
10306
|
-
},
|
|
10307
|
-
required: ["label", "description"]
|
|
10308
|
-
}
|
|
10309
|
-
},
|
|
10310
|
-
multiple: {
|
|
10311
|
-
type: "boolean",
|
|
10312
|
-
description: "Allow selecting multiple choices"
|
|
10313
|
-
},
|
|
10314
|
-
custom: {
|
|
10315
|
-
type: "boolean",
|
|
10316
|
-
description: "Allow typing a custom answer (default: true)"
|
|
10317
|
-
}
|
|
10318
|
-
},
|
|
10319
|
-
required: ["question"]
|
|
10320
|
-
}
|
|
10321
|
-
},
|
|
10322
|
-
question: {
|
|
10323
|
-
type: "string",
|
|
10324
|
-
description: "Legacy single free-text question"
|
|
10325
|
-
}
|
|
10326
|
-
}
|
|
10327
|
-
},
|
|
10328
|
-
handler: async (ctx, args) => {
|
|
10329
|
-
if (ctx.exitOnComplete) {
|
|
10330
|
-
return { success: false, output: t("tool.interactive_disabled") };
|
|
10331
|
-
}
|
|
10332
|
-
const questions = normalizeQuestions(args);
|
|
10333
|
-
if (questions.length === 0) {
|
|
10334
|
-
return { success: false, output: t("tool.question.no_questions") };
|
|
10335
|
-
}
|
|
10336
|
-
const answers = [];
|
|
10337
|
-
for (let i = 0;i < questions.length; i++) {
|
|
10338
|
-
const q = questions[i];
|
|
10339
|
-
const progress = questions.length > 1 ? t("tool.question.progress", {
|
|
10340
|
-
current: i + 1,
|
|
10341
|
-
total: questions.length
|
|
10342
|
-
}) : undefined;
|
|
10343
|
-
answers.push(await askUser(q.question, {
|
|
10344
|
-
header: q.header,
|
|
10345
|
-
options: q.options,
|
|
10346
|
-
multiple: q.multiple,
|
|
10347
|
-
custom: q.custom,
|
|
10348
|
-
progress
|
|
10349
|
-
}));
|
|
10350
|
-
}
|
|
10351
|
-
const formatted = questions.map((q, i) => `"${q.question}"="${answers[i].length ? answers[i].join(", ") : t("tool.question.unanswered")}"`).join(", ");
|
|
10352
|
-
return {
|
|
10353
|
-
success: true,
|
|
10354
|
-
output: t("tool.question.answered", { formatted })
|
|
10355
|
-
};
|
|
10356
|
-
}
|
|
10357
|
-
};
|
|
10358
|
-
});
|
|
10359
|
-
|
|
10360
|
-
// src/tools/approve.ts
|
|
10361
|
-
var approveTool;
|
|
10362
|
-
var init_approve = __esm(() => {
|
|
10363
|
-
init_i18n();
|
|
10364
|
-
init_user_input();
|
|
10365
|
-
approveTool = {
|
|
10366
|
-
name: "approve",
|
|
10367
|
-
description: "Request user approval for an action. The user picks Yes or No from a menu.",
|
|
10368
|
-
tags: ["core"],
|
|
10369
|
-
interactive: true,
|
|
10370
|
-
parameters: {
|
|
10371
|
-
type: "object",
|
|
10372
|
-
properties: {
|
|
10373
|
-
action: {
|
|
10374
|
-
type: "string",
|
|
10375
|
-
description: "Description of the action requiring approval"
|
|
10376
|
-
}
|
|
10377
|
-
},
|
|
10378
|
-
required: ["action"]
|
|
10379
|
-
},
|
|
10380
|
-
handler: async (ctx, args) => {
|
|
10381
|
-
if (ctx.exitOnComplete) {
|
|
10382
|
-
return { success: false, output: t("tool.interactive_disabled") };
|
|
10383
|
-
}
|
|
10384
|
-
const action = String(args.action || "");
|
|
10385
|
-
const indexes = await askChoice(t("tool.approve_prompt", { action }), [
|
|
10386
|
-
{ label: t("tool.approve_yes"), description: t("tool.approve_yes_desc") },
|
|
10387
|
-
{ label: t("tool.approve_no"), description: t("tool.approve_no_desc") }
|
|
10388
|
-
]);
|
|
10389
|
-
if (indexes[0] === 0) {
|
|
10390
|
-
return { success: true, output: t("tool.approved") };
|
|
10391
|
-
}
|
|
10392
|
-
return { success: false, output: t("tool.rejected") };
|
|
10393
|
-
}
|
|
10394
|
-
};
|
|
10395
|
-
});
|
|
10396
|
-
|
|
10397
10122
|
// src/tools/load-skill.ts
|
|
10398
10123
|
function createLoadSkillTool(skillsModule) {
|
|
10399
10124
|
return {
|
|
@@ -12394,8 +12119,6 @@ function registerAllTools(registry2, skillsModule) {
|
|
|
12394
12119
|
webSearchTool,
|
|
12395
12120
|
webFetchTool,
|
|
12396
12121
|
webBrowseTool,
|
|
12397
|
-
questionTool,
|
|
12398
|
-
approveTool,
|
|
12399
12122
|
pipelineRunTool,
|
|
12400
12123
|
mcpCallTool,
|
|
12401
12124
|
searchHistoryTool,
|
|
@@ -12428,8 +12151,6 @@ var init_tools = __esm(() => {
|
|
|
12428
12151
|
init_web_search();
|
|
12429
12152
|
init_web_fetch();
|
|
12430
12153
|
init_web_browse();
|
|
12431
|
-
init_question();
|
|
12432
|
-
init_approve();
|
|
12433
12154
|
init_load_skill();
|
|
12434
12155
|
init_pipeline_run();
|
|
12435
12156
|
init_mcp_call();
|
|
@@ -12664,29 +12385,67 @@ var init_notify = __esm(() => {
|
|
|
12664
12385
|
});
|
|
12665
12386
|
|
|
12666
12387
|
// src/modules/execution/planner.ts
|
|
12388
|
+
function generatePlanId() {
|
|
12389
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
12390
|
+
let id = "";
|
|
12391
|
+
for (let i = 0;i < 6; i++) {
|
|
12392
|
+
id += chars[Math.floor(Math.random() * chars.length)];
|
|
12393
|
+
}
|
|
12394
|
+
return `plan_${id}`;
|
|
12395
|
+
}
|
|
12396
|
+
|
|
12667
12397
|
class PlanCreator {
|
|
12668
12398
|
static isMultiStep(task) {
|
|
12669
12399
|
const fileCount = (task.match(/\b[\w./-]+\.[a-z]+\b/gi) || []).length;
|
|
12670
12400
|
if (fileCount > 1)
|
|
12671
12401
|
return true;
|
|
12672
|
-
const actionWords = [
|
|
12402
|
+
const actionWords = [
|
|
12403
|
+
"implement",
|
|
12404
|
+
"create",
|
|
12405
|
+
"add",
|
|
12406
|
+
"build",
|
|
12407
|
+
"setup",
|
|
12408
|
+
"configure",
|
|
12409
|
+
"write",
|
|
12410
|
+
"make",
|
|
12411
|
+
"develop"
|
|
12412
|
+
];
|
|
12673
12413
|
const words = task.split(/\s+/);
|
|
12674
12414
|
const hasActionWord = actionWords.some((w) => task.toLowerCase().includes(w));
|
|
12675
12415
|
return hasActionWord && words.length > 8;
|
|
12676
12416
|
}
|
|
12677
|
-
static createPlan(title, stepDescriptions) {
|
|
12417
|
+
static createPlan(title, stepDescriptions, baseDir) {
|
|
12418
|
+
const stepCount = stepDescriptions.length;
|
|
12678
12419
|
return {
|
|
12679
|
-
|
|
12420
|
+
id: generatePlanId(),
|
|
12421
|
+
title: `[${stepCount} ст.] ${title}`,
|
|
12680
12422
|
steps: stepDescriptions.map((desc, i) => ({
|
|
12681
12423
|
id: i + 1,
|
|
12682
12424
|
description: desc,
|
|
12683
12425
|
status: "pending"
|
|
12684
12426
|
})),
|
|
12685
|
-
createdAt: new Date().toISOString()
|
|
12427
|
+
createdAt: new Date().toISOString(),
|
|
12428
|
+
baseDir
|
|
12686
12429
|
};
|
|
12687
12430
|
}
|
|
12431
|
+
static validateSteps(steps) {
|
|
12432
|
+
const errors = [];
|
|
12433
|
+
for (let i = 0;i < steps.length; i++) {
|
|
12434
|
+
const s = steps[i].trim();
|
|
12435
|
+
if (s.length < MIN_STEP_DESC_LENGTH) {
|
|
12436
|
+
errors.push(`Step ${i + 1} too short (${s.length} chars). Describe WHAT files to create, WHICH packages to install, WHICH commands to run. Minimum ${MIN_STEP_DESC_LENGTH} chars.`);
|
|
12437
|
+
}
|
|
12438
|
+
}
|
|
12439
|
+
return { valid: errors.length === 0, errors };
|
|
12440
|
+
}
|
|
12688
12441
|
static toPromptBlock(plan, currentStepIndex) {
|
|
12689
|
-
const
|
|
12442
|
+
const date = plan.createdAt.slice(0, 10);
|
|
12443
|
+
const lines = [
|
|
12444
|
+
`[${plan.id}] ${plan.title}`,
|
|
12445
|
+
`Dir: ${plan.baseDir}`,
|
|
12446
|
+
`Created: ${date} | Progress: ${plan.steps.filter((s) => s.status === "done").length}/${plan.steps.length} done, current: step ${currentStepIndex + 1}`,
|
|
12447
|
+
``
|
|
12448
|
+
];
|
|
12690
12449
|
for (const step of plan.steps) {
|
|
12691
12450
|
const icon = step.status === "done" ? "[x]" : step.status === "in_progress" ? "[*]" : step.status === "failed" ? "[!]" : step.status === "skipped" ? "[-]" : "[ ]";
|
|
12692
12451
|
const note = step.note ? ` — ${step.note}` : "";
|
|
@@ -12696,6 +12455,7 @@ class PlanCreator {
|
|
|
12696
12455
|
`);
|
|
12697
12456
|
}
|
|
12698
12457
|
}
|
|
12458
|
+
var MIN_STEP_DESC_LENGTH = 20;
|
|
12699
12459
|
|
|
12700
12460
|
// src/modules/execution/tracker.ts
|
|
12701
12461
|
class PlanTracker {
|
|
@@ -12744,7 +12504,7 @@ class PlanTracker {
|
|
|
12744
12504
|
const barWidth = 10;
|
|
12745
12505
|
const filled = Math.round(done / total * barWidth);
|
|
12746
12506
|
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
12747
|
-
return `[
|
|
12507
|
+
return `[${this.plan.id}] ${this.plan.title} ${done}/${total} ${bar} ${pct}%`;
|
|
12748
12508
|
}
|
|
12749
12509
|
toPromptBlock() {
|
|
12750
12510
|
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
@@ -12993,10 +12753,12 @@ class PlanPersister {
|
|
|
12993
12753
|
}
|
|
12994
12754
|
save(plan) {
|
|
12995
12755
|
const file = {
|
|
12756
|
+
id: plan.id,
|
|
12996
12757
|
title: plan.title,
|
|
12997
12758
|
steps: plan.steps,
|
|
12998
12759
|
createdAt: plan.createdAt,
|
|
12999
|
-
updatedAt: new Date().toISOString()
|
|
12760
|
+
updatedAt: new Date().toISOString(),
|
|
12761
|
+
baseDir: plan.baseDir
|
|
13000
12762
|
};
|
|
13001
12763
|
writeFileSync9(this.filePath, JSON.stringify(file, null, 2), "utf-8");
|
|
13002
12764
|
}
|
|
@@ -13007,9 +12769,11 @@ class PlanPersister {
|
|
|
13007
12769
|
const raw = readFileSync14(this.filePath, "utf-8");
|
|
13008
12770
|
const file = JSON.parse(raw);
|
|
13009
12771
|
return {
|
|
12772
|
+
id: file.id || "plan_legacy",
|
|
13010
12773
|
title: file.title,
|
|
13011
12774
|
steps: file.steps,
|
|
13012
|
-
createdAt: file.createdAt
|
|
12775
|
+
createdAt: file.createdAt,
|
|
12776
|
+
baseDir: file.baseDir || process.cwd()
|
|
13013
12777
|
};
|
|
13014
12778
|
} catch {
|
|
13015
12779
|
return null;
|
|
@@ -13097,7 +12861,15 @@ class ExecutionModule {
|
|
|
13097
12861
|
return [
|
|
13098
12862
|
{
|
|
13099
12863
|
name: "plan",
|
|
13100
|
-
description:
|
|
12864
|
+
description: `Create, update, show, or abort a multi-step plan. Use "create" at the start of complex tasks. Use "update" after completing each step to track progress. Use "show" to re-print the current plan checklist.
|
|
12865
|
+
|
|
12866
|
+
IMPORTANT — each step MUST be detailed and concrete:
|
|
12867
|
+
- Specify WHICH files to create (filenames with paths, e.g. "create src/components/Header.tsx...")
|
|
12868
|
+
- Specify WHICH packages to install (e.g. "run npm install react react-dom")
|
|
12869
|
+
- Specify WHICH commands to run with exact arguments
|
|
12870
|
+
- Minimum 20 characters per step description
|
|
12871
|
+
- BAD: "Настройка проекта"
|
|
12872
|
+
- GOOD: "Создать package.json с зависимостями react@18, vite@5, установить их через npm install"`,
|
|
13101
12873
|
parameters: {
|
|
13102
12874
|
type: "object",
|
|
13103
12875
|
properties: {
|
|
@@ -13121,7 +12893,16 @@ class ExecutionModule {
|
|
|
13121
12893
|
if (steps.length === 0) {
|
|
13122
12894
|
return { success: false, output: t("plan.no_steps") };
|
|
13123
12895
|
}
|
|
13124
|
-
const
|
|
12896
|
+
const validation = PlanCreator.validateSteps(steps);
|
|
12897
|
+
if (!validation.valid) {
|
|
12898
|
+
return {
|
|
12899
|
+
success: false,
|
|
12900
|
+
output: t("plan.bad_steps", {
|
|
12901
|
+
errors: validation.errors.join("; ")
|
|
12902
|
+
})
|
|
12903
|
+
};
|
|
12904
|
+
}
|
|
12905
|
+
const plan = PlanCreator.createPlan(title, steps, this.baseDir);
|
|
13125
12906
|
this.setPlan(plan);
|
|
13126
12907
|
const display = PlanCreator.toPromptBlock(plan, 0);
|
|
13127
12908
|
return {
|
|
@@ -13336,9 +13117,7 @@ Sub-tasks: ${note}`
|
|
|
13336
13117
|
"glob",
|
|
13337
13118
|
"grep",
|
|
13338
13119
|
"file_info",
|
|
13339
|
-
"load_skill"
|
|
13340
|
-
"question",
|
|
13341
|
-
"approve"
|
|
13120
|
+
"load_skill"
|
|
13342
13121
|
];
|
|
13343
13122
|
if (allowedAlways.includes(call.name))
|
|
13344
13123
|
return null;
|
|
@@ -14868,7 +14647,7 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
14868
14647
|
lines.push(``, `Windows environment — use Windows-compatible commands:`, `- Use "dir" instead of "ls". Use "dir /b" for bare listing.`, `- Use "type" or "Get-Content" instead of "cat".`, `- Use "cd" instead of "pwd". Use "echo %cd%" to print working directory.`, `- Use "copy" instead of "cp", "move" instead of "mv", "del" instead of "rm".`, `- Do not use "mkdir -p" — Windows mkdir creates intermediate dirs by default. Use the create_dir tool instead.`, `- Use forward slashes (/) or escaped backslashes (\\\\) in file paths.`, `- Your working directory is: ${baseDir}`);
|
|
14869
14648
|
}
|
|
14870
14649
|
lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory. Do NOT chain "cd dir && cmd" — the security module blocks the "&&" operator.`, `- Run one command per tool call. Split multi-step shell operations into separate bash calls.`, `- Background processes (dev servers, watchers, long-running npm install): use the "background: true" parameter or the tool will auto-detect and background them. Check output with process_log.`);
|
|
14871
|
-
lines.push(``, `=== DEVELOPMENT RULES — follow these strictly ===`, ``, `1. DEPENDENCIES FIRST: Before writing any source code, ALWAYS install project dependencies (e.g., "npm install", "pip install -r requirements.txt", "cargo build", "go mod tidy"). Verify the package manager's lock file or dependency directory exists. Never write code that imports/uses packages that aren't installed yet.`, `2. TOOLKIT/FWK FIRST: If the task specifies a framework or UI library, initialize and configure it BEFORE writing application code. Run its project init command first, then add components/modules. Never write your own version of what the framework already provides.`, `3. ONE STEP AT A TIME: Follow the plan sequentially. Complete step N before starting step N+1. When a step is done: verify the deliverables exist and have real content (not empty), then call "plan update step=N status=done". Do not redo completed work.`, `4. VERIFY YOUR WORK: After creating/modifying files, verify they exist on disk. After installing dependencies, verify the package manager completed successfully. After any command, check its output for errors. Don't assume operations succeeded.`, `5. NO PREMATURE WORK: Do not create files for future steps. Do not add imports/references to packages or modules that haven't been installed yet. Do not reference files or components that don't exist yet. Build incrementally — one layer at a time.`, `6. WHEN STUCK: If a command fails 2+ times, STOP and try a different approach. Write files directly instead of using commands. Ask the user for help. Never repeat the same failing command more than twice.`);
|
|
14650
|
+
lines.push(``, `=== DEVELOPMENT RULES — follow these strictly ===`, ``, `1. DEPENDENCIES FIRST: Before writing any source code, ALWAYS install project dependencies (e.g., "npm install", "pip install -r requirements.txt", "cargo build", "go mod tidy"). Verify the package manager's lock file or dependency directory exists. Never write code that imports/uses packages that aren't installed yet.`, `2. TOOLKIT/FWK FIRST: If the task specifies a framework or UI library, initialize and configure it BEFORE writing application code. Run its project init command first, then add components/modules. Never write your own version of what the framework already provides.`, `3. ONE STEP AT A TIME: Follow the plan sequentially. Complete step N before starting step N+1. When a step is done: verify the deliverables exist and have real content (not empty), then call "plan update step=N status=done". Do not redo completed work.`, `4. VERIFY YOUR WORK: After creating/modifying files, verify they exist on disk. After installing dependencies, verify the package manager completed successfully. After any command, check its output for errors. Don't assume operations succeeded.`, `5. NO PREMATURE WORK: Do not create files for future steps. Do not add imports/references to packages or modules that haven't been installed yet. Do not reference files or components that don't exist yet. Build incrementally — one layer at a time.`, `6. WHEN STUCK: If a command fails 2+ times, STOP and try a different approach. Write files directly instead of using commands. Ask the user for help. Never repeat the same failing command more than twice.`, ``, `=== PLAN QUALITY RULES — your plan MUST follow these ===`, ``, `- Each step must describe CONCRETE deliverables: exact filenames with paths, exact packages to install, exact CLI commands to run.`, `- A step like "Настройка проекта" or "Setup the project" is USELESS and will be REJECTED.`, `- A step like "Создать src/components/Header.tsx с хедером навигации, подключить в App.tsx" is GOOD.`, `- Include file extensions (.tsx, .css, .json) and directory paths. Never write vague steps.`, `- The plan must cover EVERYTHING needed: init → deps → framework setup → code → verification.`, `- Number of steps: 5-8 for a typical task. Too few means you're being vague. Too many means you're over-splitting.`);
|
|
14872
14651
|
if (config.autoPlan) {
|
|
14873
14652
|
lines.push(``, `Plan rule (MANDATORY): For ANY task that requires creating files, installing packages, or multiple actions — you MUST create a plan using the "plan" tool BEFORE starting work. Each step must describe a concrete deliverable (specific files to create, packages to install, commands to run). Do not combine unrelated work into one step.`);
|
|
14874
14653
|
}
|
|
@@ -15629,7 +15408,7 @@ init_config();
|
|
|
15629
15408
|
init_colors();
|
|
15630
15409
|
init_i18n();
|
|
15631
15410
|
init_spinner();
|
|
15632
|
-
import * as
|
|
15411
|
+
import * as readline from "readline";
|
|
15633
15412
|
|
|
15634
15413
|
// src/ui/box.ts
|
|
15635
15414
|
init_string_width();
|
|
@@ -15809,7 +15588,7 @@ async function testChat(apiBase, apiKey, model) {
|
|
|
15809
15588
|
}
|
|
15810
15589
|
async function runSetup() {
|
|
15811
15590
|
console.log(t("setup.title"));
|
|
15812
|
-
const rl =
|
|
15591
|
+
const rl = readline.createInterface({
|
|
15813
15592
|
input: process.stdin,
|
|
15814
15593
|
output: process.stdout
|
|
15815
15594
|
});
|
|
@@ -16676,7 +16455,7 @@ init_bootstrap();
|
|
|
16676
16455
|
|
|
16677
16456
|
// src/cli/repl.ts
|
|
16678
16457
|
init_colors();
|
|
16679
|
-
import * as
|
|
16458
|
+
import * as readline2 from "readline";
|
|
16680
16459
|
import { existsSync as existsSync35, readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
|
|
16681
16460
|
import { join as join29, dirname as dirname10 } from "path";
|
|
16682
16461
|
import { homedir as homedir14 } from "os";
|
|
@@ -17207,7 +16986,7 @@ class Repl {
|
|
|
17207
16986
|
this.registerSessionCommands();
|
|
17208
16987
|
this.registerSkillCommands();
|
|
17209
16988
|
this.setupCompleter();
|
|
17210
|
-
this.rl =
|
|
16989
|
+
this.rl = readline2.createInterface({
|
|
17211
16990
|
input: process.stdin,
|
|
17212
16991
|
output: process.stdout,
|
|
17213
16992
|
prompt: pc.cyan("> "),
|
|
@@ -17844,7 +17623,7 @@ class Repl {
|
|
|
17844
17623
|
this.running = false;
|
|
17845
17624
|
});
|
|
17846
17625
|
if (process.stdin.isTTY) {
|
|
17847
|
-
|
|
17626
|
+
readline2.emitKeypressEvents(process.stdin);
|
|
17848
17627
|
process.stdin.on("keypress", async (str, key) => {
|
|
17849
17628
|
if (key.name === "escape") {
|
|
17850
17629
|
const now = Date.now();
|