@zq-silk/yui 0.0.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 (95) hide show
  1. package/ARCHITECTURE.md +141 -0
  2. package/LICENSE +21 -0
  3. package/README.md +211 -0
  4. package/dist/agent/adapterCatalog.js +10 -0
  5. package/dist/agent/agent.js +89 -0
  6. package/dist/agent/agentRegistry.js +10 -0
  7. package/dist/agent/argumentPolicy.js +80 -0
  8. package/dist/brief/taskBrief.js +37 -0
  9. package/dist/cli/commandCatalog.js +647 -0
  10. package/dist/cli/completion.js +111 -0
  11. package/dist/cli/completionWizard.js +143 -0
  12. package/dist/cli/dynamicCompletion.js +48 -0
  13. package/dist/cli/helpRenderer.js +32 -0
  14. package/dist/cli/interactionCandidates.js +139 -0
  15. package/dist/cli/interactionPolicy.js +389 -0
  16. package/dist/cli/interactiveSelection.js +185 -0
  17. package/dist/cli/invocationRouter.js +51 -0
  18. package/dist/cli/roleOptionCatalog.js +67 -0
  19. package/dist/cli/roleWizard.js +546 -0
  20. package/dist/cli/selectionPorts.js +1 -0
  21. package/dist/cli/updateCommand.js +22 -0
  22. package/dist/cli.js +402 -0
  23. package/dist/commands/agentCommands.js +196 -0
  24. package/dist/commands/globalRoleCommands.js +367 -0
  25. package/dist/commands/jobCommands.js +100 -0
  26. package/dist/commands/operatorCommands.js +38 -0
  27. package/dist/commands/repositoryCommands.js +86 -0
  28. package/dist/commands/roleConfiguration.js +201 -0
  29. package/dist/commands/taskCommands.js +1344 -0
  30. package/dist/commands/taskContextCommand.js +215 -0
  31. package/dist/commands/taskInputCommands.js +423 -0
  32. package/dist/commands/taskRoleRuntimeStatus.js +152 -0
  33. package/dist/completion/completionInstaller.js +168 -0
  34. package/dist/completion/completionPort.js +1 -0
  35. package/dist/completion/completionState.js +137 -0
  36. package/dist/completion/completionWizard.js +125 -0
  37. package/dist/completion/fileCompletionManager.js +51 -0
  38. package/dist/config/yuiConfig.js +17 -0
  39. package/dist/context/dispatchContext.js +74 -0
  40. package/dist/controller/clientRuntime.js +215 -0
  41. package/dist/controller/controller.js +158 -0
  42. package/dist/controller/controllerMain.js +37 -0
  43. package/dist/controller/fileSchedulerStoreAdapter.js +322 -0
  44. package/dist/controller/runtime.js +31 -0
  45. package/dist/controller/sessionNotify.js +136 -0
  46. package/dist/core/controllerClient.js +127 -0
  47. package/dist/core/controllerServer.js +269 -0
  48. package/dist/core/protocol.js +169 -0
  49. package/dist/decision/decision.js +42 -0
  50. package/dist/doctor/doctor.js +229 -0
  51. package/dist/errors/cliError.js +38 -0
  52. package/dist/event/taskEvent.js +44 -0
  53. package/dist/executor/agentAdapter.js +338 -0
  54. package/dist/executor/agentExecutor.js +144 -0
  55. package/dist/executor/executorRegistry.js +101 -0
  56. package/dist/executor/fileRoleLaunchPlanner.js +156 -0
  57. package/dist/executor/launchPlan.js +16 -0
  58. package/dist/input/inputRequest.js +326 -0
  59. package/dist/message/message.js +69 -0
  60. package/dist/milestone/milestone.js +27 -0
  61. package/dist/operator/operatorContext.js +66 -0
  62. package/dist/output/rolePresentation.js +82 -0
  63. package/dist/output/table.js +77 -0
  64. package/dist/output/terminal.js +198 -0
  65. package/dist/repository/gitWorkspace.js +210 -0
  66. package/dist/repository/repository.js +55 -0
  67. package/dist/repository/taskWorkspacePreparer.js +256 -0
  68. package/dist/role/role.js +246 -0
  69. package/dist/role/systemRoles.js +20 -0
  70. package/dist/run/agentRun.js +102 -0
  71. package/dist/scheduler/activeRoleRunDelivery.js +94 -0
  72. package/dist/scheduler/archivedTaskRuntime.js +12 -0
  73. package/dist/scheduler/leaderFailure.js +18 -0
  74. package/dist/scheduler/leaderWakeupProcessor.js +143 -0
  75. package/dist/scheduler/operatorInputNotificationProcessor.js +85 -0
  76. package/dist/scheduler/operatorNotification.js +17 -0
  77. package/dist/scheduler/pendingWakeup.js +33 -0
  78. package/dist/scheduler/ports.js +1 -0
  79. package/dist/scheduler/roleRunLiveness.js +41 -0
  80. package/dist/scheduler/wakeupQueue.js +13 -0
  81. package/dist/setup/setupCommand.js +317 -0
  82. package/dist/storage/durableFile.js +38 -0
  83. package/dist/storage/storageSchema.js +259 -0
  84. package/dist/storage/taskStore.js +1032 -0
  85. package/dist/task/task.js +216 -0
  86. package/dist/tmux/commandExecutor.js +69 -0
  87. package/dist/tmux/terminalHandoff.js +17 -0
  88. package/dist/tmux/tmuxManager.js +408 -0
  89. package/dist/workItem/workItem.js +45 -0
  90. package/dist/worktree/roleWorkspace.js +62 -0
  91. package/i18n/README.zh-CN.md +205 -0
  92. package/package.json +47 -0
  93. package/skills/yui-leader/SKILL.md +72 -0
  94. package/skills/yui-operator/SKILL.md +57 -0
  95. package/skills/yui-worker/SKILL.md +31 -0
@@ -0,0 +1,216 @@
1
+ export function createTask(id, title, now, metadata = {}) {
2
+ const timestamp = now.toISOString();
3
+ return {
4
+ schemaVersion: 1,
5
+ id: requireSafeIdentity(id, "Task id"),
6
+ title: requireText(title, "Task title"),
7
+ ...cloneMetadata(metadata),
8
+ status: "draft",
9
+ createdAt: timestamp,
10
+ updatedAt: timestamp
11
+ };
12
+ }
13
+ export function activateTask(task, now) {
14
+ if (task.status === "archived")
15
+ throw new Error(`Cannot activate archived Task: ${task.id}.`);
16
+ if (task.status === "completed")
17
+ throw new Error(`Cannot activate completed Task ${task.id}; reopen it instead.`);
18
+ if (task.status === "active")
19
+ return task;
20
+ return { ...task, status: "active", updatedAt: now.toISOString() };
21
+ }
22
+ export function completeTask(task, now, completion) {
23
+ if (task.status === "completed")
24
+ return task;
25
+ if (task.status !== "active") {
26
+ throw new Error(`Only an active Task can be completed: ${task.id}.`);
27
+ }
28
+ const timestamp = now.toISOString();
29
+ return {
30
+ ...task,
31
+ status: "completed",
32
+ completedAt: timestamp,
33
+ completedBy: completion.by,
34
+ completionSummary: requireText(completion.summary, "Task completion summary"),
35
+ updatedAt: timestamp
36
+ };
37
+ }
38
+ export function reopenTask(task, now) {
39
+ if (task.status === "archived")
40
+ throw new Error(`Cannot reopen archived Task: ${task.id}.`);
41
+ if (task.status !== "completed") {
42
+ throw new Error(`Only a completed Task can be reopened: ${task.id}.`);
43
+ }
44
+ const { completedAt: _completedAt, completedBy: _completedBy, completionSummary: _completionSummary, ...reopened } = task;
45
+ return { ...reopened, status: "active", updatedAt: now.toISOString() };
46
+ }
47
+ export function archiveTask(task, now, archive) {
48
+ if (task.status === "archived")
49
+ return task;
50
+ const timestamp = now.toISOString();
51
+ return {
52
+ ...task,
53
+ status: "archived",
54
+ archivedAt: timestamp,
55
+ archivedBy: archive?.by ?? "user",
56
+ ...(archive?.reason === undefined ? {} : { archiveReason: archive.reason.trim() }),
57
+ ...(archive?.summary === undefined ? {} : { archiveSummary: archive.summary.trim() }),
58
+ updatedAt: timestamp
59
+ };
60
+ }
61
+ export function updateTaskArchived(task, archived, now, archive) {
62
+ if (archived)
63
+ return archiveTask(task, now, archive);
64
+ if (task.status === "archived") {
65
+ throw new Error(`Cannot reopen archived Task: ${task.id}.`);
66
+ }
67
+ return { ...task, updatedAt: now.toISOString() };
68
+ }
69
+ export function updateTaskMetadata(task, metadata, now) {
70
+ const updated = { ...task, updatedAt: now.toISOString() };
71
+ if (metadata.title !== undefined)
72
+ updated.title = requireText(metadata.title, "Task title");
73
+ applyOptional(updated, "description", metadata.description);
74
+ applyOptional(updated, "priority", metadata.priority);
75
+ applyOptional(updated, "tags", metadata.tags === undefined || metadata.tags === null
76
+ ? metadata.tags
77
+ : [...metadata.tags]);
78
+ applyOptional(updated, "dueAt", metadata.dueAt);
79
+ if (metadata.repositoryId !== undefined) {
80
+ updated.repositoryId = requireSafeIdentity(metadata.repositoryId, "Repository id");
81
+ }
82
+ if (metadata.baseRef !== undefined)
83
+ updated.baseRef = requireText(metadata.baseRef, "Task base ref");
84
+ if (metadata.cwd !== undefined)
85
+ updated.cwd = requireText(metadata.cwd, "Task workspace");
86
+ validateRepositorySelection(updated);
87
+ return updated;
88
+ }
89
+ function applyOptional(task, key, value) {
90
+ if (value === undefined)
91
+ return;
92
+ if (value === null)
93
+ delete task[key];
94
+ else
95
+ task[key] = value;
96
+ }
97
+ export function updateTaskWorkspace(task, cwd, now) {
98
+ return { ...task, cwd: requireText(cwd, "Task workspace"), updatedAt: now.toISOString() };
99
+ }
100
+ export function isTaskArchived(task) {
101
+ return task.status === "archived";
102
+ }
103
+ export function validateTask(task) {
104
+ if (task.schemaVersion !== 1)
105
+ throw new Error("Task must use schemaVersion 1.");
106
+ requireSafeIdentity(task.id, "Task id");
107
+ requireText(task.title, "Task title");
108
+ if (!["draft", "active", "completed", "archived"].includes(task.status)) {
109
+ throw new Error(`Task status is invalid: ${String(task.status)}.`);
110
+ }
111
+ requireTimestamp(task.createdAt, "Task createdAt");
112
+ requireTimestamp(task.updatedAt, "Task updatedAt");
113
+ if (Date.parse(task.updatedAt) < Date.parse(task.createdAt)) {
114
+ throw new Error("Task updatedAt cannot precede createdAt.");
115
+ }
116
+ if (task.priority !== undefined
117
+ && !["low", "medium", "high", "urgent"].includes(task.priority)) {
118
+ throw new Error(`Task priority is invalid: ${String(task.priority)}.`);
119
+ }
120
+ if (task.description !== undefined)
121
+ requireText(task.description, "Task description");
122
+ if (task.tags !== undefined) {
123
+ if (!Array.isArray(task.tags))
124
+ throw new Error("Task tags are invalid.");
125
+ for (const tag of task.tags)
126
+ requireText(tag, "Task tag");
127
+ }
128
+ if (task.dueAt !== undefined)
129
+ requireTimestamp(task.dueAt, "Task dueAt");
130
+ if (task.repositoryId !== undefined)
131
+ requireSafeIdentity(task.repositoryId, "Repository id");
132
+ if (task.baseRef !== undefined)
133
+ requireText(task.baseRef, "Task base ref");
134
+ if (task.cwd !== undefined)
135
+ requireText(task.cwd, "Task workspace");
136
+ validateRepositorySelection(task);
137
+ const completionFields = [task.completedAt, task.completedBy, task.completionSummary];
138
+ const hasAnyCompletion = completionFields.some((value) => value !== undefined);
139
+ const hasAllCompletion = completionFields.every((value) => value !== undefined);
140
+ if (hasAnyCompletion && !hasAllCompletion) {
141
+ throw new Error("Task completion metadata must include completedAt, completedBy, and completionSummary.");
142
+ }
143
+ if (hasAllCompletion) {
144
+ requireTimestamp(task.completedAt, "Task completedAt");
145
+ if (!["user", "operator", "leader"].includes(task.completedBy)) {
146
+ throw new Error(`Task completedBy is invalid: ${String(task.completedBy)}.`);
147
+ }
148
+ requireText(task.completionSummary, "Task completion summary");
149
+ }
150
+ if (task.status === "completed" && !hasAllCompletion) {
151
+ throw new Error("A completed Task requires completedAt, completedBy, and completionSummary.");
152
+ }
153
+ if ((task.status === "draft" || task.status === "active") && hasAnyCompletion) {
154
+ throw new Error(`Task completion metadata is invalid for ${task.status} status.`);
155
+ }
156
+ const hasAnyArchive = [task.archivedAt, task.archivedBy, task.archiveReason, task.archiveSummary]
157
+ .some((value) => value !== undefined);
158
+ if (task.status === "archived") {
159
+ if (task.archivedAt === undefined || task.archivedBy === undefined) {
160
+ throw new Error("An archived Task requires archivedAt and archivedBy.");
161
+ }
162
+ requireTimestamp(task.archivedAt, "Task archivedAt");
163
+ if (!["user", "operator", "leader"].includes(task.archivedBy)) {
164
+ throw new Error(`Task archivedBy is invalid: ${String(task.archivedBy)}.`);
165
+ }
166
+ if (task.archiveReason !== undefined)
167
+ requireText(task.archiveReason, "Task archive reason");
168
+ if (task.archiveSummary !== undefined)
169
+ requireText(task.archiveSummary, "Task archive summary");
170
+ }
171
+ else if (hasAnyArchive) {
172
+ throw new Error(`Task archive metadata is invalid for ${task.status} status.`);
173
+ }
174
+ return task;
175
+ }
176
+ function cloneMetadata(metadata) {
177
+ const cloned = {
178
+ ...(metadata.description === undefined ? {} : { description: metadata.description }),
179
+ ...(metadata.priority === undefined ? {} : { priority: metadata.priority }),
180
+ ...(metadata.tags === undefined ? {} : { tags: [...metadata.tags] }),
181
+ ...(metadata.dueAt === undefined ? {} : { dueAt: metadata.dueAt }),
182
+ ...(metadata.repositoryId === undefined
183
+ ? {}
184
+ : { repositoryId: requireSafeIdentity(metadata.repositoryId, "Repository id") }),
185
+ ...(metadata.baseRef === undefined ? {} : { baseRef: requireText(metadata.baseRef, "Task base ref") }),
186
+ ...(metadata.cwd === undefined ? {} : { cwd: requireText(metadata.cwd, "Task workspace") })
187
+ };
188
+ validateRepositorySelection(cloned);
189
+ return cloned;
190
+ }
191
+ function validateRepositorySelection(value) {
192
+ if (value.baseRef !== undefined && value.repositoryId === undefined) {
193
+ throw new Error("Task base ref requires a repository.");
194
+ }
195
+ }
196
+ function requireSafeIdentity(value, label) {
197
+ const normalized = requireText(value, label);
198
+ if (["__proto__", "prototype", "constructor", ".", ".."].includes(normalized)
199
+ || /[\/\\\0]/.test(normalized)) {
200
+ throw new Error(`${label} is invalid.`);
201
+ }
202
+ return normalized;
203
+ }
204
+ function requireText(value, label) {
205
+ if (typeof value !== "string" || value.includes("\0"))
206
+ throw new Error(`${label} is invalid.`);
207
+ const normalized = value.trim();
208
+ if (normalized.length === 0)
209
+ throw new Error(`${label} is required.`);
210
+ return normalized;
211
+ }
212
+ function requireTimestamp(value, label) {
213
+ if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) {
214
+ throw new Error(`${label} is invalid.`);
215
+ }
216
+ }
@@ -0,0 +1,69 @@
1
+ import { spawnSync } from "node:child_process";
2
+ export class CommandExecutionError extends Error {
3
+ code;
4
+ exitStatus;
5
+ stderr;
6
+ constructor(code, exitStatus, stderr = "") {
7
+ super(commandExecutionMessage(code));
8
+ this.code = code;
9
+ this.exitStatus = exitStatus;
10
+ this.stderr = stderr;
11
+ this.name = "CommandExecutionError";
12
+ }
13
+ }
14
+ export class NodeCommandExecutor {
15
+ run(command, args, options = {}) {
16
+ const common = options.timeoutMs === undefined ? {} : { timeout: options.timeoutMs };
17
+ const result = options.inheritStdio === true
18
+ ? spawnSync(command, args, { ...common, stdio: "inherit" })
19
+ : spawnSync(command, args, {
20
+ ...common,
21
+ encoding: "utf8",
22
+ stdio: ["ignore", "pipe", "pipe"]
23
+ });
24
+ if (result.error !== undefined || result.status !== 0) {
25
+ throw stableCommandExecutionError(result);
26
+ }
27
+ return options.inheritStdio === true ? "" : String(result.stdout ?? "");
28
+ }
29
+ }
30
+ function stableCommandExecutionError(error) {
31
+ const details = errorDetails(error);
32
+ if (details.code === "ENOENT") {
33
+ return new CommandExecutionError("COMMAND_NOT_FOUND", details.exitStatus, details.stderr);
34
+ }
35
+ if (details.code === "ETIMEDOUT"
36
+ || details.signal === "SIGTERM"
37
+ || details.signal === "SIGKILL") {
38
+ return new CommandExecutionError("COMMAND_TIMED_OUT", details.exitStatus, details.stderr);
39
+ }
40
+ return new CommandExecutionError("COMMAND_FAILED", details.exitStatus, details.stderr);
41
+ }
42
+ function errorDetails(error) {
43
+ if (typeof error !== "object" || error === null)
44
+ return { stderr: "" };
45
+ const candidate = error;
46
+ const nested = typeof candidate.error === "object" && candidate.error !== null
47
+ ? candidate.error
48
+ : {};
49
+ return {
50
+ ...(typeof candidate.code === "string"
51
+ ? { code: candidate.code }
52
+ : typeof nested.code === "string" ? { code: nested.code } : {}),
53
+ ...(typeof candidate.signal === "string" ? { signal: candidate.signal } : {}),
54
+ ...(typeof candidate.status === "number" ? { exitStatus: candidate.status } : {}),
55
+ stderr: typeof candidate.stderr === "string"
56
+ ? candidate.stderr
57
+ : Buffer.isBuffer(candidate.stderr) ? candidate.stderr.toString("utf8") : ""
58
+ };
59
+ }
60
+ function commandExecutionMessage(code) {
61
+ switch (code) {
62
+ case "COMMAND_NOT_FOUND":
63
+ return "Command was not found.";
64
+ case "COMMAND_TIMED_OUT":
65
+ return "Command execution timed out.";
66
+ case "COMMAND_FAILED":
67
+ return "Command execution failed.";
68
+ }
69
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Release every Yui claim on a terminal before a foreground tmux client
3
+ * inherits it. The optional close callback is expected to be idempotent.
4
+ */
5
+ export function handoffTerminal(input, closeInteractiveInput = () => { }) {
6
+ try {
7
+ closeInteractiveInput();
8
+ }
9
+ finally {
10
+ if (input.isTTY === true
11
+ && input.isRaw === true
12
+ && typeof input.setRawMode === "function") {
13
+ input.setRawMode(false);
14
+ }
15
+ input.pause();
16
+ }
17
+ }