loop-task 2.1.1 → 2.1.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.
@@ -74,6 +74,7 @@ export const CTRL_SHORTCUT_DELETE = "Ctrl+D";
74
74
  export const CODE_EDITOR_MAX_VISIBLE = 2;
75
75
  export const CODE_EDITOR_MODAL_HEIGHT = 40;
76
76
  export const CODE_EDITOR_MODAL_WIDTH = 120;
77
+ export const CODE_EDITOR_WRAP_LENGTH = 40;
77
78
  export const CODE_EDITOR_UNDO_LIMIT = 50;
78
79
  export const CODE_EDITOR_SYNTAX_COLORS = {
79
80
  flag: "#38bdf8",
@@ -347,11 +347,19 @@ export class LoopController extends EventEmitter {
347
347
  let combinedStdout = "";
348
348
  for (const step of taskSteps) {
349
349
  const stepResults = await Promise.allSettled(step.commands.map((cmd) => executeCommand(interpolate(cmd.command, chainContext), cmd.commandArgs.map(a => interpolate(a, chainContext)), cwd, this.logStream, AbortSignal.any([signal, this.runAbortController.signal]), this.runCount, shouldCaptureStdout)));
350
+ let stepStdout = "";
350
351
  for (const r of stepResults) {
351
352
  if (r.status === "fulfilled") {
352
353
  totalDuration += r.value.duration;
353
354
  if (r.value.stdout)
354
- combinedStdout += (combinedStdout ? "\n" : "") + r.value.stdout;
355
+ stepStdout += (stepStdout ? "\n" : "") + r.value.stdout;
356
+ }
357
+ }
358
+ combinedStdout += (combinedStdout && stepStdout ? "\n" : "") + stepStdout;
359
+ if (shouldCaptureStdout && stepStdout) {
360
+ const parsed = parseStdout(stepStdout);
361
+ if (parsed !== null) {
362
+ Object.assign(chainContext, parsed);
355
363
  }
356
364
  }
357
365
  const stepFailure = stepResults.some((r) => r.status === "rejected" || (r.status === "fulfilled" && r.value.exitCode !== 0));
@@ -362,12 +370,6 @@ export class LoopController extends EventEmitter {
362
370
  }
363
371
  }
364
372
  this.runAbortController = null;
365
- if (shouldCaptureStdout && combinedStdout) {
366
- const parsed = parseStdout(combinedStdout);
367
- if (parsed !== null) {
368
- Object.assign(chainContext, parsed);
369
- }
370
- }
371
373
  const result = {
372
374
  exitCode,
373
375
  duration: totalDuration,
@@ -7,7 +7,7 @@ import { useUndoRedo } from "../../shared/useUndoRedo.js";
7
7
  import { highlightSegments } from "../utils/syntax.js";
8
8
  import { joinCommandLines } from "../../loop-config.js";
9
9
  import { copyToClipboard, readFromClipboard } from "../../shared/clipboard.js";
10
- import { sanitizeMultilinePaste } from "../utils/paste.js";
10
+ import { sanitizeMultilinePaste, wrapCommand } from "../utils/paste.js";
11
11
  import { CODE_EDITOR_MODAL_HEIGHT, CODE_EDITOR_MODAL_WIDTH, CODE_EDITOR_UNDO_LIMIT, CODE_EDITOR_SYNTAX_COLORS, } from "../../config/constants.js";
12
12
  // Cap modal to terminal size: never exceed (terminal - 2) so the border
13
13
  // and a 1-row margin are always visible. Ideal max is the constant.
@@ -62,7 +62,7 @@ export function CodeEditorModal(props) {
62
62
  // Must be detected BEFORE the escape check — the leading ESC trips
63
63
  // key.escape and would close the modal before paste is handled.
64
64
  if (input.includes("\x1b[200~")) {
65
- const pasted = sanitizeMultilinePaste(input);
65
+ const pasted = wrapCommand(sanitizeMultilinePaste(input));
66
66
  if (pasted) {
67
67
  const next = [...lines];
68
68
  const line = next[cursorRow] ?? "";
@@ -197,7 +197,7 @@ export function CodeEditorModal(props) {
197
197
  // Multi-char printable input = unbracketed paste (e.g. right-click
198
198
  // in terminals that don't support DECSET 2004). Preserve newlines.
199
199
  if (input.length > 1 && !key.meta) {
200
- const pasted = sanitizeMultilinePaste(input);
200
+ const pasted = wrapCommand(sanitizeMultilinePaste(input));
201
201
  if (pasted) {
202
202
  const next = [...lines];
203
203
  const line = next[cursorRow] ?? "";
@@ -1,4 +1,5 @@
1
1
  import { PASTE_MAX_CHARS } from "../../config/constants.js";
2
+ import { CODE_EDITOR_WRAP_LENGTH } from "../../config/constants.js";
2
3
  export function sanitizePaste(raw) {
3
4
  return raw
4
5
  .replace(/\x1b\[20[01]~/g, "")
@@ -14,3 +15,23 @@ export function sanitizeMultilinePaste(raw) {
14
15
  .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "")
15
16
  .slice(0, PASTE_MAX_CHARS);
16
17
  }
18
+ export function wrapCommand(text, maxLen = CODE_EDITOR_WRAP_LENGTH) {
19
+ const words = text.split(" ");
20
+ const lines = [];
21
+ let current = "";
22
+ for (const word of words) {
23
+ if (current.length === 0) {
24
+ current = word;
25
+ }
26
+ else if (current.length + 1 + word.length <= maxLen) {
27
+ current += " " + word;
28
+ }
29
+ else {
30
+ lines.push(current);
31
+ current = word;
32
+ }
33
+ }
34
+ if (current)
35
+ lines.push(current);
36
+ return lines.join("\n");
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loop-task",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "Loop engineering toolkit. Run any command on a cadence, in the background, managed from a terminal board. Schedule tests, builds, syncs, or agent prompts.",
5
5
  "type": "module",
6
6
  "bin": {