@quiel/cli 0.6.0 → 0.6.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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/dist/cli.js +101 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -111,6 +111,8 @@ For Claude Code, `Stop` gets **two** commands, and they are not duplicates: one
111
111
 
112
112
  **Your token goes into none of them.** It is stored separately in `~/.quiel/credentials.json` with `0600` permissions, outside the repository. `.quiel.json` — the file that *is* committed — holds only the platform address, the project key, the agent id, its roles and which agent program you chose.
113
113
 
114
+ **After upgrading the package, run `quiel init` again.** `npm i -g` replaces the binary but does not touch your agent's configuration — and a new release may add a hook. Version 0.6.0 added the task watcher this way, and an agent upgraded without re-running `init` went quiet instead of idling. The client now notices and keeps working the old way, but `init` is what turns the new behaviour on. Running the MCP server at the time? Reconnect it (`/mcp` in Claude Code) — the old process keeps answering until you do.
115
+
114
116
  Existing configuration is merged, not overwritten: other MCP servers, other hooks and your own comments survive. Running `init` again is the normal way to reconnect an agent, and it does not duplicate anything.
115
117
 
116
118
  ## Commands
package/dist/cli.js CHANGED
@@ -3436,6 +3436,36 @@ var ClaudeSettingsSchema = z29.object({
3436
3436
  env: z29.record(z29.string(), z29.string()).optional()
3437
3437
  }).passthrough();
3438
3438
  var CLAUDE_SETTINGS_RELATIVE_PATH = join2(".claude", "settings.json");
3439
+ function getClaudeSettingsPath(dir) {
3440
+ return join2(dir, ".claude", "settings.json");
3441
+ }
3442
+ function readClaudeSettings(dir) {
3443
+ const path = getClaudeSettingsPath(dir);
3444
+ if (!existsSync2(path)) return { ok: true, settings: {} };
3445
+ let raw;
3446
+ try {
3447
+ raw = readFileSync2(path, "utf8");
3448
+ } catch (error) {
3449
+ return { ok: false, message: `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C ${path}: ${String(error)}` };
3450
+ }
3451
+ let parsedJson;
3452
+ try {
3453
+ parsedJson = raw.trim().length === 0 ? {} : JSON.parse(raw);
3454
+ } catch {
3455
+ return {
3456
+ ok: false,
3457
+ message: `${path} \u2014 \u043D\u0435 \u0432\u0430\u043B\u0438\u0434\u043D\u044B\u0439 JSON. \u041F\u043E\u043F\u0440\u0430\u0432\u044C \u0432\u0440\u0443\u0447\u043D\u0443\u044E (\u044D\u0442\u043E \u0444\u0430\u0439\u043B Claude Code, \u043D\u0435 \u043D\u0430\u0448) \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438 "quiel init".`
3458
+ };
3459
+ }
3460
+ const parsed = ClaudeSettingsSchema.safeParse(parsedJson);
3461
+ if (!parsed.success) {
3462
+ return {
3463
+ ok: false,
3464
+ message: `${path} \u043D\u0435 \u043F\u0440\u043E\u0445\u043E\u0434\u0438\u0442 \u043E\u0436\u0438\u0434\u0430\u0435\u043C\u0443\u044E \u0444\u043E\u0440\u043C\u0443: ${parsed.error.issues.map((i) => i.message).join("; ")}`
3465
+ };
3466
+ }
3467
+ return { ok: true, settings: parsed.data };
3468
+ }
3439
3469
  var CLI_BIN = "quiel";
3440
3470
  var HOOK_SPECS = [
3441
3471
  { event: "SessionStart", command: `${CLI_BIN} hook session-start` },
@@ -3662,6 +3692,16 @@ var ClientStateSchema = z30.object({
3662
3692
  * попытка что-то принесла.
3663
3693
  */
3664
3694
  queueEmptyAt: z30.string().nullable().default(null),
3695
+ /**
3696
+ * Когда `next_task` в последний раз получил ОТКАЗ сервера.
3697
+ *
3698
+ * Отличается от пустой очереди принципиально: там работы нет, здесь её
3699
+ * не удалось спросить. Без этого поля хук видел «задачи не сдана нет,
3700
+ * очередь не пуста» и гнал звать `next_task` снова — а тот снова
3701
+ * падал. Агент не мог ни работать, ни остановиться: девять отказов
3702
+ * подряд за пятнадцать минут на боевом проекте 24 сентября 2026.
3703
+ */
3704
+ claimFailedAt: z30.string().nullable().default(null),
3665
3705
  /** Кэш стоп-паттернов/правил доверия для `PreToolUse` фазы 4. */
3666
3706
  trust: TrustCacheSchema.default(null),
3667
3707
  /** Снимок задачи на момент последнего `PreCompact` — пишет src/hooks/pre-compact.ts, читает SessionStart(source="compact"), чтобы напомнить модели, чем она занималась. */
@@ -5937,7 +5977,7 @@ ${question.answerMd ?? ""}${describeAnswerAttachments(question)}`;
5937
5977
  // package.json
5938
5978
  var package_default = {
5939
5979
  name: "@quiel/cli",
5940
- version: "0.6.0",
5980
+ version: "0.6.2",
5941
5981
  description: "CLI \u0438 MCP-\u0441\u0435\u0440\u0432\u0435\u0440 Quiel: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0430\u0433\u0435\u043D\u0442\u0430 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0430 (Claude Code, Codex, Cursor, OpenCode) \u043A \u043F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0435",
5942
5982
  homepage: "https://quiel.app",
5943
5983
  repository: {
@@ -6118,12 +6158,22 @@ function describeNoWorktree(reason) {
6118
6158
  }
6119
6159
 
6120
6160
  // src/mcp/next-task.ts
6161
+ var DEFAULT_WAIT_SEC = 90;
6162
+ var MAX_WAIT_SEC = 110;
6121
6163
  var nextTaskInputShape = {
6122
- waitSec: taskClaimPayloadSchema.shape.waitSec.optional().describe(
6123
- "\u0421\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u0435\u043A\u0443\u043D\u0434 \u0436\u0434\u0430\u0442\u044C \u043F\u043E\u044F\u0432\u043B\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438, 0..600 (\xA711.1 \u0422\u0417). \u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 300 \u2014 \u043A\u0430\u043A claimTimeoutSec \u0434\u0438\u0441\u043F\u0435\u0442\u0447\u0435\u0440\u0430 (\xA79.1 \u0422\u0417)."
6164
+ // ПОТОЛОК УЖЕ, ЧЕМ У СЕРВЕРА, И ЭТО НАМЕРЕННО. Схема события
6165
+ // допускает до 600 с — столько держит long-poll диспетчер. Но у
6166
+ // Claude Code вызов MCP умирает по таймауту на 600 с, а ожидание ack
6167
+ // здесь ещё на 15 с длиннее. Попроси агент 600 — и есть окно, в
6168
+ // котором сервер ВЫДАЛ задачу, а вызов уже убит: задача ушла в
6169
+ // IN_PROGRESS, агент об этом не знает, аренда висит до истечения.
6170
+ // Найдено владельцем на боевом проекте 24 сентября 2026.
6171
+ waitSec: taskClaimPayloadSchema.shape.waitSec.refine((value) => value <= MAX_WAIT_SEC, {
6172
+ message: `\u0411\u043E\u043B\u044C\u0448\u0435 ${MAX_WAIT_SEC} \u0441 \u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F: \u0432\u044B\u0437\u043E\u0432 MCP \u043D\u0435 \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0451\u0442 \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435. \u0417\u0430\u0432\u0435\u0440\u0448\u0438 \u0445\u043E\u0434 \u2014 \u0431\u0443\u0434\u0438\u043B\u044C\u043D\u0438\u043A \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442, \u043A\u043E\u0433\u0434\u0430 \u043F\u043E\u044F\u0432\u0438\u0442\u0441\u044F \u0437\u0430\u0434\u0430\u0447\u0430.`
6173
+ }).optional().describe(
6174
+ "\u0421\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u0435\u043A\u0443\u043D\u0434 \u0436\u0434\u0430\u0442\u044C \u043F\u043E\u044F\u0432\u043B\u0435\u043D\u0438\u044F \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438, 0..110. \u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 90. \u0411\u043E\u043B\u044C\u0448\u0435 \u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043D\u0435\u043B\u044C\u0437\u044F \u0438 \u043D\u0435 \u043D\u0443\u0436\u043D\u043E: Claude Code \u0443\u0432\u043E\u0434\u0438\u0442 \u0432\u044B\u0437\u043E\u0432 MCP \u0432 \u0444\u043E\u043D \u043F\u0440\u0438\u043C\u0435\u0440\u043D\u043E \u0447\u0435\u0440\u0435\u0437 120 \u0441\u0435\u043A\u0443\u043D\u0434, \u0430 \u0434\u043E\u043B\u0433\u043E\u0435 \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435 \u2014 \u0440\u0430\u0431\u043E\u0442\u0430 \u0431\u0443\u0434\u0438\u043B\u044C\u043D\u0438\u043A\u0430 (quiel wait-for-task), \u0430 \u043D\u0435 \u044D\u0442\u043E\u0433\u043E \u0442\u0443\u043B\u0430. \u041F\u0443\u0441\u0442\u043E\u0439 \u043E\u0442\u0432\u0435\u0442 \u2014 \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434: \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0445\u043E\u0434, \u0442\u0435\u0431\u044F \u0440\u0430\u0437\u0431\u0443\u0434\u044F\u0442."
6124
6175
  )
6125
6176
  };
6126
- var DEFAULT_WAIT_SEC = 90;
6127
6177
  var CLAIM_ACK_BUFFER_MS = 15e3;
6128
6178
  async function nextTaskHandler(args, runtime) {
6129
6179
  const { client, stateFilePath } = runtime;
@@ -6147,9 +6197,12 @@ async function nextTaskHandler(args, runtime) {
6147
6197
  taskClaimAckSchema,
6148
6198
  waitSec * 1e3 + CLAIM_ACK_BUFFER_MS
6149
6199
  );
6150
- if (!claimed.ok) return errorResult(claimed.message);
6200
+ if (!claimed.ok) {
6201
+ updateState({ claimFailedAt: (/* @__PURE__ */ new Date()).toISOString() }, stateFilePath);
6202
+ return errorResult(claimed.message);
6203
+ }
6151
6204
  if ("empty" in claimed.data) {
6152
- updateState({ queueEmptyAt: (/* @__PURE__ */ new Date()).toISOString() }, stateFilePath);
6205
+ updateState({ queueEmptyAt: (/* @__PURE__ */ new Date()).toISOString(), claimFailedAt: null }, stateFilePath);
6153
6206
  return textResult(
6154
6207
  `\u0417\u0430\u0434\u0430\u0447 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u0441\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0442 (\u0436\u0434\u0430\u043B ${waitSec} \u0441). \u042D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0438\u0441\u0445\u043E\u0434, \u043D\u0435 \u043E\u0448\u0438\u0431\u043A\u0430. \u041C\u043E\u0436\u0435\u0448\u044C \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C \u0445\u043E\u0434 \u2014 \u0445\u0443\u043A \u0442\u0435\u0431\u044F \u043D\u0435 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442, \u043F\u043E\u043A\u0430 \u043E\u0447\u0435\u0440\u0435\u0434\u044C \u043F\u0443\u0441\u0442\u0430.`
6155
6208
  );
@@ -6165,9 +6218,10 @@ async function nextTaskHandler(args, runtime) {
6165
6218
  branchName: task.project.branchName,
6166
6219
  commitPrefix: task.project.commitPrefix
6167
6220
  },
6168
- // Очередь оказалась не пуста — пометка снимается, иначе хук
6169
- // продолжал бы считать, что работы нет.
6170
- queueEmptyAt: null
6221
+ // Очередь оказалась не пуста — пометки снимаются, иначе хук
6222
+ // продолжал бы считать, что работы нет или что сервер лежит.
6223
+ queueEmptyAt: null,
6224
+ claimFailedAt: null
6171
6225
  },
6172
6226
  stateFilePath
6173
6227
  );
@@ -6247,11 +6301,12 @@ async function linkTasksHandler(args, runtime) {
6247
6301
  taskLinkAckSchema
6248
6302
  );
6249
6303
  if (!result.ok) return errorResult(result.message);
6304
+ const relation = parsedArgs.data.kind === "RELATES" ? `\u0441\u0432\u044F\u0437\u0430\u043D\u0430 \u0441 ${args.blockedId} (RELATES, \u043D\u0430 \u043E\u0447\u0435\u0440\u0435\u0434\u044C \u043D\u0435 \u0432\u043B\u0438\u044F\u0435\u0442)` : `\u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 ${args.blockedId} (BLOCKS)`;
6250
6305
  if (!result.data.linked) {
6251
- return textResult(`\u0421\u0432\u044F\u0437\u044C \u0443\u0436\u0435 \u0431\u044B\u043B\u0430: ${args.blockerId} \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 ${args.blockedId}.`);
6306
+ return textResult(`\u0421\u0432\u044F\u0437\u044C \u0443\u0436\u0435 \u0431\u044B\u043B\u0430: ${args.blockerId} ${relation}.`);
6252
6307
  }
6253
6308
  const stopped = result.data.blockedNow ? " \u0417\u0430\u0434\u0430\u0447\u0430 \u0431\u044B\u043B\u0430 \u0432 \u0440\u0430\u0431\u043E\u0442\u0435 \u0438 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u043E\u043D\u0430 \u0436\u0434\u0451\u0442 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0443\u044E." : "";
6254
- return textResult(`\u0413\u043E\u0442\u043E\u0432\u043E: ${args.blockerId} \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 ${args.blockedId}.${stopped}`);
6309
+ return textResult(`\u0413\u043E\u0442\u043E\u0432\u043E: ${args.blockerId} ${relation}.${stopped}`);
6255
6310
  }
6256
6311
  var unlinkTasksInputShape = {
6257
6312
  blockerId: taskUnlinkPayloadSchema.shape.blockerId.describe("ID \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438."),
@@ -6268,7 +6323,7 @@ async function unlinkTasksHandler(args, runtime) {
6268
6323
  );
6269
6324
  if (!result.ok) return errorResult(result.message);
6270
6325
  return textResult(
6271
- result.data.unlinked ? `\u0421\u0432\u044F\u0437\u044C \u0441\u043D\u044F\u0442\u0430: ${args.blockerId} \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u0435\u0442 ${args.blockedId}.` : `\u0422\u0430\u043A\u043E\u0439 \u0441\u0432\u044F\u0437\u0438 \u043D\u0435 \u0431\u044B\u043B\u043E: ${args.blockerId} \u043D\u0435 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043B ${args.blockedId}.`
6326
+ result.data.unlinked ? `\u0421\u0432\u044F\u0437\u044C \u0441\u043D\u044F\u0442\u0430: \u043C\u0435\u0436\u0434\u0443 ${args.blockerId} \u0438 ${args.blockedId} \u0435\u0451 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0442.` : `\u0422\u0430\u043A\u043E\u0439 \u0441\u0432\u044F\u0437\u0438 \u043D\u0435 \u0431\u044B\u043B\u043E: \u043C\u0435\u0436\u0434\u0443 ${args.blockerId} \u0438 ${args.blockedId}.`
6272
6327
  );
6273
6328
  }
6274
6329
  var planTaskInputShape = {
@@ -7443,7 +7498,7 @@ function taskLabel(state) {
7443
7498
  if (!state.currentTask) return "";
7444
7499
  return state.currentTask.taskKey ?? state.currentTask.taskId;
7445
7500
  }
7446
- function stopHook(input, state) {
7501
+ function stopHook(input, state, env = { wakeupAvailable: false }) {
7447
7502
  if (input.stop_hook_active) {
7448
7503
  return { output: {} };
7449
7504
  }
@@ -7468,10 +7523,26 @@ function stopHook(input, state) {
7468
7523
  if (state.tasksCompletedInSession >= state.contextResetAfterTasks) {
7469
7524
  return limitReached(state);
7470
7525
  }
7526
+ if (state.claimFailedAt) {
7527
+ return {
7528
+ output: {
7529
+ systemMessage: "\u041F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0430 \u043E\u0442\u043A\u0430\u0437\u0430\u043B\u0430 \u043D\u0430 \u0437\u0430\u043F\u0440\u043E\u0441\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u0410\u0433\u0435\u043D\u0442 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u0437\u0432\u0430\u0442\u044C \u043F\u043E \u043A\u0440\u0443\u0433\u0443 \u0442\u043E, \u0447\u0442\u043E \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442. \u041F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u0441\u0432\u044F\u0437\u044C \u0441 \u043F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u043E\u0439 \u0438 \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0440\u0435\u0436\u0438\u043C \u0430\u0433\u0435\u043D\u0442\u0430 \u0441\u043D\u043E\u0432\u0430."
7530
+ }
7531
+ };
7532
+ }
7471
7533
  if (state.queueEmptyAt) {
7534
+ if (!env.wakeupAvailable) {
7535
+ return {
7536
+ output: {
7537
+ decision: "block",
7538
+ reason: "\u041E\u0447\u0435\u0440\u0435\u0434\u044C \u043F\u0443\u0441\u0442\u0430. \u041F\u043E\u0434\u043E\u0436\u0434\u0438 \u0438 \u0432\u044B\u0437\u043E\u0432\u0438 next_task \u0435\u0449\u0451 \u0440\u0430\u0437 \u2014 \u0431\u0443\u0434\u0438\u043B\u044C\u043D\u0438\u043A \u0432 \u044D\u0442\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u043D\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D, \u0442\u0430\u043A \u0447\u0442\u043E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C\u0441\u044F \u043D\u0435\u043B\u044C\u0437\u044F: \u0437\u0430\u0434\u0430\u0447\u0443 \u0438\u043D\u0430\u0447\u0435 \u043D\u0438\u043A\u0442\u043E \u043D\u0435 \u043F\u043E\u0434\u0445\u0432\u0430\u0442\u0438\u0442.",
7539
+ systemMessage: "\u0410\u0433\u0435\u043D\u0442 \u043E\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u043E\u0447\u0435\u0440\u0435\u0434\u044C \u043F\u043E \u043A\u0440\u0443\u0433\u0443, \u043F\u043E\u0442\u043E\u043C\u0443 \u0447\u0442\u043E \u0431\u0443\u0434\u0438\u043B\u044C\u043D\u0438\u043A \u043D\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D. \u0412\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u0435 \xABquiel init\xBB \u0432 \u043F\u0440\u043E\u0435\u043A\u0442\u0435 \u2014 \u043E\u043D \u043F\u0440\u043E\u043F\u0438\u0448\u0435\u0442 \u043E\u0436\u0438\u0434\u0430\u0442\u0435\u043B\u044C \u0437\u0430\u0434\u0430\u0447, \u0438 \u0430\u0433\u0435\u043D\u0442 \u0441\u043C\u043E\u0436\u0435\u0442 \u0441\u043F\u043E\u043A\u043E\u0439\u043D\u043E \u043F\u0440\u043E\u0441\u0442\u0430\u0438\u0432\u0430\u0442\u044C, \u043F\u0440\u043E\u0441\u044B\u043F\u0430\u044F\u0441\u044C \u0441\u0430\u043C."
7540
+ }
7541
+ };
7542
+ }
7472
7543
  return {
7473
7544
  output: {
7474
- systemMessage: "\u041E\u0447\u0435\u0440\u0435\u0434\u044C \u043F\u0443\u0441\u0442\u0430 \u2014 \u0430\u0433\u0435\u043D\u0442 \u0441\u0432\u043E\u0431\u043E\u0434\u0435\u043D. \u041F\u043E\u0437\u043E\u0432\u0438\u0442\u0435 \u0435\u0433\u043E \u0441\u043D\u043E\u0432\u0430, \u043A\u043E\u0433\u0434\u0430 \u043F\u043E\u044F\u0432\u044F\u0442\u0441\u044F \u0437\u0430\u0434\u0430\u0447\u0438: \u043E\u043D \u0432\u043E\u0437\u044C\u043C\u0451\u0442 \u0438\u0445 \u0441 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0436\u0435 next_task."
7545
+ systemMessage: "\u041E\u0447\u0435\u0440\u0435\u0434\u044C \u043F\u0443\u0441\u0442\u0430 \u2014 \u0430\u0433\u0435\u043D\u0442 \u0436\u0434\u0451\u0442. \u041E\u043D \u043F\u0440\u043E\u0441\u043D\u0451\u0442\u0441\u044F \u0441\u0430\u043C, \u043A\u0430\u043A \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u044F\u0432\u0438\u0442\u0441\u044F \u0437\u0430\u0434\u0430\u0447\u0430 \u0435\u0433\u043E \u0440\u043E\u043B\u0438."
7475
7546
  }
7476
7547
  };
7477
7548
  }
@@ -7511,6 +7582,20 @@ var HookInputSchema = z44.discriminatedUnion("hook_event_name", [
7511
7582
  PreCompactInputSchema,
7512
7583
  PreToolUseInputSchema
7513
7584
  ]);
7585
+ function isWakeupConfigured(cwd) {
7586
+ try {
7587
+ const root = findProjectRoot(cwd);
7588
+ if (!root) return false;
7589
+ const result = readClaudeSettings(root);
7590
+ if (!result.ok) return false;
7591
+ const groups = result.settings.hooks?.Stop ?? [];
7592
+ return groups.some(
7593
+ (group) => group.hooks.some((entry) => entry.command.includes("wait-for-task"))
7594
+ );
7595
+ } catch {
7596
+ return false;
7597
+ }
7598
+ }
7514
7599
  function runHook(rawInput, stateFilePath) {
7515
7600
  const parsedInput = HookInputSchema.safeParse(rawInput);
7516
7601
  if (!parsedInput.success) {
@@ -7550,7 +7635,7 @@ function dispatch2(input, state) {
7550
7635
  case "SessionStart":
7551
7636
  return sessionStartHook(input, state);
7552
7637
  case "Stop":
7553
- return stopHook(input, state);
7638
+ return stopHook(input, state, { wakeupAvailable: isWakeupConfigured(input.cwd) });
7554
7639
  case "StopFailure":
7555
7640
  return stopFailureHook(input, state);
7556
7641
  case "PreCompact":
@@ -7729,7 +7814,7 @@ function missingNonInteractiveFields(options) {
7729
7814
  return REQUIRED_FIELDS.filter((field) => !options[field] || options[field]?.trim().length === 0);
7730
7815
  }
7731
7816
  function parseRoles(raw) {
7732
- return raw.split(",").map((r) => r.trim()).filter((r) => r.length > 0);
7817
+ return raw.split(/[,\s]+/).map((r) => r.trim()).filter((r) => r.length > 0);
7733
7818
  }
7734
7819
  function createNoopPrompter() {
7735
7820
  const fail = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quiel/cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "CLI и MCP-сервер Quiel: подключает агента участника (Claude Code, Codex, Cursor, OpenCode) к платформе",
5
5
  "homepage": "https://quiel.app",
6
6
  "repository": {