@plainconceptsplatform/loop-task 2.6.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.
Files changed (202) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +534 -0
  3. package/dist/app/App.js +132 -0
  4. package/dist/app/BoardLayout.js +5 -0
  5. package/dist/app/index.js +48 -0
  6. package/dist/app/providers/index.js +1 -0
  7. package/dist/app/router/index.js +16 -0
  8. package/dist/app/types.js +1 -0
  9. package/dist/cli/import-validator.js +152 -0
  10. package/dist/cli/import-writer.js +81 -0
  11. package/dist/cli.js +573 -0
  12. package/dist/client/cli-format.js +7 -0
  13. package/dist/client/commands.js +162 -0
  14. package/dist/client/ipc.js +96 -0
  15. package/dist/client/project-commands.js +164 -0
  16. package/dist/core/command/command-runner.js +300 -0
  17. package/dist/core/command/process-tree.js +73 -0
  18. package/dist/core/command/resolve-cwd.js +8 -0
  19. package/dist/core/command/stdout-capture-transform.js +73 -0
  20. package/dist/core/context/context-parser.js +65 -0
  21. package/dist/core/context/log-context.js +3 -0
  22. package/dist/core/context/template.js +20 -0
  23. package/dist/core/context/validate-context.js +19 -0
  24. package/dist/core/foreground/index.js +81 -0
  25. package/dist/core/logging/bounded-log-reader.js +368 -0
  26. package/dist/core/logging/log-follower.js +26 -0
  27. package/dist/core/logging/log-parser.js +29 -0
  28. package/dist/core/logging/log-rotator.js +34 -0
  29. package/dist/core/logging/rotating-log-stream.js +137 -0
  30. package/dist/core/loop/chain-executor.js +171 -0
  31. package/dist/core/loop/delay-utils.js +65 -0
  32. package/dist/core/loop/loop-controller.js +240 -0
  33. package/dist/core/loop/loop-runner.js +194 -0
  34. package/dist/core/loop/run-executor.js +144 -0
  35. package/dist/core/loop/types.js +1 -0
  36. package/dist/core/scheduling/index.js +16 -0
  37. package/dist/daemon/daemon-log.js +11 -0
  38. package/dist/daemon/diagnostics.js +85 -0
  39. package/dist/daemon/http/helpers.js +81 -0
  40. package/dist/daemon/http/openapi.js +119 -0
  41. package/dist/daemon/http/route-loops.js +311 -0
  42. package/dist/daemon/http/route-misc.js +60 -0
  43. package/dist/daemon/http/route-projects.js +55 -0
  44. package/dist/daemon/http/route-tasks.js +141 -0
  45. package/dist/daemon/http/routes.js +19 -0
  46. package/dist/daemon/http/server.js +82 -0
  47. package/dist/daemon/http/sse.js +36 -0
  48. package/dist/daemon/index.js +189 -0
  49. package/dist/daemon/ipc/send.js +5 -0
  50. package/dist/daemon/managers/loop-entry.js +27 -0
  51. package/dist/daemon/managers/loop-manager.js +346 -0
  52. package/dist/daemon/managers/loop-options.js +17 -0
  53. package/dist/daemon/managers/loop-serialization.js +53 -0
  54. package/dist/daemon/managers/project-manager.js +170 -0
  55. package/dist/daemon/managers/task-manager.js +94 -0
  56. package/dist/daemon/mcp/index.js +2 -0
  57. package/dist/daemon/mcp/openapi-sync.js +50 -0
  58. package/dist/daemon/mcp/server.js +167 -0
  59. package/dist/daemon/mcp/tools.js +382 -0
  60. package/dist/daemon/recipe/deferred-reload.js +32 -0
  61. package/dist/daemon/recipe/diagram-reader.js +26 -0
  62. package/dist/daemon/recipe/file-writer.js +52 -0
  63. package/dist/daemon/recipe/id-remapper.js +46 -0
  64. package/dist/daemon/recipe/runtime-state.js +23 -0
  65. package/dist/daemon/recipe/scanner.js +165 -0
  66. package/dist/daemon/recipe/task-store.js +33 -0
  67. package/dist/daemon/recipe/validator.js +35 -0
  68. package/dist/daemon/server/handlers/diagnostics-handlers.js +7 -0
  69. package/dist/daemon/server/handlers/index.js +101 -0
  70. package/dist/daemon/server/handlers/log-handlers.js +129 -0
  71. package/dist/daemon/server/handlers/loop-handlers.js +64 -0
  72. package/dist/daemon/server/handlers/project-handlers.js +42 -0
  73. package/dist/daemon/server/handlers/settings-handlers.js +8 -0
  74. package/dist/daemon/server/handlers/task-handlers.js +20 -0
  75. package/dist/daemon/server/handlers/telemetry-handlers.js +5 -0
  76. package/dist/daemon/server/index.js +117 -0
  77. package/dist/daemon/settings-manager.js +73 -0
  78. package/dist/daemon/spawner/index.js +204 -0
  79. package/dist/daemon/state/index.js +209 -0
  80. package/dist/daemon/telemetry/agent-integrations/agent-integration.js +1 -0
  81. package/dist/daemon/telemetry/agent-integrations/claude-code-integration.js +55 -0
  82. package/dist/daemon/telemetry/agent-integrations/detect-agent-integration.js +20 -0
  83. package/dist/daemon/telemetry/agent-integrations/index.js +3 -0
  84. package/dist/daemon/telemetry/agent-integrations/opencode-integration.js +72 -0
  85. package/dist/daemon/telemetry/index.js +6 -0
  86. package/dist/daemon/telemetry/noop-telemetry-adapter.js +53 -0
  87. package/dist/daemon/telemetry/open-telemetry-adapter.js +464 -0
  88. package/dist/daemon/telemetry/telemetry-manager.js +147 -0
  89. package/dist/daemon/telemetry/telemetry-redaction.js +67 -0
  90. package/dist/daemon/telemetry/telemetry-types.js +45 -0
  91. package/dist/daemon/telemetry/telemetry.js +1 -0
  92. package/dist/daemon/watcher/index.js +250 -0
  93. package/dist/duration.js +24 -0
  94. package/dist/entities/loops/filters.js +109 -0
  95. package/dist/entities/loops/index.js +1 -0
  96. package/dist/entities/projects/filters.js +69 -0
  97. package/dist/entities/projects/index.js +1 -0
  98. package/dist/entities/tasks/filters.js +41 -0
  99. package/dist/entities/tasks/index.js +1 -0
  100. package/dist/entry.js +16 -0
  101. package/dist/esm-loader.js +39 -0
  102. package/dist/features/chain-editor/ChainEditor.js +116 -0
  103. package/dist/features/chain-editor/mermaidToAscii.js +375 -0
  104. package/dist/features/chain-editor/renderChainDiagram.js +106 -0
  105. package/dist/features/code-editor/CodeEditorModal.js +84 -0
  106. package/dist/features/code-editor/CodeEditorPreview.js +14 -0
  107. package/dist/features/code-editor/useEditorKeyboard.js +150 -0
  108. package/dist/features/code-editor/useModalDimensions.js +9 -0
  109. package/dist/features/commands/commands.js +132 -0
  110. package/dist/features/commands/useCommandHandlers.js +280 -0
  111. package/dist/features/commands/useContextualActions.js +78 -0
  112. package/dist/features/commands/useGlobalShortcuts.js +154 -0
  113. package/dist/features/forms/FormRouter.js +46 -0
  114. package/dist/features/overlays/ContextHelpModal.js +8 -0
  115. package/dist/features/overlays/DiagramModal.js +45 -0
  116. package/dist/features/overlays/ExportModal.js +51 -0
  117. package/dist/features/overlays/HelpGuideModal.js +23 -0
  118. package/dist/features/overlays/HelpModal.js +59 -0
  119. package/dist/features/overlays/OverlayStack.js +10 -0
  120. package/dist/features/overlays/ProjectsModal.js +42 -0
  121. package/dist/features/overlays/TaskPickerModal.js +62 -0
  122. package/dist/features/overlays/WelcomeScreen.js +74 -0
  123. package/dist/features/overlays/useOverlayStack.js +55 -0
  124. package/dist/features/state/useAppState.js +151 -0
  125. package/dist/logger.js +25 -0
  126. package/dist/loop-config.js +152 -0
  127. package/dist/shared/clipboard.js +111 -0
  128. package/dist/shared/config/constants.js +104 -0
  129. package/dist/shared/config/paths.js +67 -0
  130. package/dist/shared/container/index.js +24 -0
  131. package/dist/shared/fs-utils.js +44 -0
  132. package/dist/shared/hooks/useBreakpoint.js +11 -0
  133. package/dist/shared/hooks/useDaemonSettings.js +41 -0
  134. package/dist/shared/hooks/useInject.js +6 -0
  135. package/dist/shared/hooks/useLogStream.js +37 -0
  136. package/dist/shared/hooks/useLoopFormValidation.js +131 -0
  137. package/dist/shared/hooks/useLoopPolling.js +30 -0
  138. package/dist/shared/hooks/useMouseScroll.js +56 -0
  139. package/dist/shared/hooks/useUndoRedo.js +77 -0
  140. package/dist/shared/i18n/en.json +709 -0
  141. package/dist/shared/i18n/index.js +11 -0
  142. package/dist/shared/providers/InversifyProvider.js +6 -0
  143. package/dist/shared/services/export-service.js +43 -0
  144. package/dist/shared/services/log-service.js +28 -0
  145. package/dist/shared/services/loop-service.js +78 -0
  146. package/dist/shared/services/project-service.js +39 -0
  147. package/dist/shared/services/settings-service.js +102 -0
  148. package/dist/shared/services/task-service.js +41 -0
  149. package/dist/shared/services/types.js +8 -0
  150. package/dist/shared/sleep.js +17 -0
  151. package/dist/shared/tail.js +4 -0
  152. package/dist/shared/ui/Button.js +18 -0
  153. package/dist/shared/ui/DebugPanel.js +9 -0
  154. package/dist/shared/ui/FocusableButton.js +18 -0
  155. package/dist/shared/ui/FocusableInput.js +72 -0
  156. package/dist/shared/ui/FocusableList.js +48 -0
  157. package/dist/shared/ui/Modal.js +13 -0
  158. package/dist/shared/ui/SelectModal.js +78 -0
  159. package/dist/shared/ui/Toast.js +37 -0
  160. package/dist/shared/ui/format.js +117 -0
  161. package/dist/shared/ui/hooks/useHoverState.js +11 -0
  162. package/dist/shared/ui/state.js +9 -0
  163. package/dist/shared/ui/theme.js +96 -0
  164. package/dist/shared/utils/log-lines.js +13 -0
  165. package/dist/shared/utils/paste.js +37 -0
  166. package/dist/shared/utils/syntax.js +128 -0
  167. package/dist/shared/utils/validation.js +56 -0
  168. package/dist/types.js +1 -0
  169. package/dist/visual-evidence/capture.js +51 -0
  170. package/dist/visual-evidence/cli.js +85 -0
  171. package/dist/visual-evidence/evidence-required.js +71 -0
  172. package/dist/visual-evidence/index.js +10 -0
  173. package/dist/visual-evidence/launch.js +61 -0
  174. package/dist/visual-evidence/manifest.js +32 -0
  175. package/dist/visual-evidence/openspec-resolver.js +45 -0
  176. package/dist/visual-evidence/publish.js +126 -0
  177. package/dist/visual-evidence/run.js +121 -0
  178. package/dist/visual-evidence/scenario-registry.js +132 -0
  179. package/dist/widgets/command-input/CommandDropdown.js +69 -0
  180. package/dist/widgets/command-input/CommandInput.js +133 -0
  181. package/dist/widgets/command-input/HintBar.js +9 -0
  182. package/dist/widgets/command-input/InputModes.js +72 -0
  183. package/dist/widgets/commands-browser/CommandsBrowserModal.js +85 -0
  184. package/dist/widgets/header/Header.js +54 -0
  185. package/dist/widgets/header/TabBar.js +20 -0
  186. package/dist/widgets/left-panel/LeftPanel.js +34 -0
  187. package/dist/widgets/left-panel/Navigator.js +107 -0
  188. package/dist/widgets/left-panel/ProjectsPage.js +76 -0
  189. package/dist/widgets/left-panel/ProjectsPageParts.js +34 -0
  190. package/dist/widgets/left-panel/TaskBrowser.js +132 -0
  191. package/dist/widgets/log-modal/LogModal.js +211 -0
  192. package/dist/widgets/loop-form/CreateForm.js +82 -0
  193. package/dist/widgets/loop-form/TextField.js +9 -0
  194. package/dist/widgets/loop-form/WizardForm.js +199 -0
  195. package/dist/widgets/loop-form/useCreateSteps.js +135 -0
  196. package/dist/widgets/loop-form/useHandleComplete.js +94 -0
  197. package/dist/widgets/project-form/ProjectForm.js +83 -0
  198. package/dist/widgets/right-panel/Inspector.js +41 -0
  199. package/dist/widgets/right-panel/RightPanel.js +38 -0
  200. package/dist/widgets/right-panel/RunHistory.js +187 -0
  201. package/dist/widgets/task-form/TaskForm.js +272 -0
  202. package/package.json +119 -0
@@ -0,0 +1,272 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useCallback, useState, useRef } from "react";
3
+ import { DEFAULT_TASK_MAX_RUNS } from "../../types.js";
4
+ import { WizardForm } from "../loop-form/WizardForm.js";
5
+ import { SelectModal, SelectValueField } from "../../shared/ui/SelectModal.js";
6
+ import { CodeEditorPreview } from "../../features/code-editor/CodeEditorPreview.js";
7
+ import { CodeEditorModal } from "../../features/code-editor/CodeEditorModal.js";
8
+ import { useInject } from "../../shared/hooks/useInject.js";
9
+ import { TYPES } from "../../shared/services/types.js";
10
+ import { validateContext } from "../../core/context/validate-context.js";
11
+ import crypto from "node:crypto";
12
+ import { t } from "../../shared/i18n/index.js";
13
+ import { joinCommandLines, parseCommandLine, parseMaxRuns } from "../../loop-config.js";
14
+ function parseArgs(cmd) {
15
+ const tokens = [];
16
+ const regex = /"([^"]*)"|'([^']*)'|(\S+)/g;
17
+ let match;
18
+ while ((match = regex.exec(cmd)) !== null) {
19
+ tokens.push(match[1] ?? match[2] ?? match[3] ?? "");
20
+ }
21
+ return tokens.slice(1);
22
+ }
23
+ function joinCommand(task) {
24
+ return [task.command, ...task.commandArgs].join(" ");
25
+ }
26
+ export function TaskForm(props) {
27
+ const { mode, editTask, onCancel, onDone } = props;
28
+ const taskService = useInject(TYPES.TaskService);
29
+ const [tasks, setTasks] = useState([]);
30
+ const [commandValue, setCommandValue] = useState(editTask
31
+ ? (editTask.steps?.length
32
+ ? editTask.steps.map(step => step.commands.map(c => c.commandRaw ?? [c.command, ...c.commandArgs].join(" ")).join("\n||\n")).join("\n&&\n")
33
+ : (editTask.commandRaw ?? joinCommand(editTask)))
34
+ : "");
35
+ useEffect(() => {
36
+ taskService.list().then(setTasks).catch(() => setTasks([]));
37
+ }, []);
38
+ const chainOptions = useMemo(() => [t("wizard.chainNone"), ...tasks.map((task) => task.name)], [tasks]);
39
+ const chainSelectOptions = useMemo(() => chainOptions.map((v) => ({ value: v, label: v })), [chainOptions]);
40
+ const [commandEditorOpen, setCommandEditorOpen] = useState(false);
41
+ const [contextEditorOpen, setContextEditorOpen] = useState(false);
42
+ const [contextValue, setContextValue] = useState(editTask?.context ? JSON.stringify(editTask.context) : "");
43
+ const [silentChainValue, setSilentChainValue] = useState(editTask?.silentChain ? t("board.silentChainYes") : "");
44
+ const [openChainField, setOpenChainField] = useState(null);
45
+ const [openSilentChain, setOpenSilentChain] = useState(false);
46
+ const chainFieldsRef = useRef({});
47
+ const resolveChainId = useCallback((val) => {
48
+ if (!val || val === t("wizard.chainNone"))
49
+ return null;
50
+ const found = tasks.find((task) => task.name === val || task.id === val);
51
+ return found?.id ?? val;
52
+ }, [tasks]);
53
+ const resolveChainName = useCallback((id) => {
54
+ if (!id)
55
+ return t("wizard.chainNone");
56
+ const found = tasks.find((task) => task.id === id);
57
+ return found?.name ?? id;
58
+ }, [tasks]);
59
+ const steps = useMemo(() => {
60
+ const list = [
61
+ {
62
+ key: "name",
63
+ prompt: t("wizard.taskNamePrompt"),
64
+ hint: t("wizard.taskNameHint"),
65
+ required: true,
66
+ inputType: "text",
67
+ defaultValue: editTask?.name ?? undefined,
68
+ },
69
+ {
70
+ key: "command",
71
+ prompt: t("wizard.taskCommandPrompt"),
72
+ hint: t("wizard.commandHint"),
73
+ required: true,
74
+ inputType: "text",
75
+ defaultValue: commandValue || undefined,
76
+ onActivate: () => setCommandEditorOpen(true),
77
+ renderCustom: ({ isActive }) => (_jsx(CodeEditorPreview, { value: commandValue, hint: t("wizard.commandHint"), isActive: isActive, onActivate: () => setCommandEditorOpen(true) })),
78
+ },
79
+ {
80
+ key: "onSuccess",
81
+ prompt: t("wizard.onSuccessPrompt"),
82
+ hint: t("board.taskHintOnSuccess"),
83
+ required: false,
84
+ defaultValue: editTask ? resolveChainName(editTask.onSuccessTaskId) : undefined,
85
+ onActivate: () => setOpenChainField("onSuccess"),
86
+ renderCustom: ({ value, isActive, onChange, onAdvance }) => {
87
+ chainFieldsRef.current.onSuccess = { value, onChange, onAdvance };
88
+ return (_jsx(SelectValueField, { label: value || null, placeholder: t("wizard.onSuccessPrompt"), isActive: isActive }));
89
+ },
90
+ },
91
+ {
92
+ key: "onFailure",
93
+ prompt: t("wizard.onFailurePrompt"),
94
+ hint: t("board.taskHintOnFailure"),
95
+ required: false,
96
+ defaultValue: editTask ? resolveChainName(editTask.onFailureTaskId) : undefined,
97
+ onActivate: () => setOpenChainField("onFailure"),
98
+ renderCustom: ({ value, isActive, onChange, onAdvance }) => {
99
+ chainFieldsRef.current.onFailure = { value, onChange, onAdvance };
100
+ return (_jsx(SelectValueField, { label: value || null, placeholder: t("wizard.onFailurePrompt"), isActive: isActive }));
101
+ },
102
+ },
103
+ {
104
+ key: "silentChain",
105
+ prompt: t("wizard.silentChainPrompt"),
106
+ hint: t("wizard.silentChainHint"),
107
+ required: false,
108
+ defaultValue: silentChainValue || undefined,
109
+ onActivate: () => setOpenSilentChain(true),
110
+ renderCustom: ({ value, isActive }) => (_jsx(SelectValueField, { label: value || null, placeholder: t("wizard.silentChainPrompt"), isActive: isActive })),
111
+ },
112
+ {
113
+ key: "maxRuns",
114
+ prompt: t("wizard.taskMaxRunsPrompt"),
115
+ hint: t("wizard.taskMaxRunsHint"),
116
+ required: false,
117
+ inputType: "text",
118
+ defaultValue: editTask ? String(editTask.maxRuns) : String(DEFAULT_TASK_MAX_RUNS),
119
+ validate: (value) => {
120
+ if (!value?.trim())
121
+ return true;
122
+ try {
123
+ parseMaxRuns(value);
124
+ return true;
125
+ }
126
+ catch {
127
+ return false;
128
+ }
129
+ },
130
+ validationError: t("errors.maxRunsInvalid"),
131
+ },
132
+ {
133
+ key: "context",
134
+ prompt: t("wizard.contextPrompt"),
135
+ hint: t("wizard.contextHint"),
136
+ required: false,
137
+ inputType: "text",
138
+ defaultValue: contextValue || undefined,
139
+ onActivate: () => setContextEditorOpen(true),
140
+ validate: (value) => {
141
+ if (!value?.trim())
142
+ return true;
143
+ try {
144
+ const parsed = JSON.parse(value);
145
+ const result = validateContext(parsed);
146
+ return result.valid;
147
+ }
148
+ catch {
149
+ return false;
150
+ }
151
+ },
152
+ validationError: t("wizard.contextInvalid"),
153
+ renderCustom: ({ isActive }) => (_jsx(CodeEditorPreview, { value: contextValue, hint: t("wizard.contextHint"), isActive: isActive, onActivate: () => setContextEditorOpen(true) })),
154
+ },
155
+ ];
156
+ return list;
157
+ }, [commandValue, chainOptions, editTask, resolveChainName, contextValue, silentChainValue]);
158
+ const handleComplete = useCallback((values) => {
159
+ const name = values.name?.trim() ?? "";
160
+ const rawCommand = joinCommandLines(commandValue);
161
+ if (!name || !rawCommand)
162
+ return;
163
+ const onSuccessTaskId = resolveChainId(values.onSuccess ?? "");
164
+ const onFailureTaskId = resolveChainId(values.onFailure ?? "");
165
+ const silentChain = values.silentChain === t("board.silentChainYes");
166
+ const maxRunsRaw = values.maxRuns?.trim();
167
+ let maxRuns = DEFAULT_TASK_MAX_RUNS;
168
+ if (maxRunsRaw) {
169
+ try {
170
+ maxRuns = parseMaxRuns(maxRunsRaw) ?? DEFAULT_TASK_MAX_RUNS;
171
+ }
172
+ catch {
173
+ maxRuns = DEFAULT_TASK_MAX_RUNS;
174
+ }
175
+ }
176
+ let context;
177
+ const contextStr = contextValue?.trim();
178
+ if (contextStr) {
179
+ try {
180
+ const parsed = JSON.parse(contextStr);
181
+ const result = validateContext(parsed);
182
+ if (result.valid) {
183
+ context = result.context;
184
+ }
185
+ }
186
+ catch {
187
+ // invalid context, skip
188
+ }
189
+ }
190
+ const commandSegments = commandValue
191
+ .split(/\n&&\n|\n&&|&&\n|&&/)
192
+ .map(s => s.trim())
193
+ .filter(s => s.length > 0);
194
+ let payload;
195
+ if (commandSegments.length > 1 || commandValue.includes("||")) {
196
+ const steps = commandSegments.slice(0, 8).map(seg => {
197
+ const parallelCmds = seg
198
+ .split(/\n\|\|\n|\n\|\||\|\|\n|\|\|/)
199
+ .map(s => s.trim())
200
+ .filter(s => s.length > 0)
201
+ .slice(0, 4)
202
+ .map(cmdSeg => {
203
+ const joined = joinCommandLines(cmdSeg);
204
+ const tokens = parseCommandLine(joined);
205
+ return {
206
+ command: tokens[0] ?? "",
207
+ commandArgs: tokens.slice(1),
208
+ commandRaw: cmdSeg,
209
+ };
210
+ });
211
+ return { commands: parallelCmds };
212
+ });
213
+ const firstCmd = steps[0].commands[0];
214
+ payload = {
215
+ name,
216
+ command: firstCmd.command,
217
+ commandArgs: firstCmd.commandArgs,
218
+ commandRaw: firstCmd.commandRaw,
219
+ steps,
220
+ onSuccessTaskId,
221
+ onFailureTaskId,
222
+ silentChain: silentChain || undefined,
223
+ maxRuns,
224
+ context,
225
+ };
226
+ }
227
+ else {
228
+ payload = {
229
+ name,
230
+ command: rawCommand.split(" ")[0] ?? "",
231
+ commandArgs: parseArgs(rawCommand),
232
+ commandRaw: commandValue,
233
+ onSuccessTaskId,
234
+ onFailureTaskId,
235
+ silentChain: silentChain || undefined,
236
+ maxRuns,
237
+ context,
238
+ };
239
+ }
240
+ if (mode === "edit" && editTask) {
241
+ taskService.update(editTask.id, payload)
242
+ .then(() => onDone(true, editTask.id))
243
+ .catch(() => { });
244
+ }
245
+ else {
246
+ const id = crypto.randomUUID().slice(0, 8);
247
+ taskService.create({ id, ...payload })
248
+ .then((result) => onDone(false, result.id))
249
+ .catch(() => { });
250
+ }
251
+ }, [commandValue, resolveChainId, mode, editTask, onDone, contextValue, silentChainValue]);
252
+ const title = mode === "edit"
253
+ ? t("board.taskEditTitle")
254
+ : t("board.taskCreateTitle");
255
+ return (_jsxs(_Fragment, { children: [_jsx(WizardForm, { title: title, steps: steps, onComplete: handleComplete, onCancel: onCancel, disabled: openChainField !== null || commandEditorOpen || contextEditorOpen || openSilentChain }), openChainField ? (_jsx(SelectModal, { title: openChainField === "onSuccess" ? t("wizard.onSuccessPrompt") : t("wizard.onFailurePrompt"), options: chainSelectOptions, initialValue: chainFieldsRef.current[openChainField]?.value, onSelect: (option) => {
256
+ chainFieldsRef.current[openChainField]?.onChange(option.value);
257
+ chainFieldsRef.current[openChainField]?.onAdvance();
258
+ setOpenChainField(null);
259
+ }, onClose: () => setOpenChainField(null) })) : null, commandEditorOpen ? (_jsx(CodeEditorModal, { initialValue: commandValue, onSave: (v) => {
260
+ setCommandValue(v);
261
+ setCommandEditorOpen(false);
262
+ }, onCancel: () => setCommandEditorOpen(false) })) : null, contextEditorOpen ? (_jsx(CodeEditorModal, { initialValue: contextValue, onSave: (v) => {
263
+ setContextValue(v);
264
+ setContextEditorOpen(false);
265
+ }, onCancel: () => setContextEditorOpen(false) })) : null, openSilentChain ? (_jsx(SelectModal, { title: t("wizard.silentChainPrompt"), options: [
266
+ { value: t("board.silentChainNo"), label: t("board.silentChainNo") },
267
+ { value: t("board.silentChainYes"), label: t("board.silentChainYes") },
268
+ ], initialValue: silentChainValue || t("board.silentChainNo"), onSelect: (option) => {
269
+ setSilentChainValue(option.value === t("board.silentChainNo") ? "" : option.value);
270
+ setOpenSilentChain(false);
271
+ }, onClose: () => setOpenSilentChain(false) })) : null] }));
272
+ }
package/package.json ADDED
@@ -0,0 +1,119 @@
1
+ {
2
+ "name": "@plainconceptsplatform/loop-task",
3
+ "version": "2.6.0",
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
+ "type": "module",
6
+ "bin": {
7
+ "loop-task": "dist/entry.js"
8
+ },
9
+ "main": "dist/entry.js",
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && node -e \"require('fs').copyFileSync('src/entry.js','dist/entry.js'); require('fs').copyFileSync('src/esm-loader.js','dist/esm-loader.js')\"",
17
+ "prepublishOnly": "npm run build",
18
+ "start": "node dist/entry.js",
19
+ "dev": "tsx src/cli.ts",
20
+ "dev:watch": "tsx --watch src/cli.ts",
21
+ "board": "node dist/entry.js",
22
+ "test": "vitest run --exclude '**/background-cli.test.ts'",
23
+ "test:all": "vitest run",
24
+ "test:watch": "vitest",
25
+ "test:coverage": "vitest run --coverage --exclude '**/background-cli.test.ts'",
26
+ "lint": "eslint src/ tests/",
27
+ "typecheck": "tsc --noEmit",
28
+ "release:dry": "npm publish --dry-run",
29
+ "release": "npm publish",
30
+ "visual-evidence": "tsx src/visual-evidence/cli.ts",
31
+ "visual-evidence:publish": "tsx src/visual-evidence/publish.ts",
32
+ "skills:mark-internal": "node scripts/mark-agent-skills-internal.mjs",
33
+ "prepare": "husky"
34
+ },
35
+ "keywords": [
36
+ "cli",
37
+ "loop",
38
+ "loop-engineering",
39
+ "repeat",
40
+ "interval",
41
+ "schedule",
42
+ "timer",
43
+ "cron",
44
+ "automation",
45
+ "automations",
46
+ "devops",
47
+ "agent",
48
+ "ai-agent",
49
+ "coding-agent",
50
+ "agent-automation",
51
+ "claude-code",
52
+ "codex",
53
+ "opencode",
54
+ "background-tasks",
55
+ "scheduler"
56
+ ],
57
+ "author": "Quique Fdez Guerra",
58
+ "license": "MIT",
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "https://github.com/plainconceptsplatform/loop-task.git"
62
+ },
63
+ "homepage": "https://github.com/plainconceptsplatform/loop-task",
64
+ "bugs": {
65
+ "url": "https://github.com/plainconceptsplatform/loop-task/issues"
66
+ },
67
+ "engines": {
68
+ "node": ">=20.0.0"
69
+ },
70
+ "dependencies": {
71
+ "@modelcontextprotocol/sdk": "^1.29.0",
72
+ "@opentelemetry/api": "^1.9.1",
73
+ "@opentelemetry/exporter-metrics-otlp-grpc": "^0.221.0",
74
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.221.0",
75
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.221.0",
76
+ "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
77
+ "@opentelemetry/resources": "^2.10.0",
78
+ "@opentelemetry/sdk-metrics": "^2.10.0",
79
+ "@opentelemetry/sdk-node": "^0.221.0",
80
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
81
+ "@opentelemetry/sdk-trace-node": "^2.10.0",
82
+ "@opentelemetry/semantic-conventions": "^1.43.0",
83
+ "commander": "^13.1.0",
84
+ "execa": "^9.6.0",
85
+ "ink": "^7.1.0",
86
+ "ink-combobox": "^0.2.0",
87
+ "ink-scroll-list": "^0.4.1",
88
+ "ink-select-input": "^6.2.0",
89
+ "ink-spinner": "^5.0.0",
90
+ "ink-text-input": "^6.0.0",
91
+ "inversify": "^8.1.1",
92
+ "inversify-hooks": "^4.0.0",
93
+ "js-yaml": "^5.2.2",
94
+ "ms": "^2.1.3",
95
+ "react": "^19.2.7",
96
+ "yaml": "^2.9.0",
97
+ "zod": "^4.4.3"
98
+ },
99
+ "devDependencies": {
100
+ "@types/js-yaml": "^4.0.9",
101
+ "@types/ms": "^2.1.0",
102
+ "@types/node": "^22.15.0",
103
+ "@types/react": "^19.2.17",
104
+ "@vitest/coverage-v8": "^3.1.0",
105
+ "eslint": "^9.25.0",
106
+ "husky": "^9.1.7",
107
+ "ink-testing-library": "^4.0.0",
108
+ "tsx": "^4.19.0",
109
+ "typescript": "^5.8.0",
110
+ "typescript-eslint": "^8.30.0",
111
+ "vitest": "^3.1.0"
112
+ },
113
+ "packageManager": "pnpm@10.30.3",
114
+ "pnpm": {
115
+ "overrides": {
116
+ "picocolors": "1.1.0"
117
+ }
118
+ }
119
+ }