@alook/cli 0.0.101 → 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() }))
@@ -14947,13 +14947,24 @@ var StudioMemberSchema = exports_external.object({
14947
14947
  description: exports_external.string().optional().default(""),
14948
14948
  instructions: exports_external.string().optional().default(""),
14949
14949
  avatar_url: exports_external.string().max(2000).nullable().optional(),
14950
- email_handle: exports_external.string().max(30).optional()
14950
+ email_handle: exports_external.string().max(30).optional(),
14951
+ relationship: exports_external.object({
14952
+ leaderSees: exports_external.string(),
14953
+ memberSees: exports_external.string()
14954
+ }).optional()
14951
14955
  });
14952
14956
  var CreateStudioRequestSchema = exports_external.object({
14953
14957
  name: exports_external.string().max(100).optional(),
14954
14958
  scenario: exports_external.string().max(50).optional(),
14955
14959
  members: exports_external.array(StudioMemberSchema).min(1).max(4)
14956
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
+ });
14957
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
14958
14969
  var entityKind = Symbol.for("drizzle:entityKind");
14959
14970
  var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
@@ -16852,9 +16863,6 @@ var agentSkill = sqliteTable("agent_skill", {
16852
16863
  // ../shared/src/db/queries/task.ts
16853
16864
  var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
16854
16865
  var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
16855
- // ../shared/src/db/queries/task-message.ts
16856
- var HIDDEN_STEP_TYPES = ["status", "log", "tool-result", "text"];
16857
- var FIXED_PARAMS = 1 + HIDDEN_STEP_TYPES.length;
16858
16866
  // ../shared/src/utils/email.ts
16859
16867
  var DOMAIN = `@${process.env.ALOOK_DOMAIN || "alook.ai"}`;
16860
16868
  var RESERVED_HANDLES = new Set([
@@ -18827,6 +18835,23 @@ Upload files for your owner to review in the app.
18827
18835
  When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
18828
18836
  Use your Read tool to open them. Images and PDFs are read visually.
18829
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
+ ---
18830
18855
  `;
18831
18856
  content += `
18832
18857
  ### Calendar
@@ -18916,7 +18941,7 @@ function ensureSymlinks(workDir) {
18916
18941
  symlinkSync(CANONICAL_FILE, aliasPath);
18917
18942
  } catch (err) {
18918
18943
  const code = err?.code;
18919
- if (code === "EPERM" || code === "EACCES") {
18944
+ if (code === "EEXIST") {} else if (code === "EPERM" || code === "EACCES") {
18920
18945
  copyFileSync(canonicalPath, aliasPath);
18921
18946
  } else {
18922
18947
  throw err;
@@ -20338,7 +20363,7 @@ function runScan() {
20338
20363
  const prevHash = readCacheHash(globalCachePath(scannerConfig.daemonId, runtime));
20339
20364
  if (prevHash !== hash2) {
20340
20365
  const skillItems = skills.map((s) => ({ name: s.name, description: s.description }));
20341
- log8.info(`Syncing global ${runtime} — ${skills.length} skills`);
20366
+ log8.debug(`Syncing global ${runtime} — ${skills.length} skills`);
20342
20367
  const daemonId = scannerConfig.daemonId;
20343
20368
  const syncPromises = scannerConfig.workspaces.map((ws) => clientRef.syncSkills(ws.token, {
20344
20369
  scope: "global",
@@ -20364,7 +20389,7 @@ function runScan() {
20364
20389
  const prevHash = readCacheHash(agentCachePath(target.agentId, target.runtime));
20365
20390
  if (prevHash !== hash2) {
20366
20391
  const skillItems = skills.map((s) => ({ name: s.name, description: s.description }));
20367
- log8.info(`Syncing ${target.agentId}:${target.runtime} — ${skills.length} agent skills`);
20392
+ log8.debug(`Syncing ${target.agentId}:${target.runtime} — ${skills.length} agent skills`);
20368
20393
  clientRef.syncSkills(target.token, {
20369
20394
  scope: "agent",
20370
20395
  agent_id: target.agentId,
@@ -20884,17 +20909,17 @@ async function startDaemon(profile, serverUrl) {
20884
20909
  break;
20885
20910
  case "daemon.kill": {
20886
20911
  const ws = workspaceStates.find((w) => w.workspaceId === msg.workspaceId);
20887
- if (ws) {
20912
+ if (ws && !activeTasks.has(msg.taskId)) {
20888
20913
  const killTask = fromApiTask({
20889
20914
  id: msg.taskId,
20890
- agent_id: "",
20915
+ agent_id: msg.agentId,
20891
20916
  runtime_id: "",
20892
20917
  conversation_id: "",
20893
20918
  workspace_id: ws.workspaceId,
20894
20919
  prompt: "",
20895
- status: "queued",
20920
+ status: "dispatched",
20896
20921
  priority: 0,
20897
- dispatched_at: null,
20922
+ dispatched_at: new Date().toISOString(),
20898
20923
  started_at: null,
20899
20924
  completed_at: null,
20900
20925
  result: null,
@@ -21254,9 +21279,16 @@ async function handleTask(client, config2, runtimeIndex, task, token, activeTask
21254
21279
  child.on("close", async (code) => {
21255
21280
  activeTasks.delete(task.id);
21256
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
+ }
21257
21289
  const msg = code === null ? `session-runner killed by signal (task ${task.id})` : `session-runner crashed (exit code ${code}, task ${task.id})`;
21258
21290
  log9.warn(msg);
21259
- const timelineDir = join11(config2.workspacesRoot, task.workspaceId, task.agentId, "workdir", ".context_timeline");
21291
+ const timelineDir = join11(agentBaseDir, ".context_timeline");
21260
21292
  updateEntry(timelineDir, task.id, (entry) => {
21261
21293
  entry.pid = null;
21262
21294
  entry.status = "failed";
@@ -22311,19 +22343,67 @@ function issueCommand() {
22311
22343
  return cmd;
22312
22344
  }
22313
22345
 
22314
- // commands/version.ts
22346
+ // commands/agent.ts
22315
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";
22316
22396
  function versionCommand() {
22317
- const cmd = new Command8("version").description("Show CLI version").action(() => {
22397
+ const cmd = new Command9("version").description("Show CLI version").action(() => {
22318
22398
  console.log(`alook version ${getCurrentVersion()}`);
22319
22399
  });
22320
22400
  return cmd;
22321
22401
  }
22322
22402
 
22323
22403
  // commands/update.ts
22324
- import { Command as Command9 } from "commander";
22404
+ import { Command as Command10 } from "commander";
22325
22405
  function updateCommand() {
22326
- 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 () => {
22327
22407
  const current = getCurrentVersion();
22328
22408
  console.log(`Current version: ${current}`);
22329
22409
  const latest = await fetchLatestVersion();
@@ -22357,18 +22437,18 @@ ${result.output}`);
22357
22437
  }
22358
22438
 
22359
22439
  // commands/sync.ts
22360
- import { Command as Command10 } from "commander";
22361
- import { readFileSync as readFileSync12 } from "fs";
22440
+ import { Command as Command11 } from "commander";
22441
+ import { readFileSync as readFileSync13 } from "fs";
22362
22442
  import { basename as basename3 } from "path";
22363
22443
  function syncCommand() {
22364
- const cmd = new Command10("sync").description("File sync utilities");
22444
+ const cmd = new Command11("sync").description("File sync utilities");
22365
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) => {
22366
22446
  const agentId = resolveAgentId(opts);
22367
22447
  const { serverUrl, token, workspaceId } = resolveClientOpts(command, { agentId });
22368
22448
  const client = new APIClient(serverUrl, token, workspaceId);
22369
22449
  let bytes;
22370
22450
  try {
22371
- bytes = readFileSync12(opts.file);
22451
+ bytes = readFileSync13(opts.file);
22372
22452
  } catch (err) {
22373
22453
  console.error(`Error: cannot read file "${opts.file}": ${err.message}`);
22374
22454
  process.exit(1);
@@ -22391,7 +22471,7 @@ function syncCommand() {
22391
22471
  }
22392
22472
 
22393
22473
  // src/index.ts
22394
- var program = new Command11;
22474
+ var program = new Command12;
22395
22475
  program.name("alook").description("Alook CLI").option("--server <url>", "Server URL").option("--profile <name>", "Profile name");
22396
22476
  program.addCommand(registerCommand());
22397
22477
  program.addCommand(statusCommand());
@@ -22399,6 +22479,7 @@ program.addCommand(daemonCommand2());
22399
22479
  program.addCommand(emailCommand());
22400
22480
  program.addCommand(calendarCommand());
22401
22481
  program.addCommand(issueCommand());
22482
+ program.addCommand(agentCommand());
22402
22483
  program.addCommand(configCommand());
22403
22484
  program.addCommand(versionCommand());
22404
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() }))
@@ -14814,13 +14814,24 @@ var StudioMemberSchema = exports_external.object({
14814
14814
  description: exports_external.string().optional().default(""),
14815
14815
  instructions: exports_external.string().optional().default(""),
14816
14816
  avatar_url: exports_external.string().max(2000).nullable().optional(),
14817
- email_handle: exports_external.string().max(30).optional()
14817
+ email_handle: exports_external.string().max(30).optional(),
14818
+ relationship: exports_external.object({
14819
+ leaderSees: exports_external.string(),
14820
+ memberSees: exports_external.string()
14821
+ }).optional()
14818
14822
  });
14819
14823
  var CreateStudioRequestSchema = exports_external.object({
14820
14824
  name: exports_external.string().max(100).optional(),
14821
14825
  scenario: exports_external.string().max(50).optional(),
14822
14826
  members: exports_external.array(StudioMemberSchema).min(1).max(4)
14823
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
+ });
14824
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
14825
14836
  var entityKind = Symbol.for("drizzle:entityKind");
14826
14837
  var hasOwnEntityKind = Symbol.for("drizzle:hasOwnEntityKind");
@@ -16719,9 +16730,6 @@ var agentSkill = sqliteTable("agent_skill", {
16719
16730
  // ../shared/src/db/queries/task.ts
16720
16731
  var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
16721
16732
  var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
16722
- // ../shared/src/db/queries/task-message.ts
16723
- var HIDDEN_STEP_TYPES = ["status", "log", "tool-result", "text"];
16724
- var FIXED_PARAMS = 1 + HIDDEN_STEP_TYPES.length;
16725
16733
  // ../shared/src/utils/email.ts
16726
16734
  var DOMAIN = `@${process.env.ALOOK_DOMAIN || "alook.ai"}`;
16727
16735
  var RESERVED_HANDLES = new Set([
@@ -18165,6 +18173,23 @@ Upload files for your owner to review in the app.
18165
18173
  When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
18166
18174
  Use your Read tool to open them. Images and PDFs are read visually.
18167
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
+ ---
18168
18193
  `;
18169
18194
  content += `
18170
18195
  ### Calendar
@@ -18254,7 +18279,7 @@ function ensureSymlinks(workDir) {
18254
18279
  symlinkSync(CANONICAL_FILE, aliasPath);
18255
18280
  } catch (err) {
18256
18281
  const code = err?.code;
18257
- if (code === "EPERM" || code === "EACCES") {
18282
+ if (code === "EEXIST") {} else if (code === "EPERM" || code === "EACCES") {
18258
18283
  copyFileSync(canonicalPath, aliasPath);
18259
18284
  } else {
18260
18285
  throw err;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alook/cli",
3
- "version": "0.0.101",
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",