@lesscap/baton-cli 0.2.3 → 0.2.4

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 (2) hide show
  1. package/dist/baton.mjs +128 -5
  2. package/package.json +1 -1
package/dist/baton.mjs CHANGED
@@ -550,6 +550,7 @@ var requirementClient = (baseUrl) => {
550
550
  listByProject: (projectId) => request(u(`/projects/${projectId}/requirements`), { method: "GET" }),
551
551
  get: (id) => request(u(`/requirements/${id}`), { method: "GET" }),
552
552
  getByCode: (projectId, code) => fetchItemByCode(baseUrl, projectId, code, "requirement"),
553
+ update: (id, patch2) => request(u(`/requirements/${id}`), { method: "PATCH", body: patch2 }),
553
554
  setStatus: (id, status) => request(u(`/requirements/${id}`), { method: "PATCH", body: { status } }),
554
555
  remove: (id) => request(u(`/requirements/${id}`), { method: "DELETE" })
555
556
  };
@@ -622,6 +623,7 @@ var taskClient = (baseUrl) => {
622
623
  listByRequirement: (requirementId) => request(u(`/requirements/${requirementId}/tasks`), { method: "GET" }),
623
624
  get: (id) => request(u(`/tasks/${id}`), { method: "GET" }),
624
625
  getByCode: (projectId, code) => fetchItemByCode(baseUrl, projectId, code, "task"),
626
+ update: (id, patch2) => request(u(`/tasks/${id}`), { method: "PATCH", body: patch2 }),
625
627
  setStatus: (id, status) => request(u(`/tasks/${id}`), { method: "PATCH", body: { status } }),
626
628
  remove: (id) => request(u(`/tasks/${id}`), { method: "DELETE" }),
627
629
  listComments: (id) => request(u(`/tasks/${id}/comments`), { method: "GET" }),
@@ -781,6 +783,12 @@ var resolveWorkspaceId = (args) => {
781
783
  "no workspace in scope. pass --workspace <id> or run `baton init` in this directory."
782
784
  );
783
785
  };
786
+ var parseIssueUrl = (url) => {
787
+ const m = url.match(/^https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/(\d+)/);
788
+ if (!m)
789
+ throw new Error(`expected a GitHub issue url (https://github.com/o/r/issues/N), got "${url}"`);
790
+ return { source: "github", number: Number(m[1]), url: url.split(/[?#]/)[0] };
791
+ };
784
792
  var splitCsv = (s) => s ? s.split(",").map((x) => x.trim()).filter(Boolean) : void 0;
785
793
 
786
794
  // src/commands/init.ts
@@ -940,6 +948,7 @@ var project = defineCommand({
940
948
 
941
949
  // src/commands/requirement.ts
942
950
  var createRequirement = (c, input, json) => c.requirements.create(input).then((r) => renderOne(r, fmtRequirement, json));
951
+ var updateRequirement = (c, id, patch2, json) => c.requirements.update(id, patch2).then((r) => renderOne(r, fmtRequirement, json));
943
952
  var listRequirements = (c, projectId, json) => c.requirements.listByProject(projectId).then((rs) => renderList(rs, fmtRequirement, json));
944
953
  var getRequirement = (c, id, json) => c.requirements.get(id).then((r) => renderOne(r, fmtRequirement, json));
945
954
  var setRequirementStatus = (c, id, status, json) => c.requirements.setStatus(id, status).then((r) => renderOne(r, fmtRequirement, json));
@@ -955,6 +964,7 @@ var requirement = defineCommand({
955
964
  project: { type: "string", description: "project id (overrides .baton.json)" },
956
965
  desc: { type: "string", description: "description" },
957
966
  body: { type: "string", description: "requirement body (markdown)" },
967
+ github: { type: "string", description: "link a GitHub issue url (light association)" },
958
968
  ...common
959
969
  },
960
970
  run: async ({ args }) => {
@@ -965,13 +975,69 @@ var requirement = defineCommand({
965
975
  projectId: resolveProjectId(args),
966
976
  title: args.title,
967
977
  description: args.desc,
968
- body: args.body
978
+ body: args.body,
979
+ external: args.github ? parseIssueUrl(args.github) : void 0
969
980
  },
970
981
  Boolean(args.json)
971
982
  )
972
983
  );
973
984
  }
974
985
  }),
986
+ update: defineCommand({
987
+ meta: { name: "update", description: "update a requirement title / body" },
988
+ args: {
989
+ code: { type: "positional", required: true, description: "requirement code (R-N)" },
990
+ title: { type: "string", description: "new title" },
991
+ body: { type: "string", description: "new body (markdown)" },
992
+ project: { type: "string", description: "project id (overrides .baton.json)" },
993
+ ...common
994
+ },
995
+ run: async ({ args }) => {
996
+ const patch2 = {
997
+ ...args.title !== void 0 ? { title: args.title } : {},
998
+ ...args.body !== void 0 ? { body: args.body } : {}
999
+ };
1000
+ if (Object.keys(patch2).length === 0) throw new Error("pass --title and/or --body");
1001
+ const c = clientFor(args);
1002
+ const id = await resolveByCode(c, resolveProjectId(args), args.code);
1003
+ console.log(await updateRequirement(c, id, patch2, Boolean(args.json)));
1004
+ }
1005
+ }),
1006
+ link: defineCommand({
1007
+ meta: { name: "link", description: "link a requirement to a GitHub issue url" },
1008
+ args: {
1009
+ code: { type: "positional", required: true, description: "requirement code (R-N)" },
1010
+ // Named `issue` (not `url`) — `url` would collide with the common --url flag.
1011
+ issue: { type: "positional", required: true, description: "GitHub issue url" },
1012
+ project: { type: "string", description: "project id (overrides .baton.json)" },
1013
+ ...common
1014
+ },
1015
+ run: async ({ args }) => {
1016
+ const c = clientFor(args);
1017
+ const id = await resolveByCode(c, resolveProjectId(args), args.code);
1018
+ console.log(
1019
+ await updateRequirement(
1020
+ c,
1021
+ id,
1022
+ { external: parseIssueUrl(args.issue) },
1023
+ Boolean(args.json)
1024
+ )
1025
+ );
1026
+ }
1027
+ }),
1028
+ unlink: defineCommand({
1029
+ meta: { name: "unlink", description: "clear a requirement\u2019s GitHub issue association" },
1030
+ args: {
1031
+ code: { type: "positional", required: true, description: "requirement code (R-N)" },
1032
+ project: { type: "string", description: "project id (overrides .baton.json)" },
1033
+ ...common
1034
+ },
1035
+ run: async ({ args }) => {
1036
+ const c = clientFor(args);
1037
+ const id = await resolveByCode(c, resolveProjectId(args), args.code);
1038
+ console.log(await updateRequirement(c, id, { external: null }, Boolean(args.json)));
1039
+ }
1040
+ }),
975
1041
  ls: defineCommand({
976
1042
  meta: { name: "ls", description: "list requirements in a project" },
977
1043
  args: {
@@ -2222,6 +2288,7 @@ var session = defineCommand({
2222
2288
 
2223
2289
  // src/commands/task.ts
2224
2290
  var createTask = (c, input, json) => c.tasks.create(input).then((t) => renderOne(t, fmtTask, json));
2291
+ var updateTask = (c, id, patch2, json) => c.tasks.update(id, patch2).then((t) => renderOne(t, fmtTask, json));
2225
2292
  var listTasks = (c, requirementId, json) => c.tasks.listByRequirement(requirementId).then((ts) => renderList(ts, fmtTask, json));
2226
2293
  var getTask = (c, id, json) => c.tasks.get(id).then((t) => renderOne(t, fmtTask, json));
2227
2294
  var setTaskStatus = (c, id, status, json) => c.tasks.setStatus(id, status).then((t) => renderOne(t, fmtTask, json));
@@ -2241,6 +2308,7 @@ var task = defineCommand({
2241
2308
  project: { type: "string", description: "project id (overrides .baton.json)" },
2242
2309
  body: { type: "string", description: "task body (markdown)" },
2243
2310
  deps: { type: "string", description: "comma-separated dependency task codes (T-N,T-N)" },
2311
+ github: { type: "string", description: "link a GitHub issue url (light association)" },
2244
2312
  ...common
2245
2313
  },
2246
2314
  run: async ({ args }) => {
@@ -2258,13 +2326,64 @@ var task = defineCommand({
2258
2326
  requirementId,
2259
2327
  title: args.title,
2260
2328
  body: args.body,
2261
- dependsOn: dependsOn.length ? dependsOn : void 0
2329
+ dependsOn: dependsOn.length ? dependsOn : void 0,
2330
+ external: args.github ? parseIssueUrl(args.github) : void 0
2262
2331
  },
2263
2332
  Boolean(args.json)
2264
2333
  )
2265
2334
  );
2266
2335
  }
2267
2336
  }),
2337
+ update: defineCommand({
2338
+ meta: { name: "update", description: "update a task title / body" },
2339
+ args: {
2340
+ code: { type: "positional", required: true, description: "task code (T-N)" },
2341
+ title: { type: "string", description: "new title" },
2342
+ body: { type: "string", description: "new body (markdown)" },
2343
+ project: { type: "string", description: "project id (overrides .baton.json)" },
2344
+ ...common
2345
+ },
2346
+ run: async ({ args }) => {
2347
+ const patch2 = {
2348
+ ...args.title !== void 0 ? { title: args.title } : {},
2349
+ ...args.body !== void 0 ? { body: args.body } : {}
2350
+ };
2351
+ if (Object.keys(patch2).length === 0) throw new Error("pass --title and/or --body");
2352
+ const c = clientFor(args);
2353
+ const id = await resolveTaskByCode(c, resolveProjectId(args), args.code);
2354
+ console.log(await updateTask(c, id, patch2, Boolean(args.json)));
2355
+ }
2356
+ }),
2357
+ link: defineCommand({
2358
+ meta: { name: "link", description: "link a task to a GitHub issue url" },
2359
+ args: {
2360
+ code: { type: "positional", required: true, description: "task code (T-N)" },
2361
+ // Named `issue` (not `url`) — `url` would collide with the common --url flag.
2362
+ issue: { type: "positional", required: true, description: "GitHub issue url" },
2363
+ project: { type: "string", description: "project id (overrides .baton.json)" },
2364
+ ...common
2365
+ },
2366
+ run: async ({ args }) => {
2367
+ const c = clientFor(args);
2368
+ const id = await resolveTaskByCode(c, resolveProjectId(args), args.code);
2369
+ console.log(
2370
+ await updateTask(c, id, { external: parseIssueUrl(args.issue) }, Boolean(args.json))
2371
+ );
2372
+ }
2373
+ }),
2374
+ unlink: defineCommand({
2375
+ meta: { name: "unlink", description: "clear a task\u2019s GitHub issue association" },
2376
+ args: {
2377
+ code: { type: "positional", required: true, description: "task code (T-N)" },
2378
+ project: { type: "string", description: "project id (overrides .baton.json)" },
2379
+ ...common
2380
+ },
2381
+ run: async ({ args }) => {
2382
+ const c = clientFor(args);
2383
+ const id = await resolveTaskByCode(c, resolveProjectId(args), args.code);
2384
+ console.log(await updateTask(c, id, { external: null }, Boolean(args.json)));
2385
+ }
2386
+ }),
2268
2387
  ls: defineCommand({
2269
2388
  meta: { name: "ls", description: "list tasks in a requirement" },
2270
2389
  args: {
@@ -2294,7 +2413,7 @@ var task = defineCommand({
2294
2413
  "set-status": defineCommand({
2295
2414
  meta: {
2296
2415
  name: "set-status",
2297
- description: "set task status (todo|in_progress|done|failed|cancelled)"
2416
+ description: "set task status (todo|in_progress|blocked|done|failed|cancelled)"
2298
2417
  },
2299
2418
  args: {
2300
2419
  code: { type: "positional", required: true, description: "task code (T-N)" },
@@ -2372,7 +2491,7 @@ import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
2372
2491
  // src/session/runner/title.ts
2373
2492
  var clip = (s, max) => s.length > max ? s.slice(0, max) : s;
2374
2493
  var isDeclined = (title) => title.replace(/[^a-z]/gi, "").toUpperCase() === "NONE";
2375
- var sanitizeTitle = (raw) => raw.replace(/[\r\n]+/g, " ").replace(/["'`]/g, "").replace(/^\s*(title|session)\s*[:-]\s*/i, "").replace(/\s+/g, " ").trim().slice(0, 40).trim();
2494
+ var sanitizeTitle = (raw) => raw.replace(/[\r\n]+/g, " ").replace(/[*_`~#>]/g, "").replace(/["']/g, "").replace(/^\s*(?:title|session|标题)\s*[::-]\s*/i, "").replace(/^\s*(?:[-*•]|\d+[.)、])\s+/, "").replace(/\s+/g, " ").trim().slice(0, 30).replace(/[\s。.,,、;;::!!??.]+$/, "").trim();
2376
2495
  var TITLE_TIMEOUT_MS = 3e4;
2377
2496
  var generateTitle = async (input) => {
2378
2497
  const { worktreePath, userText, assistantText, queryFn } = input;
@@ -2381,7 +2500,8 @@ var generateTitle = async (input) => {
2381
2500
  `User: ${clip(userText, 800)}`,
2382
2501
  assistantText ? `Assistant: ${clip(assistantText, 800)}` : ""
2383
2502
  ].filter(Boolean).join("\n");
2384
- const prompt = `Reply with ONLY a 2-5 word title (no quotes, no punctuation) summarizing this coding session. If it is too short or generic to title meaningfully, reply with exactly NONE:
2503
+ const prompt = `Reply with ONLY a short title for this session: a noun phrase like a filename \u2014 at most 6 words or ~12 Chinese characters. It must NOT be a sentence, no punctuation, no markdown, no quotes, and never an opener like "\u597D\u7684"/"\u6536\u5230"/"I'll". If it is too thin or generic to title meaningfully, reply with exactly NONE.
2504
+
2385
2505
  ${exchange}`;
2386
2506
  const exe = claudeExecutable();
2387
2507
  const abort = new AbortController();
@@ -2392,6 +2512,9 @@ ${exchange}`;
2392
2512
  prompt,
2393
2513
  options: {
2394
2514
  cwd: worktreePath,
2515
+ // Don't load the worktree's CLAUDE.md / project settings — they steer the
2516
+ // model into verbose summaries instead of a terse title.
2517
+ settingSources: [],
2395
2518
  permissionMode: "bypassPermissions",
2396
2519
  allowedTools: [],
2397
2520
  includePartialMessages: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lesscap/baton-cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "type": "module",
5
5
  "description": "baton CLI — register/run a worker, manage projects/requirements/tasks/sessions",
6
6
  "bin": {