@hasna/todos 0.11.84 → 0.11.85
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/cli/cloud-router.d.ts +73 -0
- package/dist/cli/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/agent-commands.d.ts.map +1 -1
- package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
- package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +529 -105
- package/dist/index.js +88 -7
- package/dist/mcp/index.js +227 -1
- package/dist/mcp/tools/agents.d.ts.map +1 -1
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +236 -10
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/interfaces.d.ts +67 -0
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage.js +82 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -8587,6 +8587,54 @@ async function cloudRegisterAgent(client, input) {
|
|
|
8587
8587
|
}
|
|
8588
8588
|
return raw;
|
|
8589
8589
|
}
|
|
8590
|
+
async function cloudHeartbeatAgent(client, idOrName) {
|
|
8591
|
+
const raw = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/heartbeat`, {});
|
|
8592
|
+
if (raw && typeof raw === "object" && "agent" in raw) {
|
|
8593
|
+
return raw.agent;
|
|
8594
|
+
}
|
|
8595
|
+
return raw ?? null;
|
|
8596
|
+
}
|
|
8597
|
+
async function cloudReleaseAgent(client, idOrName, sessionId) {
|
|
8598
|
+
const raw = await client.transport.post(`/agents/${encodeURIComponent(idOrName)}/release`, sessionId ? { session_id: sessionId } : {});
|
|
8599
|
+
const env = raw ?? {};
|
|
8600
|
+
return { agent: env.agent ?? null, released: env.released !== false };
|
|
8601
|
+
}
|
|
8602
|
+
async function cloudLinkCommit(client, taskId, input) {
|
|
8603
|
+
const raw = await client.transport.post(`/tasks/${encodeURIComponent(taskId)}/commits`, input);
|
|
8604
|
+
if (raw && typeof raw === "object" && "commit" in raw) {
|
|
8605
|
+
return raw.commit;
|
|
8606
|
+
}
|
|
8607
|
+
return raw;
|
|
8608
|
+
}
|
|
8609
|
+
async function cloudFindCommit(client, sha) {
|
|
8610
|
+
const raw = await client.transport.get(`/commits/${encodeURIComponent(sha)}`);
|
|
8611
|
+
const env = raw ?? {};
|
|
8612
|
+
return env.commit ?? null;
|
|
8613
|
+
}
|
|
8614
|
+
async function cloudLinkRef(client, taskId, input) {
|
|
8615
|
+
const raw = await client.transport.post(`/tasks/${encodeURIComponent(taskId)}/refs`, input);
|
|
8616
|
+
if (raw && typeof raw === "object" && "ref" in raw) {
|
|
8617
|
+
return raw.ref;
|
|
8618
|
+
}
|
|
8619
|
+
return raw;
|
|
8620
|
+
}
|
|
8621
|
+
async function cloudFindRefs(client, ref) {
|
|
8622
|
+
const raw = await client.transport.get(`/refs/${encodeURIComponent(ref)}`);
|
|
8623
|
+
const env = raw ?? {};
|
|
8624
|
+
return Array.isArray(env.refs) ? env.refs : [];
|
|
8625
|
+
}
|
|
8626
|
+
async function cloudResolvePlan(client, ref, projectId) {
|
|
8627
|
+
const direct = await client.get("plans", ref).catch(() => null);
|
|
8628
|
+
if (direct) {
|
|
8629
|
+
const env = direct;
|
|
8630
|
+
if (env.plan)
|
|
8631
|
+
return env.plan;
|
|
8632
|
+
if (direct.id)
|
|
8633
|
+
return direct;
|
|
8634
|
+
}
|
|
8635
|
+
const plans = await cloudListPlans(client, projectId);
|
|
8636
|
+
return plans.find((p) => p.id === ref) ?? plans.find((p) => p.slug === ref) ?? plans.find((p) => p.name === ref) ?? plans.find((p) => p.id.startsWith(ref)) ?? null;
|
|
8637
|
+
}
|
|
8590
8638
|
async function cloudLockTask(client, id, agentId) {
|
|
8591
8639
|
const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/lock`, { agent_id: agentId });
|
|
8592
8640
|
if (raw && typeof raw === "object" && "result" in raw) {
|
|
@@ -21094,28 +21142,54 @@ ${chalk2.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21094
21142
|
console.log(formatTaskLine(task2));
|
|
21095
21143
|
}
|
|
21096
21144
|
});
|
|
21097
|
-
program2.command("approve <id>").description("Approve a task that requires approval").action((id) => {
|
|
21145
|
+
program2.command("approve <id>").description("Approve a task that requires approval").action(async (id) => {
|
|
21098
21146
|
const globalOpts = program2.opts();
|
|
21099
|
-
const
|
|
21100
|
-
const task2 = getTask(resolvedId);
|
|
21101
|
-
if (!task2) {
|
|
21102
|
-
console.error(chalk2.red(`Task not found: ${id}`));
|
|
21103
|
-
process.exit(1);
|
|
21104
|
-
}
|
|
21105
|
-
if (!task2.requires_approval) {
|
|
21106
|
-
console.log(chalk2.yellow("This task does not require approval."));
|
|
21107
|
-
return;
|
|
21108
|
-
}
|
|
21109
|
-
if (task2.approved_by) {
|
|
21110
|
-
console.log(chalk2.yellow(`Already approved by ${task2.approved_by}.`));
|
|
21111
|
-
return;
|
|
21112
|
-
}
|
|
21147
|
+
const approver = globalOpts.agent || "cli";
|
|
21113
21148
|
try {
|
|
21114
|
-
const
|
|
21149
|
+
const cloud = getTodosCloudClient();
|
|
21150
|
+
if (cloud) {
|
|
21151
|
+
const cloudId = resolveTaskId(id);
|
|
21152
|
+
const task3 = await cloudGetTask(cloud, cloudId);
|
|
21153
|
+
if (!task3) {
|
|
21154
|
+
console.error(chalk2.red(`Task not found: ${id}`));
|
|
21155
|
+
process.exit(1);
|
|
21156
|
+
}
|
|
21157
|
+
if (!task3.requires_approval) {
|
|
21158
|
+
console.log(chalk2.yellow("This task does not require approval."));
|
|
21159
|
+
return;
|
|
21160
|
+
}
|
|
21161
|
+
if (task3.approved_by) {
|
|
21162
|
+
console.log(chalk2.yellow(`Already approved by ${task3.approved_by}.`));
|
|
21163
|
+
return;
|
|
21164
|
+
}
|
|
21165
|
+
const updated2 = await cloudUpdateTask(cloud, cloudId, { approved_by: approver, version: task3.version });
|
|
21166
|
+
if (globalOpts.json) {
|
|
21167
|
+
output(updated2, true);
|
|
21168
|
+
} else {
|
|
21169
|
+
console.log(chalk2.green(`Task approved by ${approver}:`));
|
|
21170
|
+
console.log(formatTaskLine(updated2));
|
|
21171
|
+
}
|
|
21172
|
+
return;
|
|
21173
|
+
}
|
|
21174
|
+
const resolvedId = resolveTaskId(id);
|
|
21175
|
+
const task2 = getTask(resolvedId);
|
|
21176
|
+
if (!task2) {
|
|
21177
|
+
console.error(chalk2.red(`Task not found: ${id}`));
|
|
21178
|
+
process.exit(1);
|
|
21179
|
+
}
|
|
21180
|
+
if (!task2.requires_approval) {
|
|
21181
|
+
console.log(chalk2.yellow("This task does not require approval."));
|
|
21182
|
+
return;
|
|
21183
|
+
}
|
|
21184
|
+
if (task2.approved_by) {
|
|
21185
|
+
console.log(chalk2.yellow(`Already approved by ${task2.approved_by}.`));
|
|
21186
|
+
return;
|
|
21187
|
+
}
|
|
21188
|
+
const updated = updateTask(resolvedId, { approved_by: approver, version: task2.version });
|
|
21115
21189
|
if (globalOpts.json) {
|
|
21116
21190
|
output(updated, true);
|
|
21117
21191
|
} else {
|
|
21118
|
-
console.log(chalk2.green(`Task approved by ${
|
|
21192
|
+
console.log(chalk2.green(`Task approved by ${approver}:`));
|
|
21119
21193
|
console.log(formatTaskLine(updated));
|
|
21120
21194
|
}
|
|
21121
21195
|
} catch (e) {
|
|
@@ -22033,6 +22107,40 @@ function registerPlanTemplateCommands(program2) {
|
|
|
22033
22107
|
return;
|
|
22034
22108
|
}
|
|
22035
22109
|
if (opts.show) {
|
|
22110
|
+
if (cloud) {
|
|
22111
|
+
const plan2 = await cloudResolvePlan(cloud, opts.show, projectId);
|
|
22112
|
+
if (!plan2) {
|
|
22113
|
+
console.error(chalk3.red(`Plan not found: ${opts.show}`));
|
|
22114
|
+
process.exit(1);
|
|
22115
|
+
}
|
|
22116
|
+
const tasks2 = await cloudListTasks(cloud, { plan_id: plan2.id });
|
|
22117
|
+
if (globalOpts.json) {
|
|
22118
|
+
output({ plan: plan2, tasks: tasks2, artifact: null }, true);
|
|
22119
|
+
return;
|
|
22120
|
+
}
|
|
22121
|
+
console.log(chalk3.bold(`Plan Details:
|
|
22122
|
+
`));
|
|
22123
|
+
console.log(` ${chalk3.dim("ID:")} ${plan2.id}`);
|
|
22124
|
+
if (plan2.slug)
|
|
22125
|
+
console.log(` ${chalk3.dim("Slug:")} ${plan2.slug}`);
|
|
22126
|
+
console.log(` ${chalk3.dim("Name:")} ${plan2.name}`);
|
|
22127
|
+
console.log(` ${chalk3.dim("Status:")} ${chalk3.cyan(plan2.status)}`);
|
|
22128
|
+
if (plan2.description)
|
|
22129
|
+
console.log(` ${chalk3.dim("Desc:")} ${plan2.description}`);
|
|
22130
|
+
if (plan2.project_id)
|
|
22131
|
+
console.log(` ${chalk3.dim("Project:")} ${plan2.project_id}`);
|
|
22132
|
+
console.log(` ${chalk3.dim("Created:")} ${plan2.created_at}`);
|
|
22133
|
+
if (tasks2.length > 0) {
|
|
22134
|
+
console.log(chalk3.bold(`
|
|
22135
|
+
Tasks (${tasks2.length}):`));
|
|
22136
|
+
for (const t of tasks2)
|
|
22137
|
+
console.log(` ${formatTaskLine(t)}`);
|
|
22138
|
+
} else {
|
|
22139
|
+
console.log(chalk3.dim(`
|
|
22140
|
+
No tasks in this plan.`));
|
|
22141
|
+
}
|
|
22142
|
+
return;
|
|
22143
|
+
}
|
|
22036
22144
|
const db = getDatabase();
|
|
22037
22145
|
const resolvedId = resolvePlanCliRef(opts.show, projectId);
|
|
22038
22146
|
const plan = getPlan(resolvedId);
|
|
@@ -33132,17 +33240,35 @@ Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
|
|
|
33132
33240
|
console.error(chalk5.red("Agent ID required. Use --agent or pass as argument."));
|
|
33133
33241
|
process.exit(1);
|
|
33134
33242
|
}
|
|
33135
|
-
|
|
33136
|
-
|
|
33137
|
-
|
|
33138
|
-
|
|
33139
|
-
|
|
33140
|
-
|
|
33141
|
-
|
|
33142
|
-
|
|
33143
|
-
|
|
33144
|
-
|
|
33145
|
-
|
|
33243
|
+
try {
|
|
33244
|
+
const cloud = getTodosCloudClient();
|
|
33245
|
+
if (cloud) {
|
|
33246
|
+
const a2 = await cloudHeartbeatAgent(cloud, agentId);
|
|
33247
|
+
if (!a2) {
|
|
33248
|
+
console.error(chalk5.red(`Agent not found: ${agentId}`));
|
|
33249
|
+
process.exit(1);
|
|
33250
|
+
}
|
|
33251
|
+
if (globalOpts.json) {
|
|
33252
|
+
console.log(JSON.stringify({ agent_id: a2.id, name: a2.name, last_seen_at: a2.last_seen_at }));
|
|
33253
|
+
} else {
|
|
33254
|
+
console.log(chalk5.green(`\u2665 ${a2.name} (${a2.id.slice(0, 8)}) \u2014 heartbeat sent`));
|
|
33255
|
+
}
|
|
33256
|
+
return;
|
|
33257
|
+
}
|
|
33258
|
+
const { updateAgentActivity: updateAgentActivity2, getAgent: getAgent2, getAgentByName: getAgentByName2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
33259
|
+
const a = getAgent2(agentId) || getAgentByName2(agentId);
|
|
33260
|
+
if (!a) {
|
|
33261
|
+
console.error(chalk5.red(`Agent not found: ${agentId}`));
|
|
33262
|
+
process.exit(1);
|
|
33263
|
+
}
|
|
33264
|
+
updateAgentActivity2(a.id);
|
|
33265
|
+
if (globalOpts.json) {
|
|
33266
|
+
console.log(JSON.stringify({ agent_id: a.id, name: a.name, last_seen_at: new Date().toISOString() }));
|
|
33267
|
+
} else {
|
|
33268
|
+
console.log(chalk5.green(`\u2665 ${a.name} (${a.id.slice(0, 8)}) \u2014 heartbeat sent`));
|
|
33269
|
+
}
|
|
33270
|
+
} catch (e) {
|
|
33271
|
+
handleError(e);
|
|
33146
33272
|
}
|
|
33147
33273
|
});
|
|
33148
33274
|
program2.command("release [agent]").description("Release/logout an agent \u2014 clears session binding so the name is immediately available").option("--session-id <id>", "Only release if session ID matches").action(async (agent, opts) => {
|
|
@@ -33152,21 +33278,43 @@ Use ${chalk5.cyan(`--agent ${result.id}`)} on future commands.`);
|
|
|
33152
33278
|
console.error(chalk5.red("Agent ID or name required. Use --agent or pass as argument."));
|
|
33153
33279
|
process.exit(1);
|
|
33154
33280
|
}
|
|
33155
|
-
|
|
33156
|
-
|
|
33157
|
-
|
|
33158
|
-
|
|
33159
|
-
|
|
33160
|
-
|
|
33161
|
-
|
|
33162
|
-
|
|
33163
|
-
|
|
33164
|
-
|
|
33165
|
-
|
|
33166
|
-
|
|
33167
|
-
|
|
33168
|
-
|
|
33169
|
-
|
|
33281
|
+
try {
|
|
33282
|
+
const cloud = getTodosCloudClient();
|
|
33283
|
+
if (cloud) {
|
|
33284
|
+
const result = await cloudReleaseAgent(cloud, agentId, opts?.sessionId);
|
|
33285
|
+
if (!result.agent) {
|
|
33286
|
+
console.error(chalk5.red(`Agent not found: ${agentId}`));
|
|
33287
|
+
process.exit(1);
|
|
33288
|
+
}
|
|
33289
|
+
if (!result.released) {
|
|
33290
|
+
console.error(chalk5.red("Release denied: session_id does not match agent's current session."));
|
|
33291
|
+
process.exit(1);
|
|
33292
|
+
}
|
|
33293
|
+
if (globalOpts.json) {
|
|
33294
|
+
console.log(JSON.stringify({ agent_id: result.agent.id, name: result.agent.name, released: true }));
|
|
33295
|
+
} else {
|
|
33296
|
+
console.log(chalk5.green(`\u2713 ${result.agent.name} (${result.agent.id}) released \u2014 name is now available.`));
|
|
33297
|
+
}
|
|
33298
|
+
return;
|
|
33299
|
+
}
|
|
33300
|
+
const { getAgent: getAgent2, getAgentByName: getAgentByName2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
33301
|
+
const a = getAgent2(agentId) || getAgentByName2(agentId);
|
|
33302
|
+
if (!a) {
|
|
33303
|
+
console.error(chalk5.red(`Agent not found: ${agentId}`));
|
|
33304
|
+
process.exit(1);
|
|
33305
|
+
}
|
|
33306
|
+
const released = releaseAgent(a.id, opts?.sessionId);
|
|
33307
|
+
if (!released) {
|
|
33308
|
+
console.error(chalk5.red("Release denied: session_id does not match agent's current session."));
|
|
33309
|
+
process.exit(1);
|
|
33310
|
+
}
|
|
33311
|
+
if (globalOpts.json) {
|
|
33312
|
+
console.log(JSON.stringify({ agent_id: a.id, name: a.name, released: true }));
|
|
33313
|
+
} else {
|
|
33314
|
+
console.log(chalk5.green(`\u2713 ${a.name} (${a.id}) released \u2014 name is now available.`));
|
|
33315
|
+
}
|
|
33316
|
+
} catch (e) {
|
|
33317
|
+
handleError(e);
|
|
33170
33318
|
}
|
|
33171
33319
|
});
|
|
33172
33320
|
program2.command("focus [project]").description("Focus on a project (or clear focus if no project given)").action(async (project) => {
|
|
@@ -38697,6 +38845,16 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
38697
38845
|
add: (input, context) => addVerification(input, store, context),
|
|
38698
38846
|
list: (taskId) => listVerifications(taskId, store)
|
|
38699
38847
|
},
|
|
38848
|
+
commits: {
|
|
38849
|
+
add: (input, context) => addCommit(input, store, context),
|
|
38850
|
+
list: (taskId) => listCommits(taskId, store),
|
|
38851
|
+
find: (sha) => findCommit(sha, store)
|
|
38852
|
+
},
|
|
38853
|
+
gitRefs: {
|
|
38854
|
+
add: (input, context) => addGitRef(input, store, context),
|
|
38855
|
+
list: (taskId) => listGitRefs(taskId, store),
|
|
38856
|
+
find: (ref) => findGitRefs(ref, store)
|
|
38857
|
+
},
|
|
38700
38858
|
projects: {
|
|
38701
38859
|
create: (input, context) => createProject2(input, store, context),
|
|
38702
38860
|
get: (id) => store.get("projects", id),
|
|
@@ -38717,7 +38875,9 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
38717
38875
|
get: (id) => store.get("agents", id),
|
|
38718
38876
|
getByName: async (name) => (await store.list("agents")).find((agent) => agent.name === name) ?? null,
|
|
38719
38877
|
list: async (options2) => (await store.list("agents")).filter((agent) => options2?.include_archived || agent.status !== "archived").sort((a, b) => a.name.localeCompare(b.name)),
|
|
38720
|
-
update: (id, input) => updateAgent2(id, input, store)
|
|
38878
|
+
update: (id, input) => updateAgent2(id, input, store),
|
|
38879
|
+
heartbeat: (idOrName, context) => heartbeatAgent(idOrName, store, context),
|
|
38880
|
+
release: (idOrName, sessionId, context) => releaseAgent2(idOrName, sessionId, store, context)
|
|
38721
38881
|
},
|
|
38722
38882
|
taskLists: {
|
|
38723
38883
|
create: (input, context) => createTaskList2(input, store, context),
|
|
@@ -39247,6 +39407,53 @@ async function addVerification(input, store, context) {
|
|
|
39247
39407
|
async function listVerifications(taskId, store) {
|
|
39248
39408
|
return (await store.list("verifications")).filter((verification) => verification.task_id === taskId).sort((a, b) => b.run_at.localeCompare(a.run_at));
|
|
39249
39409
|
}
|
|
39410
|
+
async function addCommit(input, store, context) {
|
|
39411
|
+
if (!await store.get("tasks", input.task_id))
|
|
39412
|
+
throw new Error(`Task not found: ${input.task_id}`);
|
|
39413
|
+
const timestamp = new Date().toISOString();
|
|
39414
|
+
const commit = {
|
|
39415
|
+
id: randomUUID3(),
|
|
39416
|
+
task_id: input.task_id,
|
|
39417
|
+
sha: input.sha,
|
|
39418
|
+
message: input.message ?? null,
|
|
39419
|
+
author: input.author ?? null,
|
|
39420
|
+
files_changed: input.files_changed ?? null,
|
|
39421
|
+
created_at: timestamp
|
|
39422
|
+
};
|
|
39423
|
+
await store.upsert("commits", { ...commit, updated_at: timestamp }, context);
|
|
39424
|
+
return commit;
|
|
39425
|
+
}
|
|
39426
|
+
async function listCommits(taskId, store) {
|
|
39427
|
+
return (await store.list("commits")).filter((commit) => commit.task_id === taskId).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
39428
|
+
}
|
|
39429
|
+
async function findCommit(sha, store) {
|
|
39430
|
+
const matches = (await store.list("commits")).filter((commit) => commit.sha === sha || commit.sha.startsWith(sha) || sha.startsWith(commit.sha)).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
39431
|
+
return matches[0] ?? null;
|
|
39432
|
+
}
|
|
39433
|
+
async function addGitRef(input, store, context) {
|
|
39434
|
+
if (!await store.get("tasks", input.task_id))
|
|
39435
|
+
throw new Error(`Task not found: ${input.task_id}`);
|
|
39436
|
+
const timestamp = new Date().toISOString();
|
|
39437
|
+
const gitRef = {
|
|
39438
|
+
id: randomUUID3(),
|
|
39439
|
+
task_id: input.task_id,
|
|
39440
|
+
ref_type: input.ref_type,
|
|
39441
|
+
name: input.name,
|
|
39442
|
+
url: input.url ?? null,
|
|
39443
|
+
provider: input.provider ?? null,
|
|
39444
|
+
metadata: input.metadata ?? {},
|
|
39445
|
+
created_at: timestamp,
|
|
39446
|
+
updated_at: timestamp
|
|
39447
|
+
};
|
|
39448
|
+
await store.upsert("refs", gitRef, context);
|
|
39449
|
+
return gitRef;
|
|
39450
|
+
}
|
|
39451
|
+
async function listGitRefs(taskId, store) {
|
|
39452
|
+
return (await store.list("refs")).filter((ref) => ref.task_id === taskId).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
39453
|
+
}
|
|
39454
|
+
async function findGitRefs(ref, store) {
|
|
39455
|
+
return (await store.list("refs")).filter((r) => r.name === ref).sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
39456
|
+
}
|
|
39250
39457
|
function toFilterArray(value) {
|
|
39251
39458
|
return Array.isArray(value) ? value : [value];
|
|
39252
39459
|
}
|
|
@@ -39393,6 +39600,28 @@ async function updateAgent2(id, input, store) {
|
|
|
39393
39600
|
last_seen_at: new Date().toISOString()
|
|
39394
39601
|
});
|
|
39395
39602
|
}
|
|
39603
|
+
async function resolveAgent(idOrName, store) {
|
|
39604
|
+
const byId = await store.get("agents", idOrName);
|
|
39605
|
+
if (byId)
|
|
39606
|
+
return byId;
|
|
39607
|
+
return (await store.list("agents")).find((agent) => agent.name === idOrName) ?? null;
|
|
39608
|
+
}
|
|
39609
|
+
async function heartbeatAgent(idOrName, store, context) {
|
|
39610
|
+
const agent = await resolveAgent(idOrName, store);
|
|
39611
|
+
if (!agent)
|
|
39612
|
+
return null;
|
|
39613
|
+
return store.upsert("agents", { ...agent, last_seen_at: new Date().toISOString() }, context);
|
|
39614
|
+
}
|
|
39615
|
+
async function releaseAgent2(idOrName, sessionId, store, context) {
|
|
39616
|
+
const agent = await resolveAgent(idOrName, store);
|
|
39617
|
+
if (!agent)
|
|
39618
|
+
return null;
|
|
39619
|
+
if (sessionId && agent.session_id && agent.session_id !== sessionId) {
|
|
39620
|
+
return { agent, released: false };
|
|
39621
|
+
}
|
|
39622
|
+
const updated = await store.upsert("agents", { ...agent, session_id: null, last_seen_at: new Date().toISOString() }, context);
|
|
39623
|
+
return { agent: updated, released: true };
|
|
39624
|
+
}
|
|
39396
39625
|
async function createTaskList2(input, store, context) {
|
|
39397
39626
|
const timestamp = new Date().toISOString();
|
|
39398
39627
|
return store.upsert("task_lists", {
|
|
@@ -40277,6 +40506,70 @@ async function handleV1Request(req, url) {
|
|
|
40277
40506
|
}
|
|
40278
40507
|
return error(405, `method ${method} not allowed on /v1/tasks/:id/verifications`);
|
|
40279
40508
|
}
|
|
40509
|
+
if (action === "commits") {
|
|
40510
|
+
if (!store.commits)
|
|
40511
|
+
return error(501, "commit links are not supported by this storage backend");
|
|
40512
|
+
if (method === "GET") {
|
|
40513
|
+
if (!await store.tasks.get(id))
|
|
40514
|
+
return error(404, "task not found");
|
|
40515
|
+
const commits = await store.commits.list(id);
|
|
40516
|
+
return json2({ commits, count: commits.length });
|
|
40517
|
+
}
|
|
40518
|
+
if (method === "POST") {
|
|
40519
|
+
const body2 = await readJson(req) ?? {};
|
|
40520
|
+
if (typeof body2.sha !== "string" || !body2.sha.trim())
|
|
40521
|
+
return error(400, "sha is required");
|
|
40522
|
+
try {
|
|
40523
|
+
const commit = await store.commits.add({
|
|
40524
|
+
task_id: id,
|
|
40525
|
+
sha: body2.sha,
|
|
40526
|
+
message: body2.message,
|
|
40527
|
+
author: body2.author,
|
|
40528
|
+
files_changed: Array.isArray(body2.files_changed) ? body2.files_changed : undefined
|
|
40529
|
+
}, contextFromPrincipal(principal));
|
|
40530
|
+
return json2({ commit }, 201);
|
|
40531
|
+
} catch (e) {
|
|
40532
|
+
const msg = e.message || "";
|
|
40533
|
+
if (msg.includes("not found"))
|
|
40534
|
+
return error(404, msg);
|
|
40535
|
+
throw e;
|
|
40536
|
+
}
|
|
40537
|
+
}
|
|
40538
|
+
return error(405, `method ${method} not allowed on /v1/tasks/:id/commits`);
|
|
40539
|
+
}
|
|
40540
|
+
if (action === "refs") {
|
|
40541
|
+
if (!store.gitRefs)
|
|
40542
|
+
return error(501, "git ref links are not supported by this storage backend");
|
|
40543
|
+
if (method === "GET") {
|
|
40544
|
+
if (!await store.tasks.get(id))
|
|
40545
|
+
return error(404, "task not found");
|
|
40546
|
+
const refs = await store.gitRefs.list(id);
|
|
40547
|
+
return json2({ refs, count: refs.length });
|
|
40548
|
+
}
|
|
40549
|
+
if (method === "POST") {
|
|
40550
|
+
const body2 = await readJson(req) ?? {};
|
|
40551
|
+
const refType = body2.ref_type === "pull_request" || body2.ref_type === "branch" ? body2.ref_type : "branch";
|
|
40552
|
+
if (typeof body2.name !== "string" || !body2.name.trim())
|
|
40553
|
+
return error(400, "name is required");
|
|
40554
|
+
try {
|
|
40555
|
+
const ref = await store.gitRefs.add({
|
|
40556
|
+
task_id: id,
|
|
40557
|
+
ref_type: refType,
|
|
40558
|
+
name: body2.name,
|
|
40559
|
+
url: body2.url,
|
|
40560
|
+
provider: body2.provider,
|
|
40561
|
+
metadata: body2.metadata
|
|
40562
|
+
}, contextFromPrincipal(principal));
|
|
40563
|
+
return json2({ ref }, 201);
|
|
40564
|
+
} catch (e) {
|
|
40565
|
+
const msg = e.message || "";
|
|
40566
|
+
if (msg.includes("not found"))
|
|
40567
|
+
return error(404, msg);
|
|
40568
|
+
throw e;
|
|
40569
|
+
}
|
|
40570
|
+
}
|
|
40571
|
+
return error(405, `method ${method} not allowed on /v1/tasks/:id/refs`);
|
|
40572
|
+
}
|
|
40280
40573
|
const body = await readJson(req) ?? {};
|
|
40281
40574
|
const agentId = body.agent_id || principal.agent || "todos-serve";
|
|
40282
40575
|
if (action === "start" && method === "POST") {
|
|
@@ -40389,6 +40682,30 @@ async function handleV1Request(req, url) {
|
|
|
40389
40682
|
}
|
|
40390
40683
|
return json2({ agent: result }, 201);
|
|
40391
40684
|
}
|
|
40685
|
+
if (id && action === "heartbeat") {
|
|
40686
|
+
if (method !== "POST")
|
|
40687
|
+
return error(405, `method ${method} not allowed on /v1/agents/:id/heartbeat`);
|
|
40688
|
+
if (typeof store.agents.heartbeat !== "function") {
|
|
40689
|
+
return error(501, "agent heartbeat is not supported by this storage backend");
|
|
40690
|
+
}
|
|
40691
|
+
const agent = await store.agents.heartbeat(id, contextFromPrincipal(principal));
|
|
40692
|
+
return agent ? json2({ agent }) : error(404, "agent not found");
|
|
40693
|
+
}
|
|
40694
|
+
if (id && action === "release") {
|
|
40695
|
+
if (method !== "POST")
|
|
40696
|
+
return error(405, `method ${method} not allowed on /v1/agents/:id/release`);
|
|
40697
|
+
if (typeof store.agents.release !== "function") {
|
|
40698
|
+
return error(501, "agent release is not supported by this storage backend");
|
|
40699
|
+
}
|
|
40700
|
+
const body = await readJson(req) ?? {};
|
|
40701
|
+
const result = await store.agents.release(id, body.session_id, contextFromPrincipal(principal));
|
|
40702
|
+
if (!result)
|
|
40703
|
+
return error(404, "agent not found");
|
|
40704
|
+
if (!result.released) {
|
|
40705
|
+
return error(409, "release denied: session_id does not match agent's current session", { released: false });
|
|
40706
|
+
}
|
|
40707
|
+
return json2({ agent: result.agent, released: true });
|
|
40708
|
+
}
|
|
40392
40709
|
if (id && method === "GET") {
|
|
40393
40710
|
const agent = await store.agents.get(id);
|
|
40394
40711
|
return agent ? json2({ agent }) : error(404, "agent not found");
|
|
@@ -40418,6 +40735,22 @@ async function handleV1Request(req, url) {
|
|
|
40418
40735
|
const dependencies = await store.dependencies.listAll();
|
|
40419
40736
|
return json2({ dependencies, count: dependencies.length });
|
|
40420
40737
|
}
|
|
40738
|
+
if (resource === "commits" && id) {
|
|
40739
|
+
if (method !== "GET")
|
|
40740
|
+
return error(405, `method ${method} not allowed on /v1/commits/:sha`);
|
|
40741
|
+
if (!store.commits)
|
|
40742
|
+
return error(501, "commit links are not supported by this storage backend");
|
|
40743
|
+
const commit = await store.commits.find(id);
|
|
40744
|
+
return json2({ commit: commit ?? null });
|
|
40745
|
+
}
|
|
40746
|
+
if (resource === "refs" && id) {
|
|
40747
|
+
if (method !== "GET")
|
|
40748
|
+
return error(405, `method ${method} not allowed on /v1/refs/:ref`);
|
|
40749
|
+
if (!store.gitRefs)
|
|
40750
|
+
return error(501, "git ref links are not supported by this storage backend");
|
|
40751
|
+
const refs = await store.gitRefs.find(id);
|
|
40752
|
+
return json2({ refs, count: refs.length });
|
|
40753
|
+
}
|
|
40421
40754
|
if (resource === "next" && !id) {
|
|
40422
40755
|
if (method !== "GET")
|
|
40423
40756
|
return error(405, `method ${method} not allowed on /v1/next`);
|
|
@@ -45774,7 +46107,7 @@ function resolveRun(parsed, db) {
|
|
|
45774
46107
|
}
|
|
45775
46108
|
return resolution;
|
|
45776
46109
|
}
|
|
45777
|
-
function
|
|
46110
|
+
function resolveAgent2(parsed, db) {
|
|
45778
46111
|
const resolution = blankResolution(parsed);
|
|
45779
46112
|
const id = resolvePartialId(db, "agents", parsed.target);
|
|
45780
46113
|
const agent = id ? getAgent(id, db) : getAgentByName(parsed.target, db);
|
|
@@ -45811,7 +46144,7 @@ function resolveTask(parsed, db) {
|
|
|
45811
46144
|
function resolveUnknown(parsed, workspace, db, maxSymbolMatches) {
|
|
45812
46145
|
const attempts = [
|
|
45813
46146
|
() => resolveTask({ ...parsed, kind: "task" }, db),
|
|
45814
|
-
() =>
|
|
46147
|
+
() => resolveAgent2({ ...parsed, kind: "agent" }, db),
|
|
45815
46148
|
() => resolvePlan({ ...parsed, kind: "plan" }, db),
|
|
45816
46149
|
() => resolveSymbol({ ...parsed, kind: "symbol" }, workspace, maxSymbolMatches)
|
|
45817
46150
|
];
|
|
@@ -45846,7 +46179,7 @@ function resolveMentions(input, db) {
|
|
|
45846
46179
|
case "run":
|
|
45847
46180
|
return resolveRun(parsed, d);
|
|
45848
46181
|
case "agent":
|
|
45849
|
-
return
|
|
46182
|
+
return resolveAgent2(parsed, d);
|
|
45850
46183
|
case "task":
|
|
45851
46184
|
return resolveTask(parsed, d);
|
|
45852
46185
|
default:
|
|
@@ -55660,7 +55993,7 @@ function resolveProjectId(value, db) {
|
|
|
55660
55993
|
return null;
|
|
55661
55994
|
return resolvePartialId(db, "projects", value) || value;
|
|
55662
55995
|
}
|
|
55663
|
-
function
|
|
55996
|
+
function resolveAgent3(agentId, db) {
|
|
55664
55997
|
return db.query("SELECT id, name FROM agents WHERE id = ? OR LOWER(name) = LOWER(?)").get(agentId, agentId);
|
|
55665
55998
|
}
|
|
55666
55999
|
function agentTaskWhere(agent, options, db) {
|
|
@@ -55704,7 +56037,7 @@ function scoreHandoffs(created, withTaskRefs, withBlockers) {
|
|
|
55704
56037
|
}
|
|
55705
56038
|
function getAgentReliabilityScorecard(agentId, options = {}, db) {
|
|
55706
56039
|
const d = db || getDatabase();
|
|
55707
|
-
const agent =
|
|
56040
|
+
const agent = resolveAgent3(agentId, d);
|
|
55708
56041
|
if (!agent)
|
|
55709
56042
|
return null;
|
|
55710
56043
|
const generatedAt = now();
|
|
@@ -55873,7 +56206,7 @@ function getAgentReliabilityScorecard(agentId, options = {}, db) {
|
|
|
55873
56206
|
}
|
|
55874
56207
|
function listAgentReliabilityScorecards(options = {}, db) {
|
|
55875
56208
|
const d = db || getDatabase();
|
|
55876
|
-
const agents = options.agent_id ?
|
|
56209
|
+
const agents = options.agent_id ? resolveAgent3(options.agent_id, d) ? [resolveAgent3(options.agent_id, d)] : [] : d.query("SELECT id, name FROM agents ORDER BY name").all();
|
|
55877
56210
|
const limit = Number.isFinite(options.limit) && options.limit > 0 ? Math.min(Math.floor(options.limit), 500) : 50;
|
|
55878
56211
|
return agents.map((agent) => getAgentReliabilityScorecard(agent.id, options, d)).filter((scorecard) => Boolean(scorecard)).filter((scorecard) => {
|
|
55879
56212
|
const signals = scorecard.signals;
|
|
@@ -63244,6 +63577,19 @@ ID: ${updated.id}${taskNote}`
|
|
|
63244
63577
|
agent_id: exports_external3.string().describe("Your agent ID or name.")
|
|
63245
63578
|
}, async ({ agent_id }) => {
|
|
63246
63579
|
try {
|
|
63580
|
+
const cloud = getTodosCloudClient();
|
|
63581
|
+
if (cloud) {
|
|
63582
|
+
const a = await cloudHeartbeatAgent(cloud, agent_id);
|
|
63583
|
+
if (!a) {
|
|
63584
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
63585
|
+
}
|
|
63586
|
+
return {
|
|
63587
|
+
content: [{
|
|
63588
|
+
type: "text",
|
|
63589
|
+
text: `Heartbeat: ${a.name} (${a.id}) \u2014 last_seen_at updated to ${a.last_seen_at}`
|
|
63590
|
+
}]
|
|
63591
|
+
};
|
|
63592
|
+
}
|
|
63247
63593
|
const agent = getAgent(agent_id) || getAgentByName(agent_id);
|
|
63248
63594
|
if (!agent) {
|
|
63249
63595
|
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
@@ -63266,6 +63612,22 @@ ID: ${updated.id}${taskNote}`
|
|
|
63266
63612
|
session_id: exports_external3.string().optional().describe("Your session ID \u2014 if provided, release only succeeds if it matches (prevents other sessions from releasing your agent).")
|
|
63267
63613
|
}, async ({ agent_id, session_id }) => {
|
|
63268
63614
|
try {
|
|
63615
|
+
const cloud = getTodosCloudClient();
|
|
63616
|
+
if (cloud) {
|
|
63617
|
+
const result = await cloudReleaseAgent(cloud, agent_id, session_id);
|
|
63618
|
+
if (!result.agent) {
|
|
63619
|
+
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
63620
|
+
}
|
|
63621
|
+
if (!result.released) {
|
|
63622
|
+
return { content: [{ type: "text", text: `Release denied: session_id does not match agent's current session.` }], isError: true };
|
|
63623
|
+
}
|
|
63624
|
+
return {
|
|
63625
|
+
content: [{
|
|
63626
|
+
type: "text",
|
|
63627
|
+
text: `Agent released: ${result.agent.name} (${result.agent.id}) \u2014 session cleared, name is now available.`
|
|
63628
|
+
}]
|
|
63629
|
+
};
|
|
63630
|
+
}
|
|
63269
63631
|
const agent = getAgent(agent_id) || getAgentByName(agent_id);
|
|
63270
63632
|
if (!agent) {
|
|
63271
63633
|
return { content: [{ type: "text", text: `Agent not found: ${agent_id}` }], isError: true };
|
|
@@ -71789,38 +72151,68 @@ exit 0
|
|
|
71789
72151
|
program2.command("link-commit <task-id> <sha>").description("Link a git commit to a task").option("--message <text>", "Commit message").option("--author <name>", "Commit author").option("--files <list>", "Comma-separated list of changed files").action(async (taskId, sha, opts) => {
|
|
71790
72152
|
const globalOpts = program2.opts();
|
|
71791
72153
|
const resolvedId = resolveTaskId(taskId);
|
|
71792
|
-
const
|
|
71793
|
-
|
|
71794
|
-
|
|
71795
|
-
|
|
71796
|
-
|
|
71797
|
-
|
|
71798
|
-
|
|
71799
|
-
|
|
71800
|
-
|
|
71801
|
-
|
|
71802
|
-
|
|
72154
|
+
const files = opts.files ? opts.files.split(",").filter(Boolean) : undefined;
|
|
72155
|
+
try {
|
|
72156
|
+
const cloud = getTodosCloudClient();
|
|
72157
|
+
const commit = cloud ? await cloudLinkCommit(cloud, resolvedId, {
|
|
72158
|
+
sha,
|
|
72159
|
+
...opts.message !== undefined ? { message: opts.message } : {},
|
|
72160
|
+
...opts.author !== undefined ? { author: opts.author } : {},
|
|
72161
|
+
...files ? { files_changed: files } : {}
|
|
72162
|
+
}) : (await Promise.resolve().then(() => (init_task_commits(), exports_task_commits))).linkTaskToCommit({
|
|
72163
|
+
task_id: resolvedId,
|
|
72164
|
+
sha,
|
|
72165
|
+
message: opts.message,
|
|
72166
|
+
author: opts.author,
|
|
72167
|
+
files_changed: files
|
|
72168
|
+
});
|
|
72169
|
+
if (globalOpts.json) {
|
|
72170
|
+
output(commit, true);
|
|
72171
|
+
return;
|
|
72172
|
+
}
|
|
72173
|
+
console.log(chalk8.green(`Linked commit ${sha.slice(0, 7)} to task ${taskId}`));
|
|
72174
|
+
} catch (e) {
|
|
72175
|
+
handleError(e);
|
|
71803
72176
|
}
|
|
71804
|
-
console.log(chalk8.green(`Linked commit ${sha.slice(0, 7)} to task ${taskId}`));
|
|
71805
72177
|
});
|
|
71806
72178
|
program2.command("find-commit <sha>").description("Find which task explains a git commit SHA").action(async (sha) => {
|
|
71807
72179
|
const globalOpts = program2.opts();
|
|
71808
|
-
|
|
71809
|
-
|
|
71810
|
-
|
|
71811
|
-
|
|
71812
|
-
|
|
71813
|
-
|
|
71814
|
-
|
|
71815
|
-
|
|
71816
|
-
|
|
71817
|
-
|
|
72180
|
+
try {
|
|
72181
|
+
const cloud = getTodosCloudClient();
|
|
72182
|
+
if (cloud) {
|
|
72183
|
+
const commit = await cloudFindCommit(cloud, sha);
|
|
72184
|
+
if (globalOpts.json) {
|
|
72185
|
+
output(commit ? { task_id: commit.task_id, commit } : null, true);
|
|
72186
|
+
return;
|
|
72187
|
+
}
|
|
72188
|
+
if (!commit) {
|
|
72189
|
+
console.log(chalk8.dim(`No task linked to commit ${sha}.`));
|
|
72190
|
+
return;
|
|
72191
|
+
}
|
|
72192
|
+
console.log(`${chalk8.yellow(commit.sha.slice(0, 7))} -> ${chalk8.cyan(commit.task_id)}`);
|
|
72193
|
+
if (commit.message)
|
|
72194
|
+
console.log(chalk8.dim(` ${commit.message}`));
|
|
72195
|
+
return;
|
|
72196
|
+
}
|
|
72197
|
+
const { findTaskByCommit: findTaskByCommit2 } = await Promise.resolve().then(() => (init_task_commits(), exports_task_commits));
|
|
72198
|
+
const { getTask: getTask3 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
72199
|
+
const result = findTaskByCommit2(sha);
|
|
72200
|
+
if (globalOpts.json) {
|
|
72201
|
+
output(result, true);
|
|
72202
|
+
return;
|
|
72203
|
+
}
|
|
72204
|
+
if (!result) {
|
|
72205
|
+
console.log(chalk8.dim(`No task linked to commit ${sha}.`));
|
|
72206
|
+
return;
|
|
72207
|
+
}
|
|
72208
|
+
const task2 = getTask3(result.task_id);
|
|
72209
|
+
const taskLabel = task2 ? `${task2.short_id || task2.id.slice(0, 8)} ${task2.title}` : result.task_id;
|
|
72210
|
+
console.log(`${chalk8.yellow(result.commit.sha.slice(0, 7))} -> ${chalk8.cyan(taskLabel)}`);
|
|
72211
|
+
if (result.commit.message)
|
|
72212
|
+
console.log(chalk8.dim(` ${result.commit.message}`));
|
|
72213
|
+
} catch (e) {
|
|
72214
|
+
handleError(e);
|
|
71818
72215
|
}
|
|
71819
|
-
const task2 = getTask3(result.task_id);
|
|
71820
|
-
const taskLabel = task2 ? `${task2.short_id || task2.id.slice(0, 8)} ${task2.title}` : result.task_id;
|
|
71821
|
-
console.log(`${chalk8.yellow(result.commit.sha.slice(0, 7))} -> ${chalk8.cyan(taskLabel)}`);
|
|
71822
|
-
if (result.commit.message)
|
|
71823
|
-
console.log(chalk8.dim(` ${result.commit.message}`));
|
|
71824
72216
|
});
|
|
71825
72217
|
program2.command("link-ref <task-id> <ref>").description("Link a git branch or pull request to a task").option("--type <type>", "Ref type: branch or pull_request", "branch").option("--url <url>", "Remote URL for the branch or pull request").option("--provider <name>", "Provider name, e.g. git or github").option("--metadata <json>", "Additional JSON metadata").action(async (taskId, ref, opts) => {
|
|
71826
72218
|
const globalOpts = program2.opts();
|
|
@@ -71840,38 +72232,70 @@ exit 0
|
|
|
71840
72232
|
process.exit(1);
|
|
71841
72233
|
}
|
|
71842
72234
|
}
|
|
71843
|
-
|
|
71844
|
-
|
|
71845
|
-
|
|
71846
|
-
|
|
71847
|
-
|
|
71848
|
-
|
|
71849
|
-
|
|
71850
|
-
|
|
71851
|
-
|
|
71852
|
-
|
|
71853
|
-
|
|
72235
|
+
try {
|
|
72236
|
+
const cloud = getTodosCloudClient();
|
|
72237
|
+
const gitRef = cloud ? await cloudLinkRef(cloud, resolvedId, {
|
|
72238
|
+
ref_type: refType,
|
|
72239
|
+
name: ref,
|
|
72240
|
+
...opts.url !== undefined ? { url: opts.url } : {},
|
|
72241
|
+
...opts.provider !== undefined ? { provider: opts.provider } : {},
|
|
72242
|
+
...metadata ? { metadata } : {}
|
|
72243
|
+
}) : linkTaskGitRef2({
|
|
72244
|
+
task_id: resolvedId,
|
|
72245
|
+
ref_type: refType,
|
|
72246
|
+
name: ref,
|
|
72247
|
+
url: opts.url,
|
|
72248
|
+
provider: opts.provider,
|
|
72249
|
+
metadata
|
|
72250
|
+
});
|
|
72251
|
+
if (globalOpts.json) {
|
|
72252
|
+
output(gitRef, true);
|
|
72253
|
+
return;
|
|
72254
|
+
}
|
|
72255
|
+
console.log(chalk8.green(`Linked ${gitRef.ref_type} ${gitRef.name} to task ${taskId}`));
|
|
72256
|
+
} catch (e) {
|
|
72257
|
+
handleError(e);
|
|
71854
72258
|
}
|
|
71855
|
-
console.log(chalk8.green(`Linked ${gitRef.ref_type} ${gitRef.name} to task ${taskId}`));
|
|
71856
72259
|
});
|
|
71857
72260
|
program2.command("find-ref <ref>").description("Find tasks linked to a git branch or pull request").action(async (ref) => {
|
|
71858
72261
|
const globalOpts = program2.opts();
|
|
71859
|
-
|
|
71860
|
-
|
|
71861
|
-
|
|
71862
|
-
|
|
71863
|
-
|
|
71864
|
-
|
|
71865
|
-
|
|
71866
|
-
|
|
71867
|
-
|
|
71868
|
-
|
|
71869
|
-
|
|
71870
|
-
|
|
71871
|
-
|
|
71872
|
-
|
|
71873
|
-
|
|
71874
|
-
|
|
72262
|
+
try {
|
|
72263
|
+
const cloud = getTodosCloudClient();
|
|
72264
|
+
if (cloud) {
|
|
72265
|
+
const refs2 = await cloudFindRefs(cloud, ref);
|
|
72266
|
+
if (globalOpts.json) {
|
|
72267
|
+
output(refs2, true);
|
|
72268
|
+
return;
|
|
72269
|
+
}
|
|
72270
|
+
if (refs2.length === 0) {
|
|
72271
|
+
console.log(chalk8.dim(`No tasks linked to ${ref}.`));
|
|
72272
|
+
return;
|
|
72273
|
+
}
|
|
72274
|
+
for (const gitRef of refs2) {
|
|
72275
|
+
const url = gitRef.url ? chalk8.dim(` ${gitRef.url}`) : "";
|
|
72276
|
+
console.log(`${chalk8.cyan(gitRef.task_id)} <- ${gitRef.ref_type} ${chalk8.yellow(gitRef.name)}${url}`);
|
|
72277
|
+
}
|
|
72278
|
+
return;
|
|
72279
|
+
}
|
|
72280
|
+
const { findTasksByGitRef: findTasksByGitRef2 } = await Promise.resolve().then(() => (init_task_commits(), exports_task_commits));
|
|
72281
|
+
const { getTask: getTask3 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
72282
|
+
const refs = findTasksByGitRef2(ref);
|
|
72283
|
+
if (globalOpts.json) {
|
|
72284
|
+
output(refs, true);
|
|
72285
|
+
return;
|
|
72286
|
+
}
|
|
72287
|
+
if (refs.length === 0) {
|
|
72288
|
+
console.log(chalk8.dim(`No tasks linked to ${ref}.`));
|
|
72289
|
+
return;
|
|
72290
|
+
}
|
|
72291
|
+
for (const gitRef of refs) {
|
|
72292
|
+
const task2 = getTask3(gitRef.task_id);
|
|
72293
|
+
const label = task2 ? `${task2.short_id || task2.id.slice(0, 8)} ${task2.title}` : gitRef.task_id;
|
|
72294
|
+
const url = gitRef.url ? chalk8.dim(` ${gitRef.url}`) : "";
|
|
72295
|
+
console.log(`${chalk8.cyan(label)} <- ${gitRef.ref_type} ${chalk8.yellow(gitRef.name)}${url}`);
|
|
72296
|
+
}
|
|
72297
|
+
} catch (e) {
|
|
72298
|
+
handleError(e);
|
|
71875
72299
|
}
|
|
71876
72300
|
});
|
|
71877
72301
|
program2.command("branch-plan [task-id]").description("Create a local branch-safe work plan from task or plan files").requiredOption("--branch <name>", "Branch name to plan").option("--base <name>", "Base branch", "main").option("--plan <id>", "Plan ID scope instead of a single task").option("--path <list>", "Comma-separated extra paths expected for this branch").option("--root <path>", "Git root to inspect", process.cwd()).option("--no-git-status", "Skip local git status checks").action(async (taskId, opts) => {
|