@linzumi/cli 0.0.76-beta → 0.0.77-beta

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 +1 -1
  2. package/dist/index.js +187 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -58,7 +58,7 @@ Install the CLI or run it with `npx`:
58
58
  ```bash
59
59
  npm install -g @linzumi/cli@latest
60
60
  npx -y @linzumi/cli@latest signup
61
- npx -y @linzumi/cli@0.0.76-beta --version
61
+ npx -y @linzumi/cli@0.0.77-beta --version
62
62
  linzumi --version
63
63
  ```
64
64
 
package/dist/index.js CHANGED
@@ -376,12 +376,7 @@ function appendDownloadedAttachmentContext(input, attachments, options = {}) {
376
376
  "Do not mention that you are using the upload tool. Do not print an upload manifest or upload footer in the message.",
377
377
  "The MCP upload tool is the only supported path for attaching generated files to Linzumi messages."
378
378
  ];
379
- return [
380
- input,
381
- ...attachmentContext,
382
- "",
383
- ...uploadInstructions
384
- ].join("\n");
379
+ return [input, ...attachmentContext, "", ...uploadInstructions].join("\n");
385
380
  }
386
381
  function codexImageInputItemsForDownloadedAttachments(attachments) {
387
382
  return attachments.flatMap(
@@ -659,7 +654,11 @@ async function commanderAttachmentUploadRoots(cwdRealPath) {
659
654
  const roots = [
660
655
  { path: cwdRealPath, label: "runner cwd" }
661
656
  ];
662
- const downloadsPath = resolve(homedir(), "Downloads");
657
+ const configuredHome = process.env.HOME?.trim();
658
+ const downloadsPath = resolve(
659
+ configuredHome === void 0 || configuredHome === "" ? homedir() : configuredHome,
660
+ "Downloads"
661
+ );
663
662
  try {
664
663
  const downloadsRealPath = await realpath(downloadsPath);
665
664
  if (!roots.some((root) => root.path === downloadsRealPath)) {
@@ -9603,7 +9602,7 @@ function defaultCliAuditLogFile() {
9603
9602
  return override === void 0 || override === "" ? join4(homedir4(), ".linzumi", "logs", "command-events.jsonl") : override;
9604
9603
  }
9605
9604
  function defaultRunnerLogFile() {
9606
- return join4(homedir4(), ".linzumi", "logs", "runner-events.jsonl");
9605
+ return join4(homedir4(), ".linzumi", "logs", "linzumi-runner.log");
9607
9606
  }
9608
9607
  function redactForCliLog(value) {
9609
9608
  return redactObject(value);
@@ -14633,7 +14632,7 @@ var linzumiCliVersion, linzumiCliVersionText;
14633
14632
  var init_version = __esm({
14634
14633
  "src/version.ts"() {
14635
14634
  "use strict";
14636
- linzumiCliVersion = "0.0.76-beta";
14635
+ linzumiCliVersion = "0.0.77-beta";
14637
14636
  linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
14638
14637
  }
14639
14638
  });
@@ -15937,8 +15936,7 @@ import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as rea
15937
15936
  import { homedir as homedir9 } from "node:os";
15938
15937
  import { dirname as dirname8, join as join12 } from "node:path";
15939
15938
  function defaultAuthFilePath() {
15940
- const base = process.env.KANDAN_HOME ?? join12(homedir9(), ".kandan");
15941
- return join12(base, "auth.json");
15939
+ return join12(homedir9(), ".linzumi", "auth.json");
15942
15940
  }
15943
15941
  function readCachedLocalRunnerToken(kandanUrl, authFilePath = defaultAuthFilePath()) {
15944
15942
  if (!existsSync8(authFilePath)) {
@@ -19742,6 +19740,15 @@ read a message by ID/URL, read a thread by ID, read the scoped channel, or send
19742
19740
  a concise DM to the Commander owner when the task genuinely requires it.
19743
19741
  </linzumi_mcp>
19744
19742
 
19743
+ <coding_job_metadata>
19744
+ When you are working inside a coding-job thread, keep the job metadata current through the Linzumi MCP tools.
19745
+ At the beginning of a job, call linzumi_upsert_coding_job_plan with your current understanding of the goal, then call linzumi_replace_coding_job_plan_steps with an ordered list of steps to accomplish that goal.
19746
+ The plan is allowed to change. Rework it when the old plan is no longer a useful description of the work, but do not churn it for minor tactical adjustments.
19747
+ As you make progress, call linzumi_update_coding_job_plan_step to mark the active step and completed steps. Read the metadata first when you need current step ids or lock versions.
19748
+ When you open, discover, or choose the primary GitHub pull request for the job, call linzumi_link_coding_job_pull_request with the PR URL, repo owner/name, PR number, title, head/base branches, head/base SHAs, local worktree path, remote URL, and current dirty status. This link is what lets GitHub PR comments, files, commits, checks, deployments, and reviews appear automatically in the metadata overview.
19749
+ The plan and goal are user-visible, so keep titles short, descriptions concrete, and completion notes focused on what was verified.
19750
+ </coding_job_metadata>
19751
+
19745
19752
  <task_instructions>
19746
19753
  Work only in the approved project folder unless the human explicitly asks for
19747
19754
  something else in the Linzumi thread. Start, inspect, and modify the local app
@@ -42334,6 +42341,7 @@ function signupHelpText() {
42334
42341
  "",
42335
42342
  "Options:",
42336
42343
  " --api-url <url> Linzumi HTTP origin, default https://serve.linzumi.com",
42344
+ " --code-server-bin <path> Custom development code-server executable",
42337
42345
  " --force-signup Ignore existing local signup auth and create a fresh login",
42338
42346
  " --no-open Print the launch URL without opening the browser",
42339
42347
  " --debug-signup Print the collected launch payload JSON after signup",
@@ -42352,6 +42360,7 @@ function signupPromptCancelMessage(error) {
42352
42360
  function parseSignupTuiOptions(args) {
42353
42361
  let serviceUrl = defaultLinzumiHttpUrl;
42354
42362
  let forceSignup = false;
42363
+ let codeServerBin;
42355
42364
  let openBrowser2 = true;
42356
42365
  let debugLaunchPayload = false;
42357
42366
  for (let index = 0; index < args.length; index += 1) {
@@ -42377,11 +42386,26 @@ function parseSignupTuiOptions(args) {
42377
42386
  index += 1;
42378
42387
  break;
42379
42388
  }
42389
+ case "--code-server-bin": {
42390
+ const value = args[index + 1];
42391
+ if (value === void 0 || value.startsWith("--")) {
42392
+ throw new Error("missing value for --code-server-bin");
42393
+ }
42394
+ codeServerBin = value;
42395
+ index += 1;
42396
+ break;
42397
+ }
42380
42398
  default:
42381
42399
  throw new Error(`invalid signup flag: ${arg ?? ""}`);
42382
42400
  }
42383
42401
  }
42384
- return { serviceUrl, forceSignup, openBrowser: openBrowser2, debugLaunchPayload };
42402
+ return {
42403
+ serviceUrl,
42404
+ forceSignup,
42405
+ ...codeServerBin === void 0 ? {} : { codeServerBin },
42406
+ openBrowser: openBrowser2,
42407
+ debugLaunchPayload
42408
+ };
42385
42409
  }
42386
42410
  function signupAuthMatchesServiceUrl(auth, serviceUrl) {
42387
42411
  return comparableServiceUrl(auth.serviceUrl) === comparableServiceUrl(serviceUrl);
@@ -42637,6 +42661,7 @@ async function runSignupFlow(deps = {}) {
42637
42661
  const preflight = deps.preflight ?? defaultPreflightRuntime();
42638
42662
  const forceSignup = deps.forceSignup === true;
42639
42663
  const serviceUrl = deps.serviceUrl ?? defaultLinzumiHttpUrl;
42664
+ const codeServerBin = deps.codeServerBin;
42640
42665
  const openBrowser2 = deps.openBrowser !== false;
42641
42666
  const debugLaunchPayload = deps.debugLaunchPayload === true;
42642
42667
  const openUrl = deps.openUrl ?? openUrlInBrowser;
@@ -42925,7 +42950,8 @@ async function runSignupFlow(deps = {}) {
42925
42950
  workspaceSlug: missionControl.workspace.slug,
42926
42951
  projectPath: starterProjectPath,
42927
42952
  selectedProjectPaths: missionControl.config.allowedCwds,
42928
- codexBin: verifiedCodexBin
42953
+ codexBin: verifiedCodexBin,
42954
+ ...codeServerBin === void 0 ? {} : { codeServerBin }
42929
42955
  });
42930
42956
  const starterTaskLaunches = await startSignupTasksForCommander(
42931
42957
  signupServerClient,
@@ -43659,6 +43685,7 @@ async function signupCommanderRunnerOptions(args, runnerId) {
43659
43685
  const editorRuntime = await resolveEditorRuntime({
43660
43686
  kandanUrl: args.serviceUrl,
43661
43687
  token: args.localRunnerAccessToken,
43688
+ customCodeServerBin: args.codeServerBin,
43662
43689
  fetchImpl: trustedFetch(trust)
43663
43690
  });
43664
43691
  const dependencyStatus = await buildRunnerDependencyStatus({
@@ -43713,6 +43740,7 @@ function signupConnectRestartCommand(args) {
43713
43740
  "--workspace",
43714
43741
  args.workspaceSlug,
43715
43742
  ...args.codexBin === void 0 ? [] : ["--codex-bin", args.codexBin],
43743
+ ...args.codeServerBin === void 0 ? [] : ["--code-server-bin", args.codeServerBin],
43716
43744
  "--cwd",
43717
43745
  args.projectPath,
43718
43746
  ...args.serviceUrl === defaultLinzumiHttpUrl ? [] : ["--api-url", args.serviceUrl]
@@ -46462,8 +46490,8 @@ function commanderStatusDir() {
46462
46490
  function commanderStatusFile(runnerId, statusDir = commanderStatusDir()) {
46463
46491
  return join16(statusDir, `${safeRunnerId(runnerId)}.json`);
46464
46492
  }
46465
- function defaultCommanderLogFile(runnerId, statusDir = commanderStatusDir()) {
46466
- return join16(statusDir, `${safeRunnerId(runnerId)}.log`);
46493
+ function defaultCommanderLogFile(runnerId) {
46494
+ return join16(homedir11(), ".linzumi", "logs", `${safeRunnerId(runnerId)}.log`);
46467
46495
  }
46468
46496
  function commanderLogIsConnected(log) {
46469
46497
  return connectedMarkers.some((marker) => log.includes(marker));
@@ -46472,7 +46500,7 @@ function startCommanderDaemon(options) {
46472
46500
  const statusDir = options.statusDir ?? commanderStatusDir();
46473
46501
  const statusFile = commanderStatusFile(options.runnerId, statusDir);
46474
46502
  const logFile = resolve8(
46475
- options.logFile ?? defaultCommanderLogFile(options.runnerId, statusDir)
46503
+ options.logFile ?? defaultCommanderLogFile(options.runnerId)
46476
46504
  );
46477
46505
  const entrypoint = options.entrypoint ?? currentEntrypoint();
46478
46506
  const nodeBin = options.nodeBin ?? process.execPath;
@@ -58505,6 +58533,11 @@ function createLinzumiMcpApiClient(options) {
58505
58533
  getMessage: (params) => request("GET", `${apiPrefix}/message`, params),
58506
58534
  getThread: (params) => request("GET", `${apiPrefix}/thread`, params),
58507
58535
  getChannel: (params) => request("GET", `${apiPrefix}/channel`, params),
58536
+ getCodingJobMetadata: (params) => request("GET", `${apiPrefix}/coding-job-metadata`, params),
58537
+ upsertCodingJobPlan: (params) => request("POST", `${apiPrefix}/coding-job-plan`, params),
58538
+ replaceCodingJobPlanSteps: (params) => request("POST", `${apiPrefix}/coding-job-plan/steps`, params),
58539
+ updateCodingJobPlanStep: (params) => request("POST", `${apiPrefix}/coding-job-plan/step`, params),
58540
+ linkCodingJobPullRequest: (params) => request("POST", `${apiPrefix}/coding-job-primary-pr`, params),
58508
58541
  listVaultSecrets: (params) => request("GET", `${apiPrefix}/vault-secrets`, params),
58509
58542
  sendChannelMessage: (params) => request("POST", `${apiPrefix}/channel-message`, params),
58510
58543
  prepareMessageUploads: (params) => request("POST", `${apiPrefix}/message-uploads/prepare`, params),
@@ -58582,6 +58615,16 @@ Tools:
58582
58615
  linzumi_get_message Read one message by scoped seq or Linzumi message URL.
58583
58616
  linzumi_get_thread Read one bounded thread by ID.
58584
58617
  linzumi_get_channel Read bounded recent messages and channel metadata.
58618
+ linzumi_get_coding_job_metadata
58619
+ Read the current coding job goal, plan, and workflow metadata.
58620
+ linzumi_upsert_coding_job_plan
58621
+ Set or update the coding job goal.
58622
+ linzumi_replace_coding_job_plan_steps
58623
+ Replace the ordered coding job plan steps.
58624
+ linzumi_update_coding_job_plan_step
58625
+ Update one coding job plan step status or note.
58626
+ linzumi_link_coding_job_pull_request
58627
+ Link the coding job to its primary GitHub pull request.
58585
58628
  linzumi_list_vault_secrets
58586
58629
  List vault secret names, descriptions, and owner scope.
58587
58630
  linzumi_send_channel_message
@@ -58669,6 +58712,123 @@ async function runMcpServer(args) {
58669
58712
  },
58670
58713
  async (params) => mcpJsonResult(await client.getChannel(params))
58671
58714
  );
58715
+ server.tool(
58716
+ "linzumi_get_coding_job_metadata",
58717
+ "Read the active coding job workflow metadata for a thread, including the agent-owned goal, ordered plan steps, step lock versions, and workflow status.",
58718
+ {
58719
+ thread_id: external_exports2.string().uuid().optional().describe(
58720
+ "Linzumi thread UUID. Defaults to the active coding job thread."
58721
+ )
58722
+ },
58723
+ async (params) => mcpJsonResult(
58724
+ await client.getCodingJobMetadata(
58725
+ paramsWithDefaultThread(params, defaultThreadId)
58726
+ )
58727
+ )
58728
+ );
58729
+ server.tool(
58730
+ "linzumi_upsert_coding_job_plan",
58731
+ "Set or update the current coding job goal. Use this at job start and whenever the goal changes materially.",
58732
+ {
58733
+ thread_id: external_exports2.string().uuid().optional().describe(
58734
+ "Linzumi thread UUID. Defaults to the active coding job thread."
58735
+ ),
58736
+ goal: external_exports2.string().min(1).max(1e4).describe("Concrete user-visible goal for this coding job.")
58737
+ },
58738
+ async (params) => mcpJsonResult(
58739
+ await client.upsertCodingJobPlan(
58740
+ paramsWithDefaultThread(params, defaultThreadId)
58741
+ )
58742
+ )
58743
+ );
58744
+ server.tool(
58745
+ "linzumi_replace_coding_job_plan_steps",
58746
+ "Replace the ordered coding job plan steps. Use this for the initial plan and for substantial rewrites when the old plan no longer fits the work.",
58747
+ {
58748
+ thread_id: external_exports2.string().uuid().optional().describe(
58749
+ "Linzumi thread UUID. Defaults to the active coding job thread."
58750
+ ),
58751
+ goal: external_exports2.string().min(1).max(1e4).optional().describe(
58752
+ "Required when no plan exists yet; updates the current coding job goal."
58753
+ ),
58754
+ steps: external_exports2.array(
58755
+ external_exports2.object({
58756
+ title: external_exports2.string().min(1).max(255),
58757
+ description: external_exports2.string().min(1).max(1e4),
58758
+ status: external_exports2.enum([
58759
+ "pending",
58760
+ "active",
58761
+ "completed",
58762
+ "blocked",
58763
+ "canceled"
58764
+ ]),
58765
+ status_note: external_exports2.string().min(1).max(4e3).optional(),
58766
+ completed_debrief: external_exports2.string().min(1).max(2e4).optional()
58767
+ })
58768
+ ).min(1).max(50).describe(
58769
+ "Ordered user-visible plan steps. Completed steps must include completed_debrief."
58770
+ )
58771
+ },
58772
+ async (params) => mcpJsonResult(
58773
+ await client.replaceCodingJobPlanSteps(
58774
+ paramsWithDefaultThread(params, defaultThreadId)
58775
+ )
58776
+ )
58777
+ );
58778
+ server.tool(
58779
+ "linzumi_update_coding_job_plan_step",
58780
+ "Update one coding job plan step status or note. Read metadata first and pass the current step lock_version.",
58781
+ {
58782
+ thread_id: external_exports2.string().uuid().optional().describe(
58783
+ "Linzumi thread UUID. Defaults to the active coding job thread."
58784
+ ),
58785
+ step_id: external_exports2.string().uuid().describe("Step id returned by linzumi_get_coding_job_metadata."),
58786
+ lock_version: external_exports2.number().int().positive().describe("Current step lock_version from metadata."),
58787
+ status: external_exports2.enum(["pending", "active", "completed", "blocked", "canceled"]),
58788
+ status_note: external_exports2.string().min(1).max(4e3).optional(),
58789
+ completed_debrief: external_exports2.string().min(1).max(2e4).optional().describe("Required when marking a step completed.")
58790
+ },
58791
+ async (params) => mcpJsonResult(
58792
+ await client.updateCodingJobPlanStep(
58793
+ paramsWithDefaultThread(params, defaultThreadId)
58794
+ )
58795
+ )
58796
+ );
58797
+ server.tool(
58798
+ "linzumi_link_coding_job_pull_request",
58799
+ "Link the active coding job to its primary GitHub pull request so PR comments, files, commits, checks, deployments, and reviews can flow into the metadata view.",
58800
+ {
58801
+ thread_id: external_exports2.string().uuid().optional().describe(
58802
+ "Linzumi thread UUID. Defaults to the active coding job thread."
58803
+ ),
58804
+ github_repo_owner: external_exports2.string().min(1).max(255),
58805
+ github_repo_name: external_exports2.string().min(1).max(255),
58806
+ github_pr_number: external_exports2.number().int().positive(),
58807
+ github_pr_url: external_exports2.string().url(),
58808
+ title: external_exports2.string().min(1).max(255),
58809
+ body: external_exports2.string().min(1).max(1e5).optional(),
58810
+ head_branch: external_exports2.string().min(1).max(255),
58811
+ head_sha: external_exports2.string().min(7).max(64),
58812
+ base_branch: external_exports2.string().min(1).max(255),
58813
+ base_sha: external_exports2.string().min(7).max(64),
58814
+ state: external_exports2.enum(["draft", "open", "closed", "merged"]).optional(),
58815
+ is_draft: external_exports2.boolean().optional(),
58816
+ review_status: external_exports2.enum(["unknown", "review_required", "approved", "changes_requested"]).optional(),
58817
+ source_remote_url: external_exports2.string().min(1).max(1e3).describe(
58818
+ "Git remote URL for the local job worktree; required when this job has no repo workspace yet."
58819
+ ),
58820
+ runtime_worktree_root: external_exports2.string().min(1).max(1e3).describe(
58821
+ "Absolute local worktree path for the coding job; required when this job has no repo workspace yet."
58822
+ ),
58823
+ dirty_status: external_exports2.enum(["unknown", "clean", "dirty"]).optional(),
58824
+ dirty_summary: external_exports2.string().min(1).max(2e3).optional()
58825
+ },
58826
+ async (params) => mcpJsonResult(
58827
+ await client.linkCodingJobPullRequest(
58828
+ paramsWithDefaultThread(params, defaultThreadId)
58829
+ )
58830
+ )
58831
+ );
58672
58832
  server.tool(
58673
58833
  "linzumi_upload_files",
58674
58834
  "Upload local files to Linzumi and attach them to a new, exact, or latest assistant message. Use silently; do not mention this tool in the visible reply.",
@@ -58966,6 +59126,13 @@ function targetWithDefaultThread(target, defaultThreadId) {
58966
59126
  return target;
58967
59127
  }
58968
59128
  }
59129
+ function paramsWithDefaultThread(params, defaultThreadId) {
59130
+ const existingThreadId = stringValue(params.thread_id);
59131
+ if (existingThreadId !== void 0 || defaultThreadId === void 0) {
59132
+ return params;
59133
+ }
59134
+ return { ...params, thread_id: defaultThreadId };
59135
+ }
58969
59136
  function resolveLinzumiUploadUrl(kandanUrl, uploadUrl) {
58970
59137
  try {
58971
59138
  return new URL(uploadUrl).toString();
@@ -59566,6 +59733,7 @@ async function main(args) {
59566
59733
  await runSignupFlow2({
59567
59734
  forceSignup: options.forceSignup,
59568
59735
  serviceUrl: options.serviceUrl,
59736
+ codeServerBin: options.codeServerBin,
59569
59737
  openBrowser: false,
59570
59738
  debugLaunchPayload: options.debugLaunchPayload,
59571
59739
  signupServerClient: false
@@ -59584,6 +59752,7 @@ async function main(args) {
59584
59752
  await runSignupFlow2({
59585
59753
  forceSignup: options.forceSignup,
59586
59754
  serviceUrl: options.serviceUrl,
59755
+ codeServerBin: options.codeServerBin,
59587
59756
  openBrowser: options.openBrowser,
59588
59757
  debugLaunchPayload: options.debugLaunchPayload
59589
59758
  });
@@ -60637,7 +60806,7 @@ Codex:
60637
60806
  --approval-policy <value> Approval-policy metadata shown in Linzumi
60638
60807
  --stream-flush-ms <ms> Batch live Codex deltas before Linzumi persistence, default 150
60639
60808
  --fast Mark this runner as low-latency/fast in the availability message
60640
- --log-file <path> JSONL event log path, default ~/.linzumi/logs/runner-events.jsonl
60809
+ --log-file <path> JSONL event log path, default ~/.linzumi/logs/linzumi-runner.log
60641
60810
  --allowed-cwd <paths> Extra comma-separated roots where Linzumi may start local Codex sessions
60642
60811
  --forward-port <ports> Comma-separated local TCP ports Linzumi may expose as authenticated previews
60643
60812
  --code-server-bin <path> Custom development code-server executable. The default editor runtime is downloaded from Linzumi.
@@ -60719,7 +60888,7 @@ What it does:
60719
60888
  Options:
60720
60889
  --api-url <url> Linzumi API URL used to select the stored scoped runner id
60721
60890
  --status-dir <path> Status directory, default ~/.linzumi/commanders
60722
- --log-file <path> Commander log path, default in the status dir
60891
+ --log-file <path> Commander log path, default ~/.linzumi/logs/<runner-id>.log
60723
60892
  --timeout-ms <ms> Wait timeout, default 30000
60724
60893
 
60725
60894
  All normal Commander options such as --agent-token-file, --allowed-cwd,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linzumi/cli",
3
- "version": "0.0.76-beta",
3
+ "version": "0.0.77-beta",
4
4
  "description": "Linzumi CLI — point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
5
5
  "type": "module",
6
6
  "bin": {