u-foo 1.0.6 → 1.1.9

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 (149) hide show
  1. package/README.md +44 -4
  2. package/SKILLS/ufoo/SKILL.md +17 -2
  3. package/SKILLS/uinit/SKILL.md +8 -3
  4. package/bin/ucode-core.js +15 -0
  5. package/bin/ucode.js +125 -0
  6. package/bin/ufoo-assistant-agent.js +5 -0
  7. package/bin/ufoo-engine.js +25 -0
  8. package/bin/ufoo.js +4 -0
  9. package/modules/AGENTS.template.md +14 -4
  10. package/modules/bus/README.md +8 -5
  11. package/modules/bus/SKILLS/ubus/SKILL.md +5 -4
  12. package/modules/context/SKILLS/uctx/SKILL.md +3 -1
  13. package/modules/online/SKILLS/ufoo-online/SKILL.md +144 -0
  14. package/package.json +12 -3
  15. package/scripts/import-pi-mono.js +124 -0
  16. package/scripts/postinstall.js +20 -49
  17. package/scripts/sync-claude-skills.sh +21 -0
  18. package/src/agent/cliRunner.js +524 -31
  19. package/src/agent/internalRunner.js +76 -9
  20. package/src/agent/launcher.js +97 -45
  21. package/src/agent/normalizeOutput.js +1 -1
  22. package/src/agent/notifier.js +144 -4
  23. package/src/agent/ptyRunner.js +480 -10
  24. package/src/agent/ptyWrapper.js +28 -3
  25. package/src/agent/readyDetector.js +16 -0
  26. package/src/agent/ucode.js +443 -0
  27. package/src/agent/ucodeBootstrap.js +113 -0
  28. package/src/agent/ucodeBuild.js +67 -0
  29. package/src/agent/ucodeDoctor.js +184 -0
  30. package/src/agent/ucodeRuntimeConfig.js +129 -0
  31. package/src/agent/ufooAgent.js +11 -2
  32. package/src/assistant/agent.js +260 -0
  33. package/src/assistant/bridge.js +172 -0
  34. package/src/assistant/engine.js +252 -0
  35. package/src/assistant/stdio.js +58 -0
  36. package/src/assistant/ufooEngineCli.js +306 -0
  37. package/src/bus/activate.js +27 -11
  38. package/src/bus/daemon.js +133 -5
  39. package/src/bus/index.js +137 -80
  40. package/src/bus/inject.js +47 -17
  41. package/src/bus/message.js +145 -17
  42. package/src/bus/nickname.js +3 -1
  43. package/src/bus/queue.js +6 -1
  44. package/src/bus/store.js +189 -0
  45. package/src/bus/subscriber.js +20 -4
  46. package/src/bus/utils.js +9 -3
  47. package/src/chat/agentBar.js +117 -0
  48. package/src/chat/agentDirectory.js +88 -0
  49. package/src/chat/agentSockets.js +225 -0
  50. package/src/chat/agentViewController.js +298 -0
  51. package/src/chat/chatLogController.js +115 -0
  52. package/src/chat/commandExecutor.js +700 -0
  53. package/src/chat/commands.js +132 -0
  54. package/src/chat/completionController.js +414 -0
  55. package/src/chat/cronScheduler.js +160 -0
  56. package/src/chat/daemonConnection.js +166 -0
  57. package/src/chat/daemonCoordinator.js +64 -0
  58. package/src/chat/daemonMessageRouter.js +257 -0
  59. package/src/chat/daemonReconnect.js +41 -0
  60. package/src/chat/daemonTransport.js +36 -0
  61. package/src/chat/daemonTransportDefaults.js +10 -0
  62. package/src/chat/dashboardKeyController.js +480 -0
  63. package/src/chat/dashboardView.js +154 -0
  64. package/src/chat/index.js +935 -2909
  65. package/src/chat/inputHistoryController.js +105 -0
  66. package/src/chat/inputListenerController.js +304 -0
  67. package/src/chat/inputMath.js +104 -0
  68. package/src/chat/inputSubmitHandler.js +171 -0
  69. package/src/chat/layout.js +165 -0
  70. package/src/chat/pasteController.js +81 -0
  71. package/src/chat/rawKeyMap.js +42 -0
  72. package/src/chat/settingsController.js +132 -0
  73. package/src/chat/statusLineController.js +177 -0
  74. package/src/chat/streamTracker.js +138 -0
  75. package/src/chat/text.js +70 -0
  76. package/src/chat/transport.js +61 -0
  77. package/src/cli/busCoreCommands.js +59 -0
  78. package/src/cli/ctxCoreCommands.js +199 -0
  79. package/src/cli/onlineCoreCommands.js +379 -0
  80. package/src/cli.js +741 -238
  81. package/src/code/README.md +29 -0
  82. package/src/code/UCODE_PROMPT.md +32 -0
  83. package/src/code/agent.js +1651 -0
  84. package/src/code/cli.js +158 -0
  85. package/src/code/config +0 -0
  86. package/src/code/dispatch.js +42 -0
  87. package/src/code/index.js +70 -0
  88. package/src/code/nativeRunner.js +1213 -0
  89. package/src/code/runtime.js +154 -0
  90. package/src/code/sessionStore.js +162 -0
  91. package/src/code/taskDecomposer.js +269 -0
  92. package/src/code/tools/bash.js +53 -0
  93. package/src/code/tools/common.js +42 -0
  94. package/src/code/tools/edit.js +70 -0
  95. package/src/code/tools/read.js +44 -0
  96. package/src/code/tools/write.js +35 -0
  97. package/src/code/tui.js +1580 -0
  98. package/src/config.js +47 -1
  99. package/src/context/decisions.js +12 -2
  100. package/src/context/index.js +18 -1
  101. package/src/context/sync.js +127 -0
  102. package/src/daemon/agentProcessManager.js +74 -0
  103. package/src/daemon/cronOps.js +241 -0
  104. package/src/daemon/index.js +661 -488
  105. package/src/daemon/ipcServer.js +99 -0
  106. package/src/daemon/ops.js +417 -179
  107. package/src/daemon/promptLoop.js +319 -0
  108. package/src/daemon/promptRequest.js +101 -0
  109. package/src/daemon/providerSessions.js +32 -17
  110. package/src/daemon/reporting.js +90 -0
  111. package/src/daemon/run.js +2 -5
  112. package/src/daemon/status.js +24 -1
  113. package/src/init/index.js +68 -14
  114. package/src/online/bridge.js +663 -0
  115. package/src/online/client.js +245 -0
  116. package/src/online/runner.js +253 -0
  117. package/src/online/server.js +992 -0
  118. package/src/online/tokens.js +103 -0
  119. package/src/report/store.js +331 -0
  120. package/src/shared/eventContract.js +35 -0
  121. package/src/shared/ptySocketContract.js +21 -0
  122. package/src/status/index.js +50 -17
  123. package/src/terminal/adapterContract.js +87 -0
  124. package/src/terminal/adapterRouter.js +84 -0
  125. package/src/terminal/adapters/externalAdapter.js +14 -0
  126. package/src/terminal/adapters/internalAdapter.js +13 -0
  127. package/src/terminal/adapters/internalPtyAdapter.js +42 -0
  128. package/src/terminal/adapters/internalQueueAdapter.js +37 -0
  129. package/src/terminal/adapters/terminalAdapter.js +31 -0
  130. package/src/terminal/adapters/tmuxAdapter.js +30 -0
  131. package/src/ufoo/agentsStore.js +69 -3
  132. package/src/utils/banner.js +5 -2
  133. package/scripts/.archived/bash-to-js-migration/README.md +0 -46
  134. package/scripts/.archived/bash-to-js-migration/banner.sh +0 -89
  135. package/scripts/.archived/bash-to-js-migration/bus-alert.sh +0 -6
  136. package/scripts/.archived/bash-to-js-migration/bus-autotrigger.sh +0 -6
  137. package/scripts/.archived/bash-to-js-migration/bus-daemon.sh +0 -231
  138. package/scripts/.archived/bash-to-js-migration/bus-inject.sh +0 -176
  139. package/scripts/.archived/bash-to-js-migration/bus-listen.sh +0 -6
  140. package/scripts/.archived/bash-to-js-migration/bus.sh +0 -986
  141. package/scripts/.archived/bash-to-js-migration/context-decisions.sh +0 -167
  142. package/scripts/.archived/bash-to-js-migration/context-doctor.sh +0 -72
  143. package/scripts/.archived/bash-to-js-migration/context-lint.sh +0 -110
  144. package/scripts/.archived/bash-to-js-migration/doctor.sh +0 -22
  145. package/scripts/.archived/bash-to-js-migration/init.sh +0 -247
  146. package/scripts/.archived/bash-to-js-migration/skills.sh +0 -113
  147. package/scripts/.archived/bash-to-js-migration/status.sh +0 -125
  148. package/scripts/banner.sh +0 -2
  149. package/src/bus/API_DESIGN.md +0 -204
@@ -0,0 +1,138 @@
1
+ function createStreamTracker(options = {}) {
2
+ const {
3
+ logBox,
4
+ writeSpacer,
5
+ appendHistory,
6
+ escapeBlessed,
7
+ onStreamStart,
8
+ now = () => new Date().toISOString(),
9
+ } = options;
10
+
11
+ const streamStates = new Map();
12
+ const pendingDeliveries = new Map();
13
+
14
+ function buildStreamDisplayText(fullText, prefix, continuationPrefix) {
15
+ const lines = String(fullText || "").split("\n");
16
+ return lines.map((line, i) => {
17
+ const p = i === 0 ? prefix : continuationPrefix;
18
+ return `${p}${escapeBlessed(line)}`;
19
+ }).join("\n");
20
+ }
21
+
22
+ function beginStream(publisher, prefix, continuationPrefix, meta) {
23
+ let state = streamStates.get(publisher);
24
+ if (state) return state;
25
+
26
+ if (typeof writeSpacer === "function") {
27
+ writeSpacer();
28
+ }
29
+ logBox.pushLine(prefix);
30
+ state = {
31
+ publisher,
32
+ prefix,
33
+ continuationPrefix,
34
+ lineIndex: logBox.getLines().length - 1,
35
+ buffer: "",
36
+ full: "",
37
+ linesEmitted: 0,
38
+ meta,
39
+ };
40
+ streamStates.set(publisher, state);
41
+ if (typeof onStreamStart === "function") {
42
+ onStreamStart();
43
+ }
44
+ return state;
45
+ }
46
+
47
+ function appendStreamDelta(state, delta) {
48
+ if (!delta || !state) return;
49
+ state.full += delta;
50
+ state.buffer += delta;
51
+ const parts = state.buffer.split("\n");
52
+ if (parts.length > 1) {
53
+ const completed = parts.slice(0, -1);
54
+ for (const line of completed) {
55
+ const prefix = state.linesEmitted === 0 ? state.prefix : state.continuationPrefix;
56
+ logBox.setLine(state.lineIndex, `${prefix}${escapeBlessed(line)}`);
57
+ state.linesEmitted += 1;
58
+ logBox.pushLine(state.continuationPrefix);
59
+ state.lineIndex = logBox.getLines().length - 1;
60
+ }
61
+ state.buffer = parts[parts.length - 1];
62
+ }
63
+ const prefix = state.linesEmitted === 0 ? state.prefix : state.continuationPrefix;
64
+ logBox.setLine(state.lineIndex, `${prefix}${escapeBlessed(state.buffer)}`);
65
+ }
66
+
67
+ function finalizeStream(publisher, meta, reason = "") {
68
+ const state = streamStates.get(publisher);
69
+ if (!state) return;
70
+ const text = buildStreamDisplayText(state.full, state.prefix, state.continuationPrefix);
71
+ appendHistory({
72
+ ts: now(),
73
+ type: "bus",
74
+ text,
75
+ meta: { ...(meta || {}), stream_done: true, stream_reason: reason },
76
+ });
77
+ streamStates.delete(publisher);
78
+ }
79
+
80
+ function hasStream(publisher) {
81
+ return streamStates.has(publisher);
82
+ }
83
+
84
+ function markPendingDelivery(agentId, agentLabel) {
85
+ const key = agentId || agentLabel;
86
+ if (!key) return;
87
+ const existing = pendingDeliveries.get(key);
88
+ const state = existing || { count: 0, keys: new Set() };
89
+ state.count += 1;
90
+ if (agentId) {
91
+ pendingDeliveries.set(agentId, state);
92
+ state.keys.add(agentId);
93
+ }
94
+ if (agentLabel && agentLabel !== agentId) {
95
+ pendingDeliveries.set(agentLabel, state);
96
+ state.keys.add(agentLabel);
97
+ }
98
+ if (!agentId && !agentLabel) {
99
+ pendingDeliveries.set(key, state);
100
+ state.keys.add(key);
101
+ }
102
+ }
103
+
104
+ function getPendingState(publisher, displayName) {
105
+ if (publisher && pendingDeliveries.has(publisher)) {
106
+ return { key: publisher, state: pendingDeliveries.get(publisher) };
107
+ }
108
+ if (displayName && pendingDeliveries.has(displayName)) {
109
+ return { key: displayName, state: pendingDeliveries.get(displayName) };
110
+ }
111
+ return null;
112
+ }
113
+
114
+ function consumePendingDelivery(publisher, displayName) {
115
+ const hit = getPendingState(publisher, displayName);
116
+ if (!hit) return false;
117
+ const state = hit.state;
118
+ state.count -= 1;
119
+ if (state.count <= 0) {
120
+ for (const key of state.keys || []) {
121
+ pendingDeliveries.delete(key);
122
+ }
123
+ }
124
+ return true;
125
+ }
126
+
127
+ return {
128
+ beginStream,
129
+ appendStreamDelta,
130
+ finalizeStream,
131
+ hasStream,
132
+ markPendingDelivery,
133
+ getPendingState,
134
+ consumePendingDelivery,
135
+ };
136
+ }
137
+
138
+ module.exports = { createStreamTracker };
@@ -0,0 +1,70 @@
1
+ function neutralizeBlessedCommaTags(text) {
2
+ if (text == null) return "";
3
+ const raw = String(text);
4
+ if (!raw.includes("{")) return raw;
5
+ return raw.replace(/\{\/?[\w\-,;!#]*[;,][\w\-,;!#]*\}/g, (m) => {
6
+ const inner = m.slice(1, -1).replace(/[,;]/g, (ch) => `${ch} `);
7
+ return `{${inner}}`;
8
+ });
9
+ }
10
+
11
+ function escapeBlessed(text) {
12
+ if (text == null) return "";
13
+ const raw = neutralizeBlessedCommaTags(text);
14
+ if (!raw) return "";
15
+ // Avoid allowing payload to terminate escape mode.
16
+ const safe = raw.replace(/\{\/escape\}/g, "{open}/escape{close}");
17
+ return `{escape}${safe}{/escape}`;
18
+ }
19
+
20
+ function stripBlessedTags(text) {
21
+ if (text == null) return "";
22
+ return String(text).replace(/\{[^}]+\}/g, "");
23
+ }
24
+
25
+ function stripAnsi(text) {
26
+ if (text == null) return "";
27
+ return String(text).replace(/\x1b\[[0-9;]*m/g, "");
28
+ }
29
+
30
+ function truncateAnsi(text, maxVisible) {
31
+ if (text == null) return "";
32
+ if (maxVisible <= 0) return "";
33
+ const input = String(text);
34
+ let out = "";
35
+ let visible = 0;
36
+ let i = 0;
37
+ let truncated = false;
38
+ while (i < input.length && visible < maxVisible) {
39
+ if (input[i] === "\x1b") {
40
+ const match = input.slice(i).match(/^\x1b\[[0-9;]*m/);
41
+ if (match) {
42
+ out += match[0];
43
+ i += match[0].length;
44
+ continue;
45
+ }
46
+ }
47
+ out += input[i];
48
+ i += 1;
49
+ visible += 1;
50
+ }
51
+ if (i < input.length) truncated = true;
52
+ if (truncated) out += "\x1b[0m";
53
+ return out;
54
+ }
55
+
56
+ function truncateText(text, maxWidth) {
57
+ if (maxWidth <= 0) return "";
58
+ if (text.length <= maxWidth) return text;
59
+ if (maxWidth <= 3) return text.slice(0, maxWidth);
60
+ return `${text.slice(0, maxWidth - 3)}...`;
61
+ }
62
+
63
+ module.exports = {
64
+ neutralizeBlessedCommaTags,
65
+ escapeBlessed,
66
+ stripBlessedTags,
67
+ stripAnsi,
68
+ truncateAnsi,
69
+ truncateText,
70
+ };
@@ -0,0 +1,61 @@
1
+ const net = require("net");
2
+ const path = require("path");
3
+ const fs = require("fs");
4
+ const { spawn, spawnSync } = require("child_process");
5
+
6
+ function connectSocket(sockPath) {
7
+ return new Promise((resolve, reject) => {
8
+ const client = net.createConnection(sockPath, () => resolve(client));
9
+ client.on("error", reject);
10
+ });
11
+ }
12
+
13
+ function resolveProjectFile(projectRoot, relativePath, fallbackRelativePath) {
14
+ const local = path.join(projectRoot, relativePath);
15
+ if (fs.existsSync(local)) return local;
16
+ return path.join(__dirname, "..", "..", fallbackRelativePath);
17
+ }
18
+
19
+ function startDaemon(projectRoot, options = {}) {
20
+ const daemonBin = resolveProjectFile(projectRoot, path.join("bin", "ufoo.js"), path.join("bin", "ufoo.js"));
21
+ const env = options.forceResume
22
+ ? { ...process.env, UFOO_FORCE_RESUME: "1" }
23
+ : process.env;
24
+ const child = spawn(process.execPath, [daemonBin, "daemon", "--start"], {
25
+ detached: true,
26
+ stdio: "ignore",
27
+ cwd: projectRoot,
28
+ env,
29
+ });
30
+ child.unref();
31
+ }
32
+
33
+ function stopDaemon(projectRoot) {
34
+ const daemonBin = resolveProjectFile(projectRoot, path.join("bin", "ufoo.js"), path.join("bin", "ufoo.js"));
35
+ spawnSync(process.execPath, [daemonBin, "daemon", "--stop"], {
36
+ stdio: "ignore",
37
+ cwd: projectRoot,
38
+ });
39
+ }
40
+
41
+ async function connectWithRetry(sockPath, retries, delayMs) {
42
+ for (let i = 0; i < retries; i += 1) {
43
+ try {
44
+ // eslint-disable-next-line no-await-in-loop
45
+ const client = await connectSocket(sockPath);
46
+ return client;
47
+ } catch {
48
+ // eslint-disable-next-line no-await-in-loop
49
+ await new Promise((r) => setTimeout(r, delayMs));
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+
55
+ module.exports = {
56
+ connectSocket,
57
+ connectWithRetry,
58
+ resolveProjectFile,
59
+ startDaemon,
60
+ stopDaemon,
61
+ };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ async function runBusCoreCommand(eventBus, cmd, cmdArgs = []) {
4
+ switch (cmd) {
5
+ case "init":
6
+ await eventBus.init();
7
+ return {};
8
+ case "join":
9
+ return {
10
+ subscriber: await eventBus.join(cmdArgs[0], cmdArgs[1], cmdArgs[2]),
11
+ };
12
+ case "leave":
13
+ await eventBus.leave(cmdArgs[0]);
14
+ return {};
15
+ case "send":
16
+ {
17
+ const publisher = await eventBus.ensureJoined();
18
+ await eventBus.send(cmdArgs[0], cmdArgs[1], publisher);
19
+ }
20
+ return {};
21
+ case "broadcast":
22
+ {
23
+ const publisher = await eventBus.ensureJoined();
24
+ await eventBus.broadcast(cmdArgs[0], publisher);
25
+ }
26
+ return {};
27
+ case "wake":
28
+ {
29
+ const publisher = await eventBus.ensureJoined();
30
+ await eventBus.wake(cmdArgs[0], { publisher, reason: "remote" });
31
+ }
32
+ return {};
33
+ case "check":
34
+ await eventBus.check(cmdArgs[0]);
35
+ return {};
36
+ case "ack":
37
+ await eventBus.ack(cmdArgs[0]);
38
+ return {};
39
+ case "consume":
40
+ await eventBus.consume(cmdArgs[0], cmdArgs.includes("--from-beginning"));
41
+ return {};
42
+ case "status":
43
+ await eventBus.status();
44
+ return {};
45
+ case "resolve":
46
+ await eventBus.resolve(cmdArgs[0], cmdArgs[1]);
47
+ return {};
48
+ case "rename":
49
+ await eventBus.rename(cmdArgs[0], cmdArgs[1]);
50
+ return {};
51
+ case "whoami":
52
+ await eventBus.whoami();
53
+ return {};
54
+ default:
55
+ throw new Error(`Unknown bus subcommand: ${cmd}`);
56
+ }
57
+ }
58
+
59
+ module.exports = { runBusCoreCommand };
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+
3
+ const path = require("path");
4
+
5
+ function createUnknownCtxError(subcmd) {
6
+ const err = new Error(`Unknown ctx subcommand: ${subcmd}`);
7
+ err.code = "UFOO_CTX_UNKNOWN";
8
+ return err;
9
+ }
10
+
11
+ async function runCtxCommand(subcmd = "doctor", subargs = [], options = {}) {
12
+ const {
13
+ cwd = process.cwd(),
14
+ allowIndexNew = true,
15
+ updateDecisionIndexPaths = true,
16
+ } = options;
17
+
18
+ const DecisionsManager = require("../context/decisions");
19
+ const ContextDoctor = require("../context/doctor");
20
+ const SyncManager = require("../context/sync");
21
+
22
+ switch (subcmd) {
23
+ case "doctor": {
24
+ const doctor = new ContextDoctor(cwd);
25
+ const mode = subargs.includes("--project") ? "project" : "protocol";
26
+ const projectPath = mode === "project" ? subargs[subargs.indexOf("--project") + 1] : null;
27
+ await doctor.run({ mode, projectPath });
28
+ return;
29
+ }
30
+ case "lint": {
31
+ const doctor = new ContextDoctor(cwd);
32
+ const mode = subargs.includes("--project") ? "project" : "protocol";
33
+ const projectPath = mode === "project" ? subargs[subargs.indexOf("--project") + 1] : null;
34
+ if (mode === "project") {
35
+ doctor.lintProject(projectPath);
36
+ } else {
37
+ doctor.lintProtocol();
38
+ }
39
+ return;
40
+ }
41
+ case "decisions": {
42
+ const manager = new DecisionsManager(cwd);
43
+ const opts = {};
44
+
45
+ if (allowIndexNew) {
46
+ if (subargs[0] === "index" || subargs.includes("--index")) {
47
+ manager.writeIndex();
48
+ return;
49
+ }
50
+ if (subargs[0] === "new") {
51
+ const create = { title: "", author: "", status: "", nickname: "" };
52
+ for (let i = 1; i < subargs.length; i++) {
53
+ const arg = subargs[i];
54
+ if (arg === "--author") {
55
+ create.author = subargs[++i] || "";
56
+ continue;
57
+ }
58
+ if (arg === "--status") {
59
+ create.status = subargs[++i] || "";
60
+ continue;
61
+ }
62
+ if (arg === "--nickname") {
63
+ create.nickname = subargs[++i] || "";
64
+ continue;
65
+ }
66
+ if (!arg.startsWith("-")) {
67
+ create.title = create.title ? `${create.title} ${arg}` : arg;
68
+ }
69
+ }
70
+ manager.createDecision(create);
71
+ return;
72
+ }
73
+ }
74
+
75
+ for (let i = 0; i < subargs.length; i++) {
76
+ if (subargs[i] === "-n") opts.num = parseInt(subargs[++i]);
77
+ if (subargs[i] === "-s") opts.status = subargs[++i];
78
+ if (subargs[i] === "-l") opts.listOnly = true;
79
+ if (subargs[i] === "-a") opts.all = true;
80
+ if (subargs[i] === "-d") {
81
+ manager.decisionsDir = subargs[++i];
82
+ if (updateDecisionIndexPaths) {
83
+ manager.contextDir = path.dirname(manager.decisionsDir);
84
+ manager.indexFile = path.join(manager.contextDir, "decisions.jsonl");
85
+ }
86
+ }
87
+ if (subargs[i] === "--nickname") opts.nickname = subargs[++i];
88
+ }
89
+
90
+ if (opts.listOnly) {
91
+ manager.list({ status: opts.status || "open" });
92
+ } else {
93
+ manager.show({
94
+ status: opts.status || "open",
95
+ num: opts.num || 1,
96
+ all: opts.all || false,
97
+ });
98
+ }
99
+ return;
100
+ }
101
+ case "sync": {
102
+ const manager = new SyncManager(cwd);
103
+ const action = (subargs[0] || "list").toLowerCase();
104
+
105
+ if (action === "write" || action === "new" || action === "add") {
106
+ const create = {
107
+ message: "",
108
+ from: "",
109
+ for: "",
110
+ decision: "",
111
+ file: "",
112
+ tests: "",
113
+ verification: "",
114
+ risk: "",
115
+ next: "",
116
+ };
117
+
118
+ for (let i = 1; i < subargs.length; i++) {
119
+ const arg = subargs[i];
120
+ if (arg === "--from") {
121
+ create.from = subargs[++i] || "";
122
+ continue;
123
+ }
124
+ if (arg === "--for") {
125
+ create.for = subargs[++i] || "";
126
+ continue;
127
+ }
128
+ if (arg === "--decision") {
129
+ create.decision = subargs[++i] || "";
130
+ continue;
131
+ }
132
+ if (arg === "--file") {
133
+ create.file = subargs[++i] || "";
134
+ continue;
135
+ }
136
+ if (arg === "--tests") {
137
+ create.tests = subargs[++i] || "";
138
+ continue;
139
+ }
140
+ if (arg === "--verification") {
141
+ create.verification = subargs[++i] || "";
142
+ continue;
143
+ }
144
+ if (arg === "--risk") {
145
+ create.risk = subargs[++i] || "";
146
+ continue;
147
+ }
148
+ if (arg === "--next") {
149
+ create.next = subargs[++i] || "";
150
+ continue;
151
+ }
152
+ if (!arg.startsWith("-")) {
153
+ create.message = create.message ? `${create.message} ${arg}` : arg;
154
+ }
155
+ }
156
+
157
+ manager.write(create);
158
+ return;
159
+ }
160
+
161
+ if (action === "list" || action === "show" || action === "-l") {
162
+ const opts = {
163
+ num: 20,
164
+ for: "",
165
+ from: "",
166
+ };
167
+
168
+ for (let i = 1; i < subargs.length; i++) {
169
+ const arg = subargs[i];
170
+ if (arg === "-n" || arg === "--num") {
171
+ opts.num = parseInt(subargs[++i], 10);
172
+ continue;
173
+ }
174
+ if (arg === "--for") {
175
+ opts.for = subargs[++i] || "";
176
+ continue;
177
+ }
178
+ if (arg === "--from") {
179
+ opts.from = subargs[++i] || "";
180
+ }
181
+ }
182
+
183
+ manager.list(opts);
184
+ return;
185
+ }
186
+
187
+ throw new Error(
188
+ `Unknown ctx sync action: ${action}. Use 'list' or 'write'.`
189
+ );
190
+ }
191
+ default:
192
+ throw createUnknownCtxError(subcmd);
193
+ }
194
+ }
195
+
196
+ module.exports = {
197
+ runCtxCommand,
198
+ createUnknownCtxError,
199
+ };