markov-cli 1.0.4 → 1.0.5
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/package.json +1 -1
- package/src/interactive.js +40 -19
package/package.json
CHANGED
package/src/interactive.js
CHANGED
|
@@ -106,15 +106,17 @@ function parseWriteCommands(text) {
|
|
|
106
106
|
/**
|
|
107
107
|
* Parse and apply all file operations from a model reply.
|
|
108
108
|
* Shows diffs/confirmations for each op. Returns updated allFiles list.
|
|
109
|
+
* options.autoConfirm: if true, skip y/n prompts and apply all ops.
|
|
109
110
|
*/
|
|
110
|
-
async function handleFileOps(reply, loadedFiles) {
|
|
111
|
+
async function handleFileOps(reply, loadedFiles, options = {}) {
|
|
112
|
+
const autoConfirm = options.autoConfirm === true;
|
|
111
113
|
let allFiles = getFilesAndDirs();
|
|
112
114
|
|
|
113
115
|
// Create folders first (so !!run: cd <folder> can succeed later)
|
|
114
116
|
for (const folderPath of parseMkdirCommands(reply)) {
|
|
115
117
|
const path = toRelativePath(folderPath);
|
|
116
118
|
process.stdout.write(chalk.dim(` mkdir: ${path} — `));
|
|
117
|
-
const confirmed = await confirm(chalk.bold(`Create folder ${chalk.cyan(path)}? [y/N] `));
|
|
119
|
+
const confirmed = autoConfirm ? true : await confirm(chalk.bold(`Create folder ${chalk.cyan(path)}? [y/N] `));
|
|
118
120
|
if (confirmed) {
|
|
119
121
|
try {
|
|
120
122
|
mkdirSync(resolve(process.cwd(), path), { recursive: true });
|
|
@@ -131,7 +133,7 @@ async function handleFileOps(reply, loadedFiles) {
|
|
|
131
133
|
// Create empty files
|
|
132
134
|
for (const filePath of parseTouchCommands(reply)) {
|
|
133
135
|
const path = toRelativePath(filePath);
|
|
134
|
-
const confirmed = await confirm(chalk.bold(`Create file ${chalk.cyan(path)}? [y/N] `));
|
|
136
|
+
const confirmed = autoConfirm ? true : await confirm(chalk.bold(`Create file ${chalk.cyan(path)}? [y/N] `));
|
|
135
137
|
if (confirmed) {
|
|
136
138
|
try {
|
|
137
139
|
const abs = resolve(process.cwd(), path);
|
|
@@ -152,7 +154,7 @@ async function handleFileOps(reply, loadedFiles) {
|
|
|
152
154
|
for (const { filepath, content } of parseWriteCommands(reply)) {
|
|
153
155
|
const path = toRelativePath(filepath);
|
|
154
156
|
renderDiff(path, content);
|
|
155
|
-
const confirmed = await confirm(chalk.bold(`Write ${chalk.cyan(path)}? [y/N] `));
|
|
157
|
+
const confirmed = autoConfirm ? true : await confirm(chalk.bold(`Write ${chalk.cyan(path)}? [y/N] `));
|
|
156
158
|
if (confirmed) {
|
|
157
159
|
try {
|
|
158
160
|
applyEdit(path, content);
|
|
@@ -171,7 +173,7 @@ async function handleFileOps(reply, loadedFiles) {
|
|
|
171
173
|
for (const { filepath, content } of edits) {
|
|
172
174
|
const path = toRelativePath(filepath);
|
|
173
175
|
renderDiff(path, content);
|
|
174
|
-
const confirmed = await confirm(chalk.bold(`Write ${chalk.cyan(path)}? [y/N] `));
|
|
176
|
+
const confirmed = autoConfirm ? true : await confirm(chalk.bold(`Write ${chalk.cyan(path)}? [y/N] `));
|
|
175
177
|
if (confirmed) {
|
|
176
178
|
try {
|
|
177
179
|
applyEdit(path, content);
|
|
@@ -187,7 +189,7 @@ async function handleFileOps(reply, loadedFiles) {
|
|
|
187
189
|
|
|
188
190
|
// Run terminal commands (after folders/files exist, so cd works)
|
|
189
191
|
for (const cmd of parseRunCommands(reply)) {
|
|
190
|
-
const ok = await confirm(chalk.bold(`Run: ${chalk.cyan(cmd)}? [y/N] `));
|
|
192
|
+
const ok = autoConfirm ? true : await confirm(chalk.bold(`Run: ${chalk.cyan(cmd)}? [y/N] `));
|
|
191
193
|
if (ok) {
|
|
192
194
|
// cd must be handled in-process — child processes can't change the parent's cwd
|
|
193
195
|
const cdMatch = cmd.match(/^cd\s+(.+)$/);
|
|
@@ -215,7 +217,7 @@ async function handleFileOps(reply, loadedFiles) {
|
|
|
215
217
|
// Remove directories
|
|
216
218
|
for (const dirPath of parseRmdirCommands(reply)) {
|
|
217
219
|
const path = toRelativePath(dirPath);
|
|
218
|
-
const confirmed = await confirm(chalk.bold(`Remove directory ${chalk.cyan(path)}? [y/N] `));
|
|
220
|
+
const confirmed = autoConfirm ? true : await confirm(chalk.bold(`Remove directory ${chalk.cyan(path)}? [y/N] `));
|
|
219
221
|
if (confirmed) {
|
|
220
222
|
const abs = resolve(process.cwd(), path);
|
|
221
223
|
if (existsSync(abs)) {
|
|
@@ -237,7 +239,7 @@ async function handleFileOps(reply, loadedFiles) {
|
|
|
237
239
|
// Delete files
|
|
238
240
|
for (const filePath of parseDeleteCommands(reply)) {
|
|
239
241
|
const path = toRelativePath(filePath);
|
|
240
|
-
const confirmed = await confirm(chalk.bold(`Delete ${chalk.cyan(path)}? [y/N] `));
|
|
242
|
+
const confirmed = autoConfirm ? true : await confirm(chalk.bold(`Delete ${chalk.cyan(path)}? [y/N] `));
|
|
241
243
|
if (confirmed) {
|
|
242
244
|
const abs = resolve(process.cwd(), path);
|
|
243
245
|
if (existsSync(abs)) {
|
|
@@ -526,7 +528,7 @@ const HELP_TEXT =
|
|
|
526
528
|
chalk.cyan(' /build') + chalk.dim(' execute the stored plan\n') +
|
|
527
529
|
chalk.cyan(' /models') + chalk.dim(' switch the active AI model\n') +
|
|
528
530
|
chalk.cyan(' /cd [path]') + chalk.dim(' change working directory\n') +
|
|
529
|
-
chalk.dim('\
|
|
531
|
+
chalk.dim('\nNormal chat: plan then build (no y/n). Tips: ') + chalk.cyan('@filename') + chalk.dim(' to attach a file · ctrl+q to cancel\n');
|
|
530
532
|
|
|
531
533
|
export async function startInteractive() {
|
|
532
534
|
printLogo();
|
|
@@ -635,7 +637,7 @@ export async function startInteractive() {
|
|
|
635
637
|
continue;
|
|
636
638
|
}
|
|
637
639
|
|
|
638
|
-
// Normal chat — file ops
|
|
640
|
+
// Normal chat — plan then build (two phases), apply file ops without y/n
|
|
639
641
|
const { loaded, failed } = resolveFileRefs(trimmed);
|
|
640
642
|
|
|
641
643
|
if (loaded.length > 0) {
|
|
@@ -645,19 +647,38 @@ export async function startInteractive() {
|
|
|
645
647
|
console.log(chalk.yellow(`\n⚠ not found: ${failed.map(f => `@${f}`).join(', ')}`));
|
|
646
648
|
}
|
|
647
649
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
650
|
+
const planPrompt =
|
|
651
|
+
`Create a detailed, numbered plan for the following task:\n\n${trimmed}\n\n` +
|
|
652
|
+
`For each step, specify exactly what will happen and which syntax will be used:\n` +
|
|
653
|
+
`- Writing or editing a file → !!write: path/to/file then fenced code block\n` +
|
|
654
|
+
`- Creating an empty file → !!touch: path/to/file\n` +
|
|
655
|
+
`- Creating a folder → !!mkdir: path/to/folder\n` +
|
|
656
|
+
`- Removing a folder → !!rmdir: path/to/folder\n` +
|
|
657
|
+
`- Deleting a file → !!delete: path/to/file\n\n` +
|
|
658
|
+
`Do NOT output any actual file contents or commands yet — only the plan.`;
|
|
659
|
+
|
|
660
|
+
chatMessages.push({ role: 'user', content: planPrompt });
|
|
651
661
|
const abortController = new AbortController();
|
|
652
662
|
try {
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
if (reply === null) {
|
|
663
|
+
const planReply = await streamWithViewport(chatMessages, abortController.signal);
|
|
664
|
+
if (planReply === null) {
|
|
656
665
|
chatMessages.pop();
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
chatMessages.push({ role: 'assistant', content: planReply });
|
|
669
|
+
|
|
670
|
+
const buildPrompt =
|
|
671
|
+
`Execute the following plan. Use !!write: path then a fenced code block for file writes, !!mkdir: for folders, !!delete: for deletions.\n\n` +
|
|
672
|
+
`Plan:\n${planReply}`;
|
|
673
|
+
|
|
674
|
+
chatMessages.push({ role: 'user', content: buildPrompt });
|
|
675
|
+
const buildReply = await streamWithViewport(chatMessages, abortController.signal);
|
|
676
|
+
if (buildReply === null) {
|
|
677
|
+
chatMessages.pop(); // remove build prompt
|
|
678
|
+
continue;
|
|
660
679
|
}
|
|
680
|
+
chatMessages.push({ role: 'assistant', content: buildReply });
|
|
681
|
+
allFiles = await handleFileOps(buildReply, loaded, { autoConfirm: true });
|
|
661
682
|
} catch (err) {
|
|
662
683
|
if (!abortController.signal.aborted) {
|
|
663
684
|
console.log(chalk.red(`\n${err.message}\n`));
|