@alook/cli 0.0.102 → 0.0.103

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.
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ var __export = (target, all) => {
15
15
  };
16
16
 
17
17
  // src/index.ts
18
- import { Command as Command11 } from "commander";
18
+ import { Command as Command12 } from "commander";
19
19
 
20
20
  // commands/register.ts
21
21
  import { Command } from "commander";
@@ -14605,7 +14605,7 @@ var DaemonPushMessageSchema = exports_external.discriminatedUnion("type", [
14605
14605
  exports_external.object({ type: exports_external.literal("daemon.evict"), workspaceId: exports_external.string() }),
14606
14606
  exports_external.object({ type: exports_external.literal("daemon.update"), version: exports_external.string() }),
14607
14607
  exports_external.object({ type: exports_external.literal("daemon.rescan") }),
14608
- exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), taskId: exports_external.string(), targetTaskId: exports_external.string() })
14608
+ exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() })
14609
14609
  ]);
14610
14610
  var RegisterResponseSchema = exports_external.object({
14611
14611
  runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
@@ -14958,6 +14958,13 @@ var CreateStudioRequestSchema = exports_external.object({
14958
14958
  scenario: exports_external.string().max(50).optional(),
14959
14959
  members: exports_external.array(StudioMemberSchema).min(1).max(4)
14960
14960
  }).refine((v) => v.members.some((m) => m.role === "leader"), { message: "at least one member must have the leader role" });
14961
+ var RecruitAgentRequestSchema = exports_external.object({
14962
+ instructions: exports_external.string().min(1, "instructions is required"),
14963
+ relationship: exports_external.string().min(1, "relationship is required"),
14964
+ name: exports_external.string().optional(),
14965
+ description: exports_external.string().optional().default(""),
14966
+ model: exports_external.string().max(100).optional()
14967
+ });
14961
14968
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@4.20260524.1_@opentelemetry+api@1.9.1_bun-types@1.3.14_kysely@0.28.17/node_modules/drizzle-orm/entity.js
14962
14969
  var entityKind = Symbol.for("drizzle:entityKind");
14963
14970
  var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
@@ -16856,9 +16863,6 @@ var agentSkill = sqliteTable("agent_skill", {
16856
16863
  // ../shared/src/db/queries/task.ts
16857
16864
  var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
16858
16865
  var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
16859
- // ../shared/src/db/queries/task-message.ts
16860
- var HIDDEN_STEP_TYPES = ["status", "log", "tool-result", "text"];
16861
- var FIXED_PARAMS = 1 + HIDDEN_STEP_TYPES.length;
16862
16866
  // ../shared/src/utils/email.ts
16863
16867
  var DOMAIN = `@${process.env.ALOOK_DOMAIN || "alook.ai"}`;
16864
16868
  var RESERVED_HANDLES = new Set([
@@ -18831,6 +18835,23 @@ Upload files for your owner to review in the app.
18831
18835
  When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
18832
18836
  Use your Read tool to open them. Images and PDFs are read visually.
18833
18837
  ---
18838
+ `;
18839
+ content += `
18840
+ ### Agent Management
18841
+ Recruit new colleague agents directly from the CLI. The server auto-generates a name and email handle.
18842
+ - Run '${cmdPrefix()} agent recruit --instructions "<SYSTEM_PROMPT>" --relationship "<DELEGATION_CRITERIA>"'
18843
+ - '--instructions' — the new agent's system prompt (what it does, how it behaves)
18844
+ - '--relationship' — delegation criteria shown in both agents' COLLEAGUES section
18845
+ - '--name <name>' (optional) — preferred name; server generates one if omitted
18846
+ - '--description <text>' (optional) — agent description
18847
+ - '--model <model>' (optional) — model override
18848
+ - '--instructions-file <path>' — alternative: read instructions from a file (mutually exclusive with --instructions)
18849
+ - '--relationship-file <path>' — alternative: read relationship from a file (mutually exclusive with --relationship)
18850
+ - '--json' — output full JSON response
18851
+ - Example: '${cmdPrefix()} agent recruit --instructions "You are a QA engineer..." --relationship "DELEGATE when: code is ready for review"'
18852
+ - Output: 'Recruited Felix (felix@alook.ai) — ag_xK9mPq2z'
18853
+ - The new agent shares your runtime, is automatically linked as your colleague, and receives a welcome task.
18854
+ ---
18834
18855
  `;
18835
18856
  content += `
18836
18857
  ### Calendar
@@ -20888,17 +20909,17 @@ async function startDaemon(profile, serverUrl) {
20888
20909
  break;
20889
20910
  case "daemon.kill": {
20890
20911
  const ws = workspaceStates.find((w) => w.workspaceId === msg.workspaceId);
20891
- if (ws) {
20912
+ if (ws && !activeTasks.has(msg.taskId)) {
20892
20913
  const killTask = fromApiTask({
20893
20914
  id: msg.taskId,
20894
- agent_id: "",
20915
+ agent_id: msg.agentId,
20895
20916
  runtime_id: "",
20896
20917
  conversation_id: "",
20897
20918
  workspace_id: ws.workspaceId,
20898
20919
  prompt: "",
20899
- status: "queued",
20920
+ status: "dispatched",
20900
20921
  priority: 0,
20901
- dispatched_at: null,
20922
+ dispatched_at: new Date().toISOString(),
20902
20923
  started_at: null,
20903
20924
  completed_at: null,
20904
20925
  result: null,
@@ -21258,9 +21279,16 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
21258
21279
  child.on("close", async (code) => {
21259
21280
  activeTasks.delete(task.id);
21260
21281
  if (code !== 0) {
21282
+ const agentBaseDir = join11(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir");
21283
+ const killIntent = readKillIntent(agentBaseDir, task.id);
21284
+ if (killIntent) {
21285
+ log9.info(`Task ${task.id} exited due to kill intent — skipping failTask`);
21286
+ clearKillIntent(agentBaseDir, task.id);
21287
+ return;
21288
+ }
21261
21289
  const msg = code === null ? `session-runner killed by signal (task ${task.id})` : `session-runner crashed (exit code ${code}, task ${task.id})`;
21262
21290
  log9.warn(msg);
21263
- const timelineDir = join11(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir", ".context_timeline");
21291
+ const timelineDir = join11(agentBaseDir, ".context_timeline");
21264
21292
  updateEntry(timelineDir, task.id, (entry) => {
21265
21293
  entry.pid = null;
21266
21294
  entry.status = "failed";
@@ -22315,19 +22343,67 @@ function issueCommand() {
22315
22343
  return cmd;
22316
22344
  }
22317
22345
 
22318
- // commands/version.ts
22346
+ // commands/agent.ts
22319
22347
  import { Command as Command8 } from "commander";
22348
+ import { readFileSync as readFileSync12 } from "fs";
22349
+ function readField(opts, inlineName, fileName) {
22350
+ if (opts.inline && opts.file) {
22351
+ console.error(`Error: --${inlineName} and --${fileName} are mutually exclusive`);
22352
+ process.exit(1);
22353
+ }
22354
+ if (opts.file)
22355
+ return readFileSync12(opts.file, "utf-8");
22356
+ return opts.inline ?? null;
22357
+ }
22358
+ function agentCommand() {
22359
+ const cmd = new Command8("agent").description("Manage agents");
22360
+ cmd.command("recruit").description("Recruit a new colleague agent").option("--agent_id <id>", "Agent ID (or set ALOOK_AGENT_ID env var)").option("--instructions <text>", "Instructions for the new agent (system prompt)").option("--instructions-file <path>", "Read instructions from a file").option("--relationship <text>", "Relationship/delegation instruction for the link").option("--relationship-file <path>", "Read relationship from a file").option("--name <name>", "Preferred name for the new agent (auto-generated if omitted)").option("--description <text>", "Agent description").option("--model <model>", "Model override").option("--json", "Output as JSON").action(async (opts, command) => {
22361
+ const instructions = readField({ inline: opts.instructions, file: opts.instructionsFile }, "instructions", "instructions-file");
22362
+ if (!instructions) {
22363
+ console.error("Error: --instructions or --instructions-file is required");
22364
+ process.exit(1);
22365
+ }
22366
+ const relationship = readField({ inline: opts.relationship, file: opts.relationshipFile }, "relationship", "relationship-file");
22367
+ if (!relationship) {
22368
+ console.error("Error: --relationship or --relationship-file is required");
22369
+ process.exit(1);
22370
+ }
22371
+ const agentId = resolveAgentId(opts);
22372
+ const { serverUrl, token, workspaceId } = resolveClientOpts(command, { agentId });
22373
+ const client = new APIClient(serverUrl, token, workspaceId);
22374
+ const body = { instructions, relationship };
22375
+ if (opts.name)
22376
+ body.name = opts.name;
22377
+ if (opts.description)
22378
+ body.description = opts.description;
22379
+ if (opts.model)
22380
+ body.model = opts.model;
22381
+ try {
22382
+ const res = await client.postJSON(`/api/agents/recruit?agentId=${encodeURIComponent(agentId)}`, body);
22383
+ if (opts.json)
22384
+ return printJSON(res);
22385
+ console.log(`Recruited ${res.agent.name} (${res.agent.email}) — ${res.agent.id}`);
22386
+ } catch (err) {
22387
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
22388
+ process.exit(1);
22389
+ }
22390
+ });
22391
+ return cmd;
22392
+ }
22393
+
22394
+ // commands/version.ts
22395
+ import { Command as Command9 } from "commander";
22320
22396
  function versionCommand() {
22321
- const cmd = new Command8("version").description("Show CLI version").action(() => {
22397
+ const cmd = new Command9("version").description("Show CLI version").action(() => {
22322
22398
  console.log(`alook version ${getCurrentVersion()}`);
22323
22399
  });
22324
22400
  return cmd;
22325
22401
  }
22326
22402
 
22327
22403
  // commands/update.ts
22328
- import { Command as Command9 } from "commander";
22404
+ import { Command as Command10 } from "commander";
22329
22405
  function updateCommand() {
22330
- const cmd = new Command9("update").description("Update CLI to the latest version").action(async () => {
22406
+ const cmd = new Command10("update").description("Update CLI to the latest version").action(async () => {
22331
22407
  const current = getCurrentVersion();
22332
22408
  console.log(`Current version: ${current}`);
22333
22409
  const latest = await fetchLatestVersion();
@@ -22361,18 +22437,18 @@ ${result.output}`);
22361
22437
  }
22362
22438
 
22363
22439
  // commands/sync.ts
22364
- import { Command as Command10 } from "commander";
22365
- import { readFileSync as readFileSync12 } from "fs";
22440
+ import { Command as Command11 } from "commander";
22441
+ import { readFileSync as readFileSync13 } from "fs";
22366
22442
  import { basename as basename3 } from "path";
22367
22443
  function syncCommand() {
22368
- const cmd = new Command10("sync").description("File sync utilities");
22444
+ const cmd = new Command11("sync").description("File sync utilities");
22369
22445
  cmd.command("upload-artifact").description("Upload a file artifact to a conversation").option("--agent_id <id>", "Agent ID").requiredOption("--conversation_id <id>", "Conversation ID").requiredOption("--file <path>", "Path to file to upload").action(async (opts, command) => {
22370
22446
  const agentId = resolveAgentId(opts);
22371
22447
  const { serverUrl, token, workspaceId } = resolveClientOpts(command, { agentId });
22372
22448
  const client = new APIClient(serverUrl, token, workspaceId);
22373
22449
  let bytes;
22374
22450
  try {
22375
- bytes = readFileSync12(opts.file);
22451
+ bytes = readFileSync13(opts.file);
22376
22452
  } catch (err) {
22377
22453
  console.error(`Error: cannot read file "${opts.file}": ${err.message}`);
22378
22454
  process.exit(1);
@@ -22395,7 +22471,7 @@ function syncCommand() {
22395
22471
  }
22396
22472
 
22397
22473
  // src/index.ts
22398
- var program = new Command11;
22474
+ var program = new Command12;
22399
22475
  program.name("alook").description("Alook CLI").option("--server <url>", "Server URL").option("--profile <name>", "Profile name");
22400
22476
  program.addCommand(registerCommand());
22401
22477
  program.addCommand(statusCommand());
@@ -22403,6 +22479,7 @@ program.addCommand(daemonCommand2());
22403
22479
  program.addCommand(emailCommand());
22404
22480
  program.addCommand(calendarCommand());
22405
22481
  program.addCommand(issueCommand());
22482
+ program.addCommand(agentCommand());
22406
22483
  program.addCommand(configCommand());
22407
22484
  program.addCommand(versionCommand());
22408
22485
  program.addCommand(updateCommand());
@@ -14472,7 +14472,7 @@ var DaemonPushMessageSchema = exports_external.discriminatedUnion("type", [
14472
14472
  exports_external.object({ type: exports_external.literal("daemon.evict"), workspaceId: exports_external.string() }),
14473
14473
  exports_external.object({ type: exports_external.literal("daemon.update"), version: exports_external.string() }),
14474
14474
  exports_external.object({ type: exports_external.literal("daemon.rescan") }),
14475
- exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), taskId: exports_external.string(), targetTaskId: exports_external.string() })
14475
+ exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() })
14476
14476
  ]);
14477
14477
  var RegisterResponseSchema = exports_external.object({
14478
14478
  runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
@@ -14825,6 +14825,13 @@ var CreateStudioRequestSchema = exports_external.object({
14825
14825
  scenario: exports_external.string().max(50).optional(),
14826
14826
  members: exports_external.array(StudioMemberSchema).min(1).max(4)
14827
14827
  }).refine((v) => v.members.some((m) => m.role === "leader"), { message: "at least one member must have the leader role" });
14828
+ var RecruitAgentRequestSchema = exports_external.object({
14829
+ instructions: exports_external.string().min(1, "instructions is required"),
14830
+ relationship: exports_external.string().min(1, "relationship is required"),
14831
+ name: exports_external.string().optional(),
14832
+ description: exports_external.string().optional().default(""),
14833
+ model: exports_external.string().max(100).optional()
14834
+ });
14828
14835
  // ../../node_modules/.pnpm/drizzle-orm@0.45.2_@cloudflare+workers-types@4.20260524.1_@opentelemetry+api@1.9.1_bun-types@1.3.14_kysely@0.28.17/node_modules/drizzle-orm/entity.js
14829
14836
  var entityKind = Symbol.for("drizzle:entityKind");
14830
14837
  var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
@@ -16723,9 +16730,6 @@ var agentSkill = sqliteTable("agent_skill", {
16723
16730
  // ../shared/src/db/queries/task.ts
16724
16731
  var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
16725
16732
  var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
16726
- // ../shared/src/db/queries/task-message.ts
16727
- var HIDDEN_STEP_TYPES = ["status", "log", "tool-result", "text"];
16728
- var FIXED_PARAMS = 1 + HIDDEN_STEP_TYPES.length;
16729
16733
  // ../shared/src/utils/email.ts
16730
16734
  var DOMAIN = `@${process.env.ALOOK_DOMAIN || "alook.ai"}`;
16731
16735
  var RESERVED_HANDLES = new Set([
@@ -18169,6 +18173,23 @@ Upload files for your owner to review in the app.
18169
18173
  When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
18170
18174
  Use your Read tool to open them. Images and PDFs are read visually.
18171
18175
  ---
18176
+ `;
18177
+ content += `
18178
+ ### Agent Management
18179
+ Recruit new colleague agents directly from the CLI. The server auto-generates a name and email handle.
18180
+ - Run '${cmdPrefix()} agent recruit --instructions "<SYSTEM_PROMPT>" --relationship "<DELEGATION_CRITERIA>"'
18181
+ - '--instructions' — the new agent's system prompt (what it does, how it behaves)
18182
+ - '--relationship' — delegation criteria shown in both agents' COLLEAGUES section
18183
+ - '--name <name>' (optional) — preferred name; server generates one if omitted
18184
+ - '--description <text>' (optional) — agent description
18185
+ - '--model <model>' (optional) — model override
18186
+ - '--instructions-file <path>' — alternative: read instructions from a file (mutually exclusive with --instructions)
18187
+ - '--relationship-file <path>' — alternative: read relationship from a file (mutually exclusive with --relationship)
18188
+ - '--json' — output full JSON response
18189
+ - Example: '${cmdPrefix()} agent recruit --instructions "You are a QA engineer..." --relationship "DELEGATE when: code is ready for review"'
18190
+ - Output: 'Recruited Felix (felix@alook.ai) — ag_xK9mPq2z'
18191
+ - The new agent shares your runtime, is automatically linked as your colleague, and receives a welcome task.
18192
+ ---
18172
18193
  `;
18173
18194
  content += `
18174
18195
  ### Calendar
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alook/cli",
3
- "version": "0.0.102",
3
+ "version": "0.0.103",
4
4
  "description": "Alook CLI — Enable Your Person Colleague",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/alookai/alook#readme",