@hasna/todos 0.14.0 → 0.15.2
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/README.md +2 -0
- package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
- package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +265 -52
- package/dist/cli/stage-a.d.ts.map +1 -1
- package/dist/contracts.js +113 -112
- package/dist/db/database.d.ts.map +1 -1
- package/dist/index.js +120 -119
- package/dist/lib/bulk-tags.d.ts +57 -0
- package/dist/lib/bulk-tags.d.ts.map +1 -0
- package/dist/lib/enum-vocabulary.d.ts.map +1 -1
- package/dist/lib/run-records.d.ts.map +1 -1
- package/dist/lib/sandbox-profiles.d.ts.map +1 -1
- package/dist/lib/sync-utils.d.ts +1 -1
- package/dist/lib/sync-utils.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +80 -64
- package/dist/mcp/tools/task-crud.d.ts +1 -0
- package/dist/mcp/tools/task-crud.d.ts.map +1 -1
- package/dist/mcp.js +1 -1
- package/dist/registry.js +113 -112
- package/dist/release-provenance.json +5 -5
- package/dist/sdk/index.js +3 -2
- package/dist/server/index.js +36 -20
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage.js +112 -111
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2123,7 +2123,7 @@ var package_default;
|
|
|
2123
2123
|
var init_package = __esm(() => {
|
|
2124
2124
|
package_default = {
|
|
2125
2125
|
name: "@hasna/todos",
|
|
2126
|
-
version: "0.
|
|
2126
|
+
version: "0.15.2",
|
|
2127
2127
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
2128
2128
|
type: "module",
|
|
2129
2129
|
main: "dist/index.js",
|
|
@@ -2444,11 +2444,30 @@ var init_types = __esm(() => {
|
|
|
2444
2444
|
});
|
|
2445
2445
|
|
|
2446
2446
|
// src/lib/sync-utils.ts
|
|
2447
|
+
var exports_sync_utils = {};
|
|
2448
|
+
__export(exports_sync_utils, {
|
|
2449
|
+
writeJsonFile: () => writeJsonFile,
|
|
2450
|
+
writeHighWaterMark: () => writeHighWaterMark,
|
|
2451
|
+
withSyncFingerprint: () => withSyncFingerprint,
|
|
2452
|
+
readJsonFile: () => readJsonFile,
|
|
2453
|
+
readHighWaterMark: () => readHighWaterMark,
|
|
2454
|
+
parseTimestamp: () => parseTimestamp,
|
|
2455
|
+
listJsonFiles: () => listJsonFiles,
|
|
2456
|
+
hasSyncFingerprintChanged: () => hasSyncFingerprintChanged,
|
|
2457
|
+
getTodosGlobalDir: () => getTodosGlobalDir,
|
|
2458
|
+
getHomeDir: () => getHomeDir,
|
|
2459
|
+
getFileMtimeMs: () => getFileMtimeMs,
|
|
2460
|
+
ensureDir: () => ensureDir,
|
|
2461
|
+
appendSyncConflict: () => appendSyncConflict,
|
|
2462
|
+
TODO_SYNC_FINGERPRINT_KEY: () => TODO_SYNC_FINGERPRINT_KEY,
|
|
2463
|
+
HOME: () => HOME
|
|
2464
|
+
});
|
|
2447
2465
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
2448
2466
|
import { createHash } from "crypto";
|
|
2467
|
+
import { homedir } from "os";
|
|
2449
2468
|
import { join } from "path";
|
|
2450
2469
|
function getHomeDir() {
|
|
2451
|
-
return process.env["HOME"] || process.env["USERPROFILE"] ||
|
|
2470
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir();
|
|
2452
2471
|
}
|
|
2453
2472
|
function getTodosGlobalDir() {
|
|
2454
2473
|
return join(getHomeDir(), ".hasna", "todos");
|
|
@@ -2536,7 +2555,7 @@ function hasSyncFingerprintChanged(record) {
|
|
|
2536
2555
|
}
|
|
2537
2556
|
var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
|
|
2538
2557
|
var init_sync_utils = __esm(() => {
|
|
2539
|
-
HOME =
|
|
2558
|
+
HOME = getHomeDir();
|
|
2540
2559
|
});
|
|
2541
2560
|
|
|
2542
2561
|
// src/lib/config.ts
|
|
@@ -6414,6 +6433,15 @@ function hasOption(args, option) {
|
|
|
6414
6433
|
function positionalArgs(args) {
|
|
6415
6434
|
return args.filter((arg) => !arg.startsWith("-"));
|
|
6416
6435
|
}
|
|
6436
|
+
function isBundledStaticInvocation(invocation) {
|
|
6437
|
+
const command = invocation.command;
|
|
6438
|
+
if (!command)
|
|
6439
|
+
return false;
|
|
6440
|
+
const storeBackedOptions = BUNDLED_STATIC_STORE_BACKED_OPTIONS.get(command);
|
|
6441
|
+
if (!storeBackedOptions)
|
|
6442
|
+
return false;
|
|
6443
|
+
return !storeBackedOptions.some((option) => hasOption(invocation.commandArgs, option));
|
|
6444
|
+
}
|
|
6417
6445
|
function isReadOnlyConfigInvocation(invocation) {
|
|
6418
6446
|
if (invocation.command !== "config")
|
|
6419
6447
|
return false;
|
|
@@ -6434,6 +6462,8 @@ function isMetadataInvocation(args, invocation) {
|
|
|
6434
6462
|
return true;
|
|
6435
6463
|
if (invocation.command === "manual" && invocation.commandArgs.length === 0)
|
|
6436
6464
|
return true;
|
|
6465
|
+
if (isBundledStaticInvocation(invocation))
|
|
6466
|
+
return true;
|
|
6437
6467
|
if (invocation.command === "help" && invocation.commandArgs.every((arg) => !arg.startsWith("-")))
|
|
6438
6468
|
return true;
|
|
6439
6469
|
if (invocation.command === "config") {
|
|
@@ -6451,6 +6481,14 @@ function firstPresentOption(args, candidates) {
|
|
|
6451
6481
|
function disqualifyingArgument(invocation) {
|
|
6452
6482
|
const command = invocation.command;
|
|
6453
6483
|
const args = invocation.commandArgs;
|
|
6484
|
+
const storeBackedOptions = BUNDLED_STATIC_STORE_BACKED_OPTIONS.get(command);
|
|
6485
|
+
if (storeBackedOptions) {
|
|
6486
|
+
const option = storeBackedOptions.find((candidate) => hasOption(args, candidate));
|
|
6487
|
+
return option ? {
|
|
6488
|
+
blame: option,
|
|
6489
|
+
remedy: `it imports bundled fixtures into the local store, which this route disables; re-run \`${command}\` without it to read the bundled content`
|
|
6490
|
+
} : null;
|
|
6491
|
+
}
|
|
6454
6492
|
switch (command) {
|
|
6455
6493
|
case "task":
|
|
6456
6494
|
return positionalArgs(args)[0] === "upsert" ? null : { blame: "any subcommand other than `upsert`", remedy: "use `todos task upsert`" };
|
|
@@ -6478,13 +6516,22 @@ function disqualifyingArgument(invocation) {
|
|
|
6478
6516
|
const action = positionalArgs(args)[0];
|
|
6479
6517
|
if (action === "plan" || action === "move-plan")
|
|
6480
6518
|
return null;
|
|
6519
|
+
if (action === "tag" || action === "untag") {
|
|
6520
|
+
if (!hasOption(args, "--tag") && !hasOption(args, "--tags")) {
|
|
6521
|
+
return {
|
|
6522
|
+
blame: `\`bulk ${action}\` without --tag`,
|
|
6523
|
+
remedy: `pass --tag <comma-separated>, e.g. bulk ${action} <ids...> --tag directive:k_msd4cz8t_ste6f4`
|
|
6524
|
+
};
|
|
6525
|
+
}
|
|
6526
|
+
return firstPresentOption(args, ["--plan", "--clear-plan"]);
|
|
6527
|
+
}
|
|
6481
6528
|
if (!action) {
|
|
6482
|
-
return { blame: "a missing action", remedy: "pass one of done, complete, start, delete, plan, move-plan" };
|
|
6529
|
+
return { blame: "a missing action", remedy: "pass one of done, complete, start, delete, plan, move-plan, tag, untag" };
|
|
6483
6530
|
}
|
|
6484
6531
|
if (!["done", "complete", "start", "delete"].includes(action)) {
|
|
6485
6532
|
return {
|
|
6486
6533
|
blame: `the \`${action}\` action`,
|
|
6487
|
-
remedy: "use one of done, complete, start, delete, plan, move-plan"
|
|
6534
|
+
remedy: "use one of done, complete, start, delete, plan, move-plan, tag, untag"
|
|
6488
6535
|
};
|
|
6489
6536
|
}
|
|
6490
6537
|
return firstPresentOption(args, ["--plan", "--clear-plan"]);
|
|
@@ -6541,7 +6588,8 @@ function assertRemoteCommandSupported(invocation) {
|
|
|
6541
6588
|
}
|
|
6542
6589
|
if (!command || !commandSupportsRemote(invocation)) {
|
|
6543
6590
|
const blame = command ? disqualifyingArgument(invocation) : null;
|
|
6544
|
-
const
|
|
6591
|
+
const servedBy = command && BUNDLED_STATIC_STORE_BACKED_OPTIONS.has(command) ? `\`${command}\` renders bundled content on this route` : `\`${command}\` is served by the Todos /v1 authority`;
|
|
6592
|
+
const detail = blame ? `${servedBy} but ${blame.blame} is not; ${blame.remedy}` : `${invocationLabel(invocation)} is not supported by the Todos /v1 CLI; local SQLite fallback is disabled`;
|
|
6545
6593
|
throw new Error(`REMOTE_COMMAND_UNSUPPORTED: ${detail}`);
|
|
6546
6594
|
}
|
|
6547
6595
|
}
|
|
@@ -6561,7 +6609,7 @@ function initializeTodosCliAuthority(args = process.argv.slice(2), env = process
|
|
|
6561
6609
|
}
|
|
6562
6610
|
return { route: "remote-http", v1_base_url: client.baseUrl };
|
|
6563
6611
|
}
|
|
6564
|
-
var REGISTERED_CANONICAL_COMMANDS, TODOS_CLI_COMMAND_ALIASES, DIAGNOSTIC_COMMANDS, REMOTE_COMMANDS, COMMAND_CAPABILITY_MATRIX, GLOBAL_OPTIONS_WITH_VALUES, GLOBAL_FLAGS, HELP_FLAGS, VERSION_FLAGS, dropIt = (blame) => ({ blame, remedy: "re-run without it" });
|
|
6612
|
+
var REGISTERED_CANONICAL_COMMANDS, TODOS_CLI_COMMAND_ALIASES, BUNDLED_STATIC_COMMANDS, BUNDLED_STATIC_STORE_BACKED_OPTIONS, DIAGNOSTIC_COMMANDS, REMOTE_COMMANDS, COMMAND_CAPABILITY_MATRIX, GLOBAL_OPTIONS_WITH_VALUES, GLOBAL_FLAGS, HELP_FLAGS, VERSION_FLAGS, dropIt = (blame) => ({ blame, remedy: "re-run without it" });
|
|
6565
6613
|
var init_stage_a = __esm(() => {
|
|
6566
6614
|
init_esm();
|
|
6567
6615
|
init_cloud_router();
|
|
@@ -6751,7 +6799,28 @@ var init_stage_a = __esm(() => {
|
|
|
6751
6799
|
reliability: ["scorecards"],
|
|
6752
6800
|
lists: ["task-lists", "tl"]
|
|
6753
6801
|
};
|
|
6754
|
-
|
|
6802
|
+
BUNDLED_STATIC_COMMANDS = {
|
|
6803
|
+
workflows: [],
|
|
6804
|
+
"template-library": [],
|
|
6805
|
+
onboarding: ["--import"],
|
|
6806
|
+
"sdk-fixtures": ["--show", "--write"]
|
|
6807
|
+
};
|
|
6808
|
+
BUNDLED_STATIC_STORE_BACKED_OPTIONS = new Map;
|
|
6809
|
+
for (const [canonical, storeBackedOptions] of Object.entries(BUNDLED_STATIC_COMMANDS)) {
|
|
6810
|
+
BUNDLED_STATIC_STORE_BACKED_OPTIONS.set(canonical, storeBackedOptions);
|
|
6811
|
+
const aliases = TODOS_CLI_COMMAND_ALIASES[canonical] ?? [];
|
|
6812
|
+
for (const alias of aliases)
|
|
6813
|
+
BUNDLED_STATIC_STORE_BACKED_OPTIONS.set(alias, storeBackedOptions);
|
|
6814
|
+
}
|
|
6815
|
+
DIAGNOSTIC_COMMANDS = new Set([
|
|
6816
|
+
"help",
|
|
6817
|
+
"manual",
|
|
6818
|
+
"completions",
|
|
6819
|
+
"completion",
|
|
6820
|
+
"config",
|
|
6821
|
+
"storage",
|
|
6822
|
+
...Object.keys(BUNDLED_STATIC_COMMANDS)
|
|
6823
|
+
]);
|
|
6755
6824
|
REMOTE_COMMANDS = new Set([
|
|
6756
6825
|
"active",
|
|
6757
6826
|
"add",
|
|
@@ -10773,8 +10842,7 @@ function findGitRoot(startDir) {
|
|
|
10773
10842
|
return null;
|
|
10774
10843
|
}
|
|
10775
10844
|
function getGlobalDbPath() {
|
|
10776
|
-
|
|
10777
|
-
return join3(home, ".hasna", "todos", "todos.db");
|
|
10845
|
+
return join3(getHomeDir(), ".hasna", "todos", "todos.db");
|
|
10778
10846
|
}
|
|
10779
10847
|
function hasExplicitProjectArg(args = process.argv.slice(2)) {
|
|
10780
10848
|
return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
|
|
@@ -10996,6 +11064,7 @@ var init_database = __esm(() => {
|
|
|
10996
11064
|
init_machines();
|
|
10997
11065
|
init_identity_mapping();
|
|
10998
11066
|
init_types();
|
|
11067
|
+
init_sync_utils();
|
|
10999
11068
|
ALLOWED_TABLES = new Set(["tasks", "projects", "agents", "plans", "task_lists", "task_templates", "project_knowledge_records", "project_risks", "local_retrospectives"]);
|
|
11000
11069
|
});
|
|
11001
11070
|
|
|
@@ -11502,14 +11571,21 @@ function suggestVocabularyMatches(value, vocabulary, limit = 3) {
|
|
|
11502
11571
|
}
|
|
11503
11572
|
function resolveEnumVocabulary(raw, spec) {
|
|
11504
11573
|
const allowList = spec.allowList !== false;
|
|
11505
|
-
const rawElements = (allowList ? raw.split(",") : [raw]).map((element) => element.trim())
|
|
11506
|
-
if (rawElements.length === 0) {
|
|
11574
|
+
const rawElements = (allowList ? raw.split(",") : [raw]).map((element) => element.trim());
|
|
11575
|
+
if (rawElements.every((element) => element.length === 0)) {
|
|
11507
11576
|
return {
|
|
11508
11577
|
ok: false,
|
|
11509
11578
|
message: `${spec.name} requires a value. Allowed values: ${spec.vocabulary.join(", ")}.`,
|
|
11510
11579
|
invalid: []
|
|
11511
11580
|
};
|
|
11512
11581
|
}
|
|
11582
|
+
if (rawElements.some((element) => element.length === 0)) {
|
|
11583
|
+
return {
|
|
11584
|
+
ok: false,
|
|
11585
|
+
message: `${spec.name} has an empty value in "${raw}" \u2014 remove the stray comma. ` + `Allowed values: ${spec.vocabulary.join(", ")}.`,
|
|
11586
|
+
invalid: []
|
|
11587
|
+
};
|
|
11588
|
+
}
|
|
11513
11589
|
const normalized = rawElements.map((element) => spec.normalize ? spec.normalize(element) : element);
|
|
11514
11590
|
const allowed = new Set(spec.vocabulary);
|
|
11515
11591
|
const invalid2 = [];
|
|
@@ -12707,7 +12783,7 @@ var init_event_hooks = __esm(() => {
|
|
|
12707
12783
|
// node_modules/.bun/@hasna+events@0.1.11/node_modules/@hasna/events/dist/index.js
|
|
12708
12784
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
12709
12785
|
import { existsSync as existsSync6 } from "fs";
|
|
12710
|
-
import { homedir } from "os";
|
|
12786
|
+
import { homedir as homedir2 } from "os";
|
|
12711
12787
|
import { join as join5 } from "path";
|
|
12712
12788
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
12713
12789
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -12809,7 +12885,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
12809
12885
|
return channel.filters.some((filter) => eventMatchesFilter(event, filter));
|
|
12810
12886
|
}
|
|
12811
12887
|
function getEventsDataDir(override) {
|
|
12812
|
-
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join5(
|
|
12888
|
+
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join5(homedir2(), ".hasna", "events");
|
|
12813
12889
|
}
|
|
12814
12890
|
|
|
12815
12891
|
class JsonEventsStore {
|
|
@@ -19241,6 +19317,49 @@ var init_tasks = __esm(() => {
|
|
|
19241
19317
|
init_calendar();
|
|
19242
19318
|
});
|
|
19243
19319
|
|
|
19320
|
+
// src/lib/bulk-tags.ts
|
|
19321
|
+
function parseTagList(raw) {
|
|
19322
|
+
if (!raw)
|
|
19323
|
+
return [];
|
|
19324
|
+
const seen = new Set;
|
|
19325
|
+
const tags = [];
|
|
19326
|
+
for (const segment of raw.split(",")) {
|
|
19327
|
+
const tag = segment.trim();
|
|
19328
|
+
if (!tag || seen.has(tag))
|
|
19329
|
+
continue;
|
|
19330
|
+
seen.add(tag);
|
|
19331
|
+
tags.push(tag);
|
|
19332
|
+
}
|
|
19333
|
+
return tags;
|
|
19334
|
+
}
|
|
19335
|
+
function resolveTagArgument(tag, tags) {
|
|
19336
|
+
if (tag === undefined)
|
|
19337
|
+
return { ok: true, raw: tags };
|
|
19338
|
+
if (tags === undefined)
|
|
19339
|
+
return { ok: true, raw: tag };
|
|
19340
|
+
const left = parseTagList(tag);
|
|
19341
|
+
const right = parseTagList(tags);
|
|
19342
|
+
const same = left.length === right.length && left.every((entry) => right.includes(entry));
|
|
19343
|
+
if (!same)
|
|
19344
|
+
return { ok: false, conflict: { tag, tags } };
|
|
19345
|
+
return { ok: true, raw: tag };
|
|
19346
|
+
}
|
|
19347
|
+
function resolveBulkTags(current, action, tags) {
|
|
19348
|
+
const existing = Array.isArray(current) ? current.filter((tag) => typeof tag === "string") : [];
|
|
19349
|
+
if (tags.length === 0)
|
|
19350
|
+
return { tags: [...existing], changed: false };
|
|
19351
|
+
if (action === "tag") {
|
|
19352
|
+
const present = new Set(existing);
|
|
19353
|
+
const additions = tags.filter((tag) => !present.has(tag));
|
|
19354
|
+
if (additions.length === 0)
|
|
19355
|
+
return { tags: [...existing], changed: false };
|
|
19356
|
+
return { tags: [...existing, ...additions], changed: true };
|
|
19357
|
+
}
|
|
19358
|
+
const removing = new Set(tags);
|
|
19359
|
+
const remaining = existing.filter((tag) => !removing.has(tag));
|
|
19360
|
+
return { tags: remaining, changed: remaining.length !== existing.length };
|
|
19361
|
+
}
|
|
19362
|
+
|
|
19244
19363
|
// src/cli/claim-guard.ts
|
|
19245
19364
|
function resolveClaimIdentity(verb, explicit) {
|
|
19246
19365
|
const resolved = resolveWritableIdentity(explicit);
|
|
@@ -19261,10 +19380,10 @@ function normalizeAgentNameInput(name) {
|
|
|
19261
19380
|
|
|
19262
19381
|
// src/lib/assignee-validation.ts
|
|
19263
19382
|
import { readFileSync as readFileSync4 } from "fs";
|
|
19264
|
-
import { homedir as
|
|
19383
|
+
import { homedir as homedir3 } from "os";
|
|
19265
19384
|
import { join as join8 } from "path";
|
|
19266
19385
|
function defaultSeatRosterPath() {
|
|
19267
|
-
return process.env["TODOS_SEAT_ROSTER_PATH"] || join8(
|
|
19386
|
+
return process.env["TODOS_SEAT_ROSTER_PATH"] || join8(homedir3(), ".hasna", "identities", "hasna-seats.roster.json");
|
|
19268
19387
|
}
|
|
19269
19388
|
function loadSeatSlugs(path = defaultSeatRosterPath()) {
|
|
19270
19389
|
try {
|
|
@@ -20398,11 +20517,31 @@ function resolveProjectIdOrSlug(input) {
|
|
|
20398
20517
|
return row.id;
|
|
20399
20518
|
handleError(new Error(`Project not found: ${input}`));
|
|
20400
20519
|
}
|
|
20520
|
+
function projectRefFromOpts(opts, globalOpts) {
|
|
20521
|
+
const own = typeof opts.project === "string" ? opts.project : undefined;
|
|
20522
|
+
const global = typeof globalOpts.project === "string" ? globalOpts.project : undefined;
|
|
20523
|
+
return own || global;
|
|
20524
|
+
}
|
|
20525
|
+
function projectOptOut(opts) {
|
|
20526
|
+
return opts.project === false;
|
|
20527
|
+
}
|
|
20528
|
+
function warnMissingProject(resolvedProjectId, optedOut) {
|
|
20529
|
+
if (resolvedProjectId || optedOut)
|
|
20530
|
+
return;
|
|
20531
|
+
console.error(chalk3.yellow("Warning: task filed with no project \u2014 it will not appear in any project list, per-seat report, or drain. " + "Pass --project <id-or-slug>, or --no-project if filing it globally is deliberate."));
|
|
20532
|
+
}
|
|
20401
20533
|
function parseStatus(value) {
|
|
20402
20534
|
if (!value)
|
|
20403
20535
|
return;
|
|
20404
20536
|
return parseEnumFlagList(value, { ...TASK_STATUS_FLAG, allowList: false })?.[0];
|
|
20405
20537
|
}
|
|
20538
|
+
function listScanLimit() {
|
|
20539
|
+
const raw = process.env["TODOS_LIST_SCAN_LIMIT"];
|
|
20540
|
+
if (raw === undefined || raw.trim() === "")
|
|
20541
|
+
return DEFAULT_LIST_SCAN_LIMIT;
|
|
20542
|
+
const parsed = Number.parseInt(raw, 10);
|
|
20543
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_LIST_SCAN_LIMIT;
|
|
20544
|
+
}
|
|
20406
20545
|
function parseIntOption(value, flag) {
|
|
20407
20546
|
if (value === undefined)
|
|
20408
20547
|
return;
|
|
@@ -20540,11 +20679,10 @@ function computeLocalReparent(current, opts) {
|
|
|
20540
20679
|
return patch;
|
|
20541
20680
|
}
|
|
20542
20681
|
function registerTaskCommands(program2) {
|
|
20543
|
-
program2.command("add <title>").description("Create a new task").option("-d, --description <text>", "Task description").option("-p, --priority <level>", "Priority: low, medium, high, critical").option("--parent <id>", "Parent task ID").option("-t, --tags <tags>", "Comma-separated tags").option("--tag <tags>", "Comma-separated tags (alias for --tags)").option("--plan <id>", "Assign to a plan").option("--assign <agent>", "Assign to agent").option("--status <status>", "Initial status").option("--list <id>", "Task list ID").option("--task-list <id>", "Task list ID (alias for --list)").option("--estimated <minutes>", "Estimated time in minutes").option("--sla-minutes <minutes>", "SLA minutes before unfinished work is escalated").option("--sla <minutes>", "Alias for --sla-minutes").option("--approval", "Require approval before completion").option("--recurrence <rule>", "Recurrence rule, e.g. 'every day', 'every weekday', 'every 2 weeks'").option("--due <date>", "Due date (ISO string or YYYY-MM-DD)").option("--reason <text>", "Why this task exists").option("--project <id>", "Assign to project by ID or slug (overrides auto-detect)").option("--unassigned", "Deliberately file this task with no assignee").option("--assign-seat", "Allow --assign to name a durable seat (a seat queue has no session watching it)").option("--created-by <agent>", "Record a different filer than the resolved agent identity").action(async (title, opts) => {
|
|
20682
|
+
program2.command("add <title>").description("Create a new task").option("-d, --description <text>", "Task description").option("-p, --priority <level>", "Priority: low, medium, high, critical").option("--parent <id>", "Parent task ID").option("-t, --tags <tags>", "Comma-separated tags").option("--tag <tags>", "Comma-separated tags (alias for --tags)").option("--plan <id>", "Assign to a plan").option("--assign <agent>", "Assign to agent").option("--status <status>", "Initial status").option("--list <id>", "Task list ID").option("--task-list <id>", "Task list ID (alias for --list)").option("--estimated <minutes>", "Estimated time in minutes").option("--sla-minutes <minutes>", "SLA minutes before unfinished work is escalated").option("--sla <minutes>", "Alias for --sla-minutes").option("--approval", "Require approval before completion").option("--recurrence <rule>", "Recurrence rule, e.g. 'every day', 'every weekday', 'every 2 weeks'").option("--due <date>", "Due date (ISO string or YYYY-MM-DD)").option("--reason <text>", "Why this task exists").option("--project <id>", "Assign to project by ID or slug (overrides auto-detect)").option("--no-project", "Deliberately file this task with no project (silences the orphan warning)").option("--unassigned", "Deliberately file this task with no assignee").option("--assign-seat", "Allow --assign to name a durable seat (a seat queue has no session watching it)").option("--created-by <agent>", "Record a different filer than the resolved agent identity").action(async (title, opts) => {
|
|
20544
20683
|
const globalOpts = program2.opts();
|
|
20545
20684
|
opts.tags = opts.tags || opts.tag;
|
|
20546
20685
|
opts.list = opts.list || opts.taskList;
|
|
20547
|
-
const creator = resolveCreatorIdentity(opts.createdBy || globalOpts.agent);
|
|
20548
20686
|
const router = resolveWritableIdentity(opts.createdBy || globalOpts.agent);
|
|
20549
20687
|
const requestedAssign = opts.assign ? await resolveValidatedAssignee(opts.assign, Boolean(opts.assignSeat), (v) => `--assign ${v} --assign-seat`) : undefined;
|
|
20550
20688
|
const assignee = requestedAssign || (opts.unassigned ? undefined : router.agent_id || undefined);
|
|
@@ -20555,8 +20693,9 @@ function registerTaskCommands(program2) {
|
|
|
20555
20693
|
if (cloud) {
|
|
20556
20694
|
let task3;
|
|
20557
20695
|
try {
|
|
20558
|
-
const cloudProjectRef = opts
|
|
20696
|
+
const cloudProjectRef = projectRefFromOpts(opts, globalOpts);
|
|
20559
20697
|
const cloudProjectId = cloudProjectRef ? await cloudResolveProjectRef(cloud, cloudProjectRef) : undefined;
|
|
20698
|
+
warnMissingProject(cloudProjectId, projectOptOut(opts));
|
|
20560
20699
|
const cloudTaskListId = opts.list ? await cloudResolveTaskListRef(cloud, opts.list, cloudProjectId) : undefined;
|
|
20561
20700
|
if (opts.list && !cloudTaskListId) {
|
|
20562
20701
|
throw new Error(`Could not resolve task list ID or slug: ${opts.list}`);
|
|
@@ -20576,9 +20715,10 @@ function registerTaskCommands(program2) {
|
|
|
20576
20715
|
status: parseStatus(opts.status),
|
|
20577
20716
|
task_list_id: cloudTaskListId,
|
|
20578
20717
|
agent_id: globalOpts.agent || router.agent_id || undefined,
|
|
20579
|
-
created_by:
|
|
20718
|
+
created_by: router.agent_id || undefined,
|
|
20580
20719
|
session_id: globalOpts.session,
|
|
20581
20720
|
project_id: cloudProjectId,
|
|
20721
|
+
working_dir: process.cwd(),
|
|
20582
20722
|
estimated_minutes: opts.estimated !== undefined ? parseIntOption(opts.estimated, "--estimated") : undefined,
|
|
20583
20723
|
sla_minutes: opts.slaMinutes !== undefined || opts.sla !== undefined ? parseIntOption(opts.slaMinutes ?? opts.sla, "--sla-minutes") : undefined,
|
|
20584
20724
|
requires_approval: opts.approval || undefined,
|
|
@@ -20597,8 +20737,9 @@ function registerTaskCommands(program2) {
|
|
|
20597
20737
|
}
|
|
20598
20738
|
return;
|
|
20599
20739
|
}
|
|
20600
|
-
const explicitProject = opts
|
|
20601
|
-
const projectId = explicitProject ? resolveProjectIdOrSlug(explicitProject) : autoProject(globalOpts);
|
|
20740
|
+
const explicitProject = projectRefFromOpts(opts, globalOpts);
|
|
20741
|
+
const projectId = explicitProject ? resolveProjectIdOrSlug(explicitProject) : projectOptOut(opts) ? undefined : autoProject(globalOpts);
|
|
20742
|
+
warnMissingProject(projectId, projectOptOut(opts));
|
|
20602
20743
|
opts.tags = opts.tags || opts.tag;
|
|
20603
20744
|
opts.list = opts.list || opts.taskList;
|
|
20604
20745
|
const taskListId = opts.list ? (() => {
|
|
@@ -20622,7 +20763,7 @@ function registerTaskCommands(program2) {
|
|
|
20622
20763
|
status: parseStatus(opts.status),
|
|
20623
20764
|
task_list_id: taskListId,
|
|
20624
20765
|
agent_id: globalOpts.agent || router.agent_id || undefined,
|
|
20625
|
-
created_by:
|
|
20766
|
+
created_by: router.agent_id || undefined,
|
|
20626
20767
|
session_id: globalOpts.session,
|
|
20627
20768
|
project_id: projectId,
|
|
20628
20769
|
working_dir: process.cwd(),
|
|
@@ -20810,15 +20951,19 @@ function registerTaskCommands(program2) {
|
|
|
20810
20951
|
}
|
|
20811
20952
|
filter["task_list_id"] = listId;
|
|
20812
20953
|
}
|
|
20813
|
-
if (opts.status) {
|
|
20954
|
+
if (opts.status !== undefined) {
|
|
20814
20955
|
filter["status"] = parseEnumFlag(opts.status, TASK_STATUS_FLAG);
|
|
20815
20956
|
} else if (!opts.all) {
|
|
20816
20957
|
filter["status"] = ["pending", "in_progress"];
|
|
20817
20958
|
}
|
|
20818
|
-
if (opts.priority)
|
|
20959
|
+
if (opts.priority !== undefined)
|
|
20819
20960
|
filter["priority"] = parseEnumFlag(opts.priority, TASK_PRIORITY_FLAG);
|
|
20820
|
-
|
|
20821
|
-
|
|
20961
|
+
let assignedFilter;
|
|
20962
|
+
let assignedWasTyped = false;
|
|
20963
|
+
if (opts.assigned) {
|
|
20964
|
+
assignedFilter = opts.assigned;
|
|
20965
|
+
assignedWasTyped = true;
|
|
20966
|
+
}
|
|
20822
20967
|
if (opts.createdBy)
|
|
20823
20968
|
filter["created_by"] = canonicalAgentRef(opts.createdBy);
|
|
20824
20969
|
if (opts.notCreatedBy)
|
|
@@ -20829,7 +20974,8 @@ function registerTaskCommands(program2) {
|
|
|
20829
20974
|
console.error(chalk3.red("--inbox needs an agent identity. Run `todos init <name>` or pass --agent <id>."));
|
|
20830
20975
|
process.exit(1);
|
|
20831
20976
|
}
|
|
20832
|
-
|
|
20977
|
+
assignedFilter = me.agent_id;
|
|
20978
|
+
assignedWasTyped = false;
|
|
20833
20979
|
filter["not_created_by"] = me.agent_id;
|
|
20834
20980
|
}
|
|
20835
20981
|
if (opts.tags)
|
|
@@ -20845,8 +20991,11 @@ function registerTaskCommands(program2) {
|
|
|
20845
20991
|
}
|
|
20846
20992
|
}
|
|
20847
20993
|
if (opts.agentName) {
|
|
20848
|
-
|
|
20994
|
+
assignedFilter = opts.agentName;
|
|
20995
|
+
assignedWasTyped = true;
|
|
20849
20996
|
}
|
|
20997
|
+
if (assignedFilter !== undefined)
|
|
20998
|
+
filter["assigned_to"] = assignedFilter;
|
|
20850
20999
|
if (opts.recurring)
|
|
20851
21000
|
filter["has_recurrence"] = true;
|
|
20852
21001
|
if (opts.limit !== undefined) {
|
|
@@ -20858,11 +21007,23 @@ function registerTaskCommands(program2) {
|
|
|
20858
21007
|
}
|
|
20859
21008
|
const creatorFilterActive = Boolean(filter["created_by"] || filter["not_created_by"]);
|
|
20860
21009
|
const requestedLimit = filter["limit"];
|
|
20861
|
-
const
|
|
20862
|
-
|
|
20863
|
-
|
|
20864
|
-
|
|
21010
|
+
const reordersAfterQuery = Boolean(opts.sort);
|
|
21011
|
+
const narrowsAfterQuery = Boolean(opts.dueToday) || Boolean(opts.overdue) || creatorFilterActive && cloud;
|
|
21012
|
+
const withholdLimit = requestedLimit !== undefined && (reordersAfterQuery || narrowsAfterQuery);
|
|
21013
|
+
const scanCeiling = cloud && (withholdLimit || requestedLimit === undefined) ? Math.max(requestedLimit ?? 0, listScanLimit()) : undefined;
|
|
21014
|
+
const serverFilter = (() => {
|
|
21015
|
+
const base = withholdLimit ? (() => {
|
|
21016
|
+
const { limit: _dropped, ...rest } = filter;
|
|
21017
|
+
return rest;
|
|
21018
|
+
})() : filter;
|
|
21019
|
+
return scanCeiling === undefined ? base : { ...base, limit: scanCeiling };
|
|
21020
|
+
})();
|
|
20865
21021
|
let tasks = cloud ? await cloudListTasks(cloud, serverFilter) : listTasks(serverFilter);
|
|
21022
|
+
if (scanCeiling !== undefined && tasks.length >= scanCeiling) {
|
|
21023
|
+
console.error(chalk3.yellow(`Warning: this query reached its ${scanCeiling}-row scan limit, so the rows below were
|
|
21024
|
+
` + ` selected from a truncated set and may not be the true result.
|
|
21025
|
+
` + ` Narrow it (--project, --status, --assigned) or raise TODOS_LIST_SCAN_LIMIT.`));
|
|
21026
|
+
}
|
|
20866
21027
|
if (cloud && creatorFilterActive) {
|
|
20867
21028
|
if (tasks.length > 0 && tasks.every((t) => !("created_by" in t))) {
|
|
20868
21029
|
console.error(chalk3.yellow(`Warning: this server does not record task authorship, so the creator filter matched nothing to exclude.
|
|
@@ -20879,8 +21040,6 @@ function registerTaskCommands(program2) {
|
|
|
20879
21040
|
return false;
|
|
20880
21041
|
return true;
|
|
20881
21042
|
});
|
|
20882
|
-
if (requestedLimit !== undefined)
|
|
20883
|
-
tasks = tasks.slice(0, requestedLimit);
|
|
20884
21043
|
}
|
|
20885
21044
|
if (opts.dueToday) {
|
|
20886
21045
|
const todayEnd = new Date;
|
|
@@ -20905,6 +21064,21 @@ function registerTaskCommands(program2) {
|
|
|
20905
21064
|
return 0;
|
|
20906
21065
|
});
|
|
20907
21066
|
}
|
|
21067
|
+
if (withholdLimit && requestedLimit !== undefined)
|
|
21068
|
+
tasks = tasks.slice(0, requestedLimit);
|
|
21069
|
+
if (assignedWasTyped && assignedFilter && tasks.length === 0) {
|
|
21070
|
+
try {
|
|
21071
|
+
const roster = await loadAssigneeContext(() => cloud ? cloudListAgents(cloud) : listAgents(), true);
|
|
21072
|
+
if (!roster.degraded) {
|
|
21073
|
+
const target = canonicalAgentRef(assignedFilter);
|
|
21074
|
+
const known = roster.agents.some((a) => canonicalAgentRef(a.name) === target || canonicalAgentRef(a.id) === target);
|
|
21075
|
+
if (!known) {
|
|
21076
|
+
console.error(chalk3.yellow(`Warning: no agent named '${assignedFilter}' is registered, so this empty result may be a
|
|
21077
|
+
` + ` mistyped name rather than an empty queue. Check with 'todos agents'.`));
|
|
21078
|
+
}
|
|
21079
|
+
}
|
|
21080
|
+
} catch {}
|
|
21081
|
+
}
|
|
20908
21082
|
const fmt = opts.format || (globalOpts.json ? "json" : "table");
|
|
20909
21083
|
const outputTasks = redactBroadTasks(tasks);
|
|
20910
21084
|
if (fmt === "json") {
|
|
@@ -21676,17 +21850,26 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21676
21850
|
handleError(new Error("Task not found."));
|
|
21677
21851
|
}
|
|
21678
21852
|
});
|
|
21679
|
-
program2.command("bulk <action> <ids...>").description("Bulk operation on multiple tasks (done, start, delete, plan/move-plan)").option("--plan <id>", "Plan ID for the plan/move-plan action").option("--clear-plan", "Remove plan assignment for the plan/move-plan action").action(async (action, ids, opts) => {
|
|
21853
|
+
program2.command("bulk <action> <ids...>").description("Bulk operation on multiple tasks (done, start, delete, plan/move-plan, tag, untag)").option("--plan <id>", "Plan ID for the plan/move-plan action").option("--clear-plan", "Remove plan assignment for the plan/move-plan action").option("--tag <tags>", "Comma-separated tags for the tag/untag action").option("--tags <tags>", "Comma-separated tags for the tag/untag action (alias for --tag)").action(async (action, ids, opts) => {
|
|
21680
21854
|
const globalOpts = program2.opts();
|
|
21681
21855
|
const results = [];
|
|
21682
21856
|
const cloud = getTodosCloudClient();
|
|
21683
21857
|
const isPlanAction = action === "plan" || action === "move-plan";
|
|
21858
|
+
const isTagAction = action === "tag" || action === "untag";
|
|
21684
21859
|
if (isPlanAction && Boolean(opts.plan) === Boolean(opts.clearPlan)) {
|
|
21685
21860
|
handleError(new Error("Use exactly one of --plan or --clear-plan with bulk plan."));
|
|
21686
21861
|
}
|
|
21687
|
-
const knownActions = new Set(["done", "complete", "start", "delete", "plan", "move-plan"]);
|
|
21862
|
+
const knownActions = new Set(["done", "complete", "start", "delete", "plan", "move-plan", "tag", "untag"]);
|
|
21688
21863
|
if (!knownActions.has(action)) {
|
|
21689
|
-
handleError(new Error(`Unknown action: ${action}. Use: done, start, delete, plan (alias: move-plan)`));
|
|
21864
|
+
handleError(new Error(`Unknown action: ${action}. Use: done, start, delete, plan (alias: move-plan), tag, untag`));
|
|
21865
|
+
}
|
|
21866
|
+
const tagArgument = resolveTagArgument(opts.tag, opts.tags);
|
|
21867
|
+
if (isTagAction && !tagArgument.ok) {
|
|
21868
|
+
handleError(new Error(`--tag and --tags name different tag sets (--tag "${tagArgument.conflict.tag}" vs --tags "${tagArgument.conflict.tags}"). ` + "They are aliases; pass one, or pass the same set to both."));
|
|
21869
|
+
}
|
|
21870
|
+
const bulkTags = isTagAction && tagArgument.ok ? parseTagList(tagArgument.raw) : [];
|
|
21871
|
+
if (isTagAction && bulkTags.length === 0) {
|
|
21872
|
+
handleError(new Error(`bulk ${action} needs --tag. Example: todos bulk ${action} <ids...> --tag directive:k_msd4cz8t_ste6f4`));
|
|
21690
21873
|
}
|
|
21691
21874
|
const bulkClaimAgentId = action === "start" ? resolveClaimIdentity("start", globalOpts.agent) : undefined;
|
|
21692
21875
|
if (cloud) {
|
|
@@ -21715,6 +21898,14 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21715
21898
|
await cloudTaskAction(cloud, resolvedId, "start", { agent_id: bulkClaimAgentId });
|
|
21716
21899
|
} else if (action === "delete") {
|
|
21717
21900
|
await cloudDeleteTask(cloud, resolvedId);
|
|
21901
|
+
} else if (isTagAction) {
|
|
21902
|
+
const current = await cloudGetTask(cloud, resolvedId);
|
|
21903
|
+
if (!current)
|
|
21904
|
+
throw new Error(`Task not found: ${rawId}`);
|
|
21905
|
+
const resolution = resolveBulkTags(current.tags, action, bulkTags);
|
|
21906
|
+
if (resolution.changed) {
|
|
21907
|
+
await cloudUpdateTask(cloud, resolvedId, { version: current.version, tags: resolution.tags });
|
|
21908
|
+
}
|
|
21718
21909
|
} else {
|
|
21719
21910
|
const current = await cloudGetTask(cloud, resolvedId);
|
|
21720
21911
|
if (!current)
|
|
@@ -21751,6 +21942,16 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21751
21942
|
} else if (action === "delete") {
|
|
21752
21943
|
deleteTask(resolvedId);
|
|
21753
21944
|
results.push({ id: resolvedId, success: true });
|
|
21945
|
+
} else if (isTagAction) {
|
|
21946
|
+
const current = getTask(resolvedId);
|
|
21947
|
+
if (!current) {
|
|
21948
|
+
throw new Error(`Task not found: ${rawId}`);
|
|
21949
|
+
}
|
|
21950
|
+
const resolution = resolveBulkTags(current.tags, action, bulkTags);
|
|
21951
|
+
if (resolution.changed) {
|
|
21952
|
+
updateTask(resolvedId, { version: current.version, tags: resolution.tags });
|
|
21953
|
+
}
|
|
21954
|
+
results.push({ id: resolvedId, success: true });
|
|
21754
21955
|
} else if (isPlanAction) {
|
|
21755
21956
|
const current = getTask(resolvedId);
|
|
21756
21957
|
if (!current) {
|
|
@@ -21759,7 +21960,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21759
21960
|
updateTask(resolvedId, { version: current.version, plan_id: planId });
|
|
21760
21961
|
results.push({ id: resolvedId, success: true });
|
|
21761
21962
|
} else {
|
|
21762
|
-
handleError(new Error(`Unknown action: ${action}. Use: done, start, delete, plan (alias: move-plan)`));
|
|
21963
|
+
handleError(new Error(`Unknown action: ${action}. Use: done, start, delete, plan (alias: move-plan), tag, untag`));
|
|
21763
21964
|
}
|
|
21764
21965
|
} catch (e) {
|
|
21765
21966
|
results.push({ id: rawId, success: false, error: e instanceof Error ? e.message : String(e) });
|
|
@@ -21777,6 +21978,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21777
21978
|
}
|
|
21778
21979
|
});
|
|
21779
21980
|
}
|
|
21981
|
+
var DEFAULT_LIST_SCAN_LIMIT = 1e4;
|
|
21780
21982
|
var init_task_commands = __esm(() => {
|
|
21781
21983
|
init_database();
|
|
21782
21984
|
init_projects();
|
|
@@ -21787,6 +21989,8 @@ var init_task_commands = __esm(() => {
|
|
|
21787
21989
|
init_lock_display();
|
|
21788
21990
|
init_claim_guard();
|
|
21789
21991
|
init_assignee_guard();
|
|
21992
|
+
init_assignee_context();
|
|
21993
|
+
init_agents();
|
|
21790
21994
|
init_helpers();
|
|
21791
21995
|
init_output_redaction();
|
|
21792
21996
|
});
|
|
@@ -39195,7 +39399,13 @@ async function handleV1Request(req, url, dependencies = {}) {
|
|
|
39195
39399
|
return error(405, `method ${method} not allowed on /v1/refs/:ref`);
|
|
39196
39400
|
if (!store.gitRefs)
|
|
39197
39401
|
return error(501, "git ref links are not supported by this storage backend");
|
|
39198
|
-
|
|
39402
|
+
let decodedRef;
|
|
39403
|
+
try {
|
|
39404
|
+
decodedRef = decodeURIComponent(id);
|
|
39405
|
+
} catch {
|
|
39406
|
+
return error(400, "ref path segment has invalid percent encoding");
|
|
39407
|
+
}
|
|
39408
|
+
const refs = await store.gitRefs.find(decodedRef);
|
|
39199
39409
|
return json3({ refs, count: refs.length });
|
|
39200
39410
|
}
|
|
39201
39411
|
if (resource === "next" && !id) {
|
|
@@ -43793,7 +44003,7 @@ async function validateMcpAssignee(value, allowSeat) {
|
|
|
43793
44003
|
return verdict.assignee;
|
|
43794
44004
|
}
|
|
43795
44005
|
function registerTaskCrudTools(server, ctx) {
|
|
43796
|
-
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
44006
|
+
const { shouldRegisterTool, resolveId, formatError, formatTask, applyFocus } = ctx;
|
|
43797
44007
|
function mutationTaskResponse(task) {
|
|
43798
44008
|
const compact = compactTask(task, 240);
|
|
43799
44009
|
compact["version"] = task.version;
|
|
@@ -43826,7 +44036,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
43826
44036
|
project_id: exports_external.string().optional().describe("Project ID"),
|
|
43827
44037
|
task_list_id: exports_external.string().optional().describe("Task list ID"),
|
|
43828
44038
|
assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
|
|
43829
|
-
created_by: exports_external.string().optional().describe("Agent who FILED this task. Defaults to the ambient agent identity
|
|
44039
|
+
created_by: exports_external.string().optional().describe("Agent who FILED this task. Defaults to the PROCESS-BOUND ambient identity (--agent / TODOS_AGENT_ID) if set; the machine-shared identity `todos init` persists to disk is never used here, because it names the station, not the caller."),
|
|
43830
44040
|
unassigned: exports_external.boolean().optional().describe("Deliberately file with no assignee. Without it, the task defaults to the filer."),
|
|
43831
44041
|
allow_seat: exports_external.boolean().optional().describe("Allow assigned_to to name a durable seat. A seat queue has no session watching it, so this must be deliberate."),
|
|
43832
44042
|
depends_on: exports_external.array(exports_external.string()).optional().describe("Array of task IDs this task depends on"),
|
|
@@ -43841,14 +44051,13 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
43841
44051
|
try {
|
|
43842
44052
|
const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, created_by, unassigned, allow_seat, ...rest } = params;
|
|
43843
44053
|
const requestedAssignee = assigned_to ? await validateMcpAssignee(assigned_to, Boolean(allow_seat)) : undefined;
|
|
43844
|
-
const creator = resolveCreatorIdentity(created_by);
|
|
43845
44054
|
const router = resolveWritableIdentity(created_by);
|
|
43846
44055
|
const assignee = requestedAssignee || (unassigned ? undefined : router.agent_id || undefined);
|
|
43847
44056
|
const cloud = getTodosCloudClient();
|
|
43848
44057
|
if (cloud) {
|
|
43849
44058
|
const payload = { ...rest };
|
|
43850
|
-
if (
|
|
43851
|
-
payload.created_by =
|
|
44059
|
+
if (router.agent_id)
|
|
44060
|
+
payload.created_by = router.agent_id;
|
|
43852
44061
|
if (router.agent_id)
|
|
43853
44062
|
payload.agent_id = payload.agent_id ?? router.agent_id;
|
|
43854
44063
|
if (assignee)
|
|
@@ -43869,12 +44078,13 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
43869
44078
|
payload.max_retries = retry_count;
|
|
43870
44079
|
if (deadline)
|
|
43871
44080
|
payload.due_at = deadline;
|
|
44081
|
+
applyFocus(payload, router.agent_id || undefined);
|
|
43872
44082
|
const created = await cloudCreateTask(cloud, payload);
|
|
43873
44083
|
return { content: [{ type: "text", text: mutationTaskResponse(created) }] };
|
|
43874
44084
|
}
|
|
43875
44085
|
const resolved = { ...rest };
|
|
43876
|
-
if (
|
|
43877
|
-
resolved.created_by =
|
|
44086
|
+
if (router.agent_id)
|
|
44087
|
+
resolved.created_by = router.agent_id;
|
|
43878
44088
|
if (router.agent_id)
|
|
43879
44089
|
resolved.agent_id = resolved.agent_id ?? router.agent_id;
|
|
43880
44090
|
if (assignee)
|
|
@@ -43895,6 +44105,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
43895
44105
|
resolved.max_retries = retry_count;
|
|
43896
44106
|
if (deadline)
|
|
43897
44107
|
resolved.due_at = deadline;
|
|
44108
|
+
applyFocus(resolved, router.agent_id || undefined);
|
|
43898
44109
|
const task = createTask(resolved);
|
|
43899
44110
|
return { content: [{ type: "text", text: mutationTaskResponse(task) }] };
|
|
43900
44111
|
} catch (e) {
|
|
@@ -63133,7 +63344,8 @@ function buildServer() {
|
|
|
63133
63344
|
formatError,
|
|
63134
63345
|
formatTask,
|
|
63135
63346
|
formatTaskDetail,
|
|
63136
|
-
getAgentFocus
|
|
63347
|
+
getAgentFocus,
|
|
63348
|
+
applyFocus
|
|
63137
63349
|
};
|
|
63138
63350
|
registerTaskCrudTools(server, toolContext);
|
|
63139
63351
|
registerTaskProjectTools(server, toolContext);
|
|
@@ -66580,7 +66792,7 @@ ${error2.message}
|
|
|
66580
66792
|
const globalOpts = program2.opts();
|
|
66581
66793
|
const projectId = autoProject(globalOpts);
|
|
66582
66794
|
const interval = parseInt(opts.interval, 10) * 1000;
|
|
66583
|
-
const statusFilter = opts.status
|
|
66795
|
+
const statusFilter = parseEnumFlagList(opts.status, TASK_STATUS_FLAG) ?? ["pending", "in_progress"];
|
|
66584
66796
|
function render2() {
|
|
66585
66797
|
const tasks = listTasks({ project_id: projectId, status: statusFilter });
|
|
66586
66798
|
const all = listTasks({ project_id: projectId });
|
|
@@ -69187,8 +69399,8 @@ Findings`));
|
|
|
69187
69399
|
const row = db.query("SELECT COUNT(*) as count FROM tasks").get();
|
|
69188
69400
|
const { statSync: statSync12 } = await import("fs");
|
|
69189
69401
|
const { join: join27 } = await import("path");
|
|
69190
|
-
const
|
|
69191
|
-
const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join27(
|
|
69402
|
+
const { getHomeDir: getHomeDir2 } = await Promise.resolve().then(() => (init_sync_utils(), exports_sync_utils));
|
|
69403
|
+
const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join27(getHomeDir2(), ".hasna", "todos", "todos.db");
|
|
69192
69404
|
let size = "unknown";
|
|
69193
69405
|
try {
|
|
69194
69406
|
size = `${(statSync12(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
|
|
@@ -72353,7 +72565,8 @@ var init_mcp_hooks_commands = __esm(() => {
|
|
|
72353
72565
|
init_tasks();
|
|
72354
72566
|
init_helpers();
|
|
72355
72567
|
init_cloud_router();
|
|
72356
|
-
|
|
72568
|
+
init_sync_utils();
|
|
72569
|
+
HOME2 = getHomeDir();
|
|
72357
72570
|
});
|
|
72358
72571
|
|
|
72359
72572
|
// src/cli/commands/dispatch.tsx
|