@hasna/todos 0.13.12 → 0.15.1
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/agent-commands.d.ts +39 -0
- package/dist/cli/commands/agent-commands.d.ts.map +1 -1
- package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
- package/dist/cli/commands/dispatch.d.ts.map +1 -1
- package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
- package/dist/cli/commands/project-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts +64 -2
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +624 -98
- package/dist/cli/stage-a.d.ts.map +1 -1
- package/dist/contracts.js +123 -116
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/index.js +135 -127
- 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 +72 -0
- package/dist/lib/enum-vocabulary.d.ts.map +1 -0
- package/dist/lib/run-records.d.ts.map +1 -1
- package/dist/lib/sandbox-profiles.d.ts.map +1 -1
- package/dist/lib/saved-search-views.d.ts +12 -1
- package/dist/lib/saved-search-views.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 +289 -82
- package/dist/mcp/tools/agents.d.ts.map +1 -1
- package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
- 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 +123 -116
- package/dist/release-provenance.json +5 -5
- package/dist/sdk/index.js +3 -2
- package/dist/sdk/v1.generated.d.ts +2 -2
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/index.js +245 -38
- package/dist/server/openapi.d.ts +58 -0
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage.js +124 -115
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -1
- 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.1",
|
|
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",
|
|
@@ -2258,7 +2258,10 @@ var init_package_version = __esm(() => {
|
|
|
2258
2258
|
function isBlockingDependencyStatus(status) {
|
|
2259
2259
|
return status !== "completed" && status !== "cancelled";
|
|
2260
2260
|
}
|
|
2261
|
-
|
|
2261
|
+
function isTerminalStatus(status) {
|
|
2262
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
2263
|
+
}
|
|
2264
|
+
var TASK_STATUSES, TASK_PRIORITIES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanNotFoundError, LockError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
|
|
2262
2265
|
var init_types = __esm(() => {
|
|
2263
2266
|
TASK_STATUSES = [
|
|
2264
2267
|
"pending",
|
|
@@ -2427,6 +2430,7 @@ var init_types = __esm(() => {
|
|
|
2427
2430
|
this.name = "CompletionGuardError";
|
|
2428
2431
|
}
|
|
2429
2432
|
};
|
|
2433
|
+
DISPATCH_STATUSES = ["pending", "sent", "failed", "cancelled"];
|
|
2430
2434
|
DispatchNotFoundError = class DispatchNotFoundError extends Error {
|
|
2431
2435
|
dispatchId;
|
|
2432
2436
|
static code = "DISPATCH_NOT_FOUND";
|
|
@@ -2440,11 +2444,30 @@ var init_types = __esm(() => {
|
|
|
2440
2444
|
});
|
|
2441
2445
|
|
|
2442
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
|
+
});
|
|
2443
2465
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
2444
2466
|
import { createHash } from "crypto";
|
|
2467
|
+
import { homedir } from "os";
|
|
2445
2468
|
import { join } from "path";
|
|
2446
2469
|
function getHomeDir() {
|
|
2447
|
-
return process.env["HOME"] || process.env["USERPROFILE"] ||
|
|
2470
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir();
|
|
2448
2471
|
}
|
|
2449
2472
|
function getTodosGlobalDir() {
|
|
2450
2473
|
return join(getHomeDir(), ".hasna", "todos");
|
|
@@ -2532,7 +2555,7 @@ function hasSyncFingerprintChanged(record) {
|
|
|
2532
2555
|
}
|
|
2533
2556
|
var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
|
|
2534
2557
|
var init_sync_utils = __esm(() => {
|
|
2535
|
-
HOME =
|
|
2558
|
+
HOME = getHomeDir();
|
|
2536
2559
|
});
|
|
2537
2560
|
|
|
2538
2561
|
// src/lib/config.ts
|
|
@@ -6410,6 +6433,15 @@ function hasOption(args, option) {
|
|
|
6410
6433
|
function positionalArgs(args) {
|
|
6411
6434
|
return args.filter((arg) => !arg.startsWith("-"));
|
|
6412
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
|
+
}
|
|
6413
6445
|
function isReadOnlyConfigInvocation(invocation) {
|
|
6414
6446
|
if (invocation.command !== "config")
|
|
6415
6447
|
return false;
|
|
@@ -6430,6 +6462,8 @@ function isMetadataInvocation(args, invocation) {
|
|
|
6430
6462
|
return true;
|
|
6431
6463
|
if (invocation.command === "manual" && invocation.commandArgs.length === 0)
|
|
6432
6464
|
return true;
|
|
6465
|
+
if (isBundledStaticInvocation(invocation))
|
|
6466
|
+
return true;
|
|
6433
6467
|
if (invocation.command === "help" && invocation.commandArgs.every((arg) => !arg.startsWith("-")))
|
|
6434
6468
|
return true;
|
|
6435
6469
|
if (invocation.command === "config") {
|
|
@@ -6447,6 +6481,14 @@ function firstPresentOption(args, candidates) {
|
|
|
6447
6481
|
function disqualifyingArgument(invocation) {
|
|
6448
6482
|
const command = invocation.command;
|
|
6449
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
|
+
}
|
|
6450
6492
|
switch (command) {
|
|
6451
6493
|
case "task":
|
|
6452
6494
|
return positionalArgs(args)[0] === "upsert" ? null : { blame: "any subcommand other than `upsert`", remedy: "use `todos task upsert`" };
|
|
@@ -6474,13 +6516,22 @@ function disqualifyingArgument(invocation) {
|
|
|
6474
6516
|
const action = positionalArgs(args)[0];
|
|
6475
6517
|
if (action === "plan" || action === "move-plan")
|
|
6476
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
|
+
}
|
|
6477
6528
|
if (!action) {
|
|
6478
|
-
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" };
|
|
6479
6530
|
}
|
|
6480
6531
|
if (!["done", "complete", "start", "delete"].includes(action)) {
|
|
6481
6532
|
return {
|
|
6482
6533
|
blame: `the \`${action}\` action`,
|
|
6483
|
-
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"
|
|
6484
6535
|
};
|
|
6485
6536
|
}
|
|
6486
6537
|
return firstPresentOption(args, ["--plan", "--clear-plan"]);
|
|
@@ -6537,7 +6588,8 @@ function assertRemoteCommandSupported(invocation) {
|
|
|
6537
6588
|
}
|
|
6538
6589
|
if (!command || !commandSupportsRemote(invocation)) {
|
|
6539
6590
|
const blame = command ? disqualifyingArgument(invocation) : null;
|
|
6540
|
-
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`;
|
|
6541
6593
|
throw new Error(`REMOTE_COMMAND_UNSUPPORTED: ${detail}`);
|
|
6542
6594
|
}
|
|
6543
6595
|
}
|
|
@@ -6557,7 +6609,7 @@ function initializeTodosCliAuthority(args = process.argv.slice(2), env = process
|
|
|
6557
6609
|
}
|
|
6558
6610
|
return { route: "remote-http", v1_base_url: client.baseUrl };
|
|
6559
6611
|
}
|
|
6560
|
-
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" });
|
|
6561
6613
|
var init_stage_a = __esm(() => {
|
|
6562
6614
|
init_esm();
|
|
6563
6615
|
init_cloud_router();
|
|
@@ -6747,7 +6799,28 @@ var init_stage_a = __esm(() => {
|
|
|
6747
6799
|
reliability: ["scorecards"],
|
|
6748
6800
|
lists: ["task-lists", "tl"]
|
|
6749
6801
|
};
|
|
6750
|
-
|
|
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
|
+
]);
|
|
6751
6824
|
REMOTE_COMMANDS = new Set([
|
|
6752
6825
|
"active",
|
|
6753
6826
|
"add",
|
|
@@ -10769,8 +10842,7 @@ function findGitRoot(startDir) {
|
|
|
10769
10842
|
return null;
|
|
10770
10843
|
}
|
|
10771
10844
|
function getGlobalDbPath() {
|
|
10772
|
-
|
|
10773
|
-
return join3(home, ".hasna", "todos", "todos.db");
|
|
10845
|
+
return join3(getHomeDir(), ".hasna", "todos", "todos.db");
|
|
10774
10846
|
}
|
|
10775
10847
|
function hasExplicitProjectArg(args = process.argv.slice(2)) {
|
|
10776
10848
|
return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
|
|
@@ -10992,6 +11064,7 @@ var init_database = __esm(() => {
|
|
|
10992
11064
|
init_machines();
|
|
10993
11065
|
init_identity_mapping();
|
|
10994
11066
|
init_types();
|
|
11067
|
+
init_sync_utils();
|
|
10995
11068
|
ALLOWED_TABLES = new Set(["tasks", "projects", "agents", "plans", "task_lists", "task_templates", "project_knowledge_records", "project_risks", "local_retrospectives"]);
|
|
10996
11069
|
});
|
|
10997
11070
|
|
|
@@ -11456,6 +11529,103 @@ var init_lock_display = __esm(() => {
|
|
|
11456
11529
|
init_database();
|
|
11457
11530
|
});
|
|
11458
11531
|
|
|
11532
|
+
// src/lib/enum-vocabulary.ts
|
|
11533
|
+
function editDistance2(a, b) {
|
|
11534
|
+
if (a === b)
|
|
11535
|
+
return 0;
|
|
11536
|
+
if (!a.length)
|
|
11537
|
+
return b.length;
|
|
11538
|
+
if (!b.length)
|
|
11539
|
+
return a.length;
|
|
11540
|
+
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
11541
|
+
for (let i = 1;i <= a.length; i += 1) {
|
|
11542
|
+
const current = [i];
|
|
11543
|
+
for (let j = 1;j <= b.length; j += 1) {
|
|
11544
|
+
current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
11545
|
+
}
|
|
11546
|
+
previous = current;
|
|
11547
|
+
}
|
|
11548
|
+
return previous[b.length];
|
|
11549
|
+
}
|
|
11550
|
+
function suggestVocabularyMatches(value, vocabulary, limit = 3) {
|
|
11551
|
+
const needle = value.trim().toLowerCase();
|
|
11552
|
+
if (!needle)
|
|
11553
|
+
return [];
|
|
11554
|
+
const scored = [];
|
|
11555
|
+
for (const member of vocabulary) {
|
|
11556
|
+
const candidate = member.toLowerCase();
|
|
11557
|
+
if (candidate.startsWith(needle) || needle.startsWith(candidate)) {
|
|
11558
|
+
scored.push({ member, score: 0 });
|
|
11559
|
+
continue;
|
|
11560
|
+
}
|
|
11561
|
+
if (candidate.includes(needle) || needle.includes(candidate)) {
|
|
11562
|
+
scored.push({ member, score: 1 });
|
|
11563
|
+
continue;
|
|
11564
|
+
}
|
|
11565
|
+
const distance = editDistance2(needle, candidate);
|
|
11566
|
+
if (distance <= Math.max(1, Math.floor(candidate.length / 3))) {
|
|
11567
|
+
scored.push({ member, score: 1 + distance });
|
|
11568
|
+
}
|
|
11569
|
+
}
|
|
11570
|
+
return scored.sort((a, b) => a.score - b.score || a.member.localeCompare(b.member)).slice(0, limit).map((entry) => entry.member);
|
|
11571
|
+
}
|
|
11572
|
+
function resolveEnumVocabulary(raw, spec) {
|
|
11573
|
+
const allowList = spec.allowList !== false;
|
|
11574
|
+
const rawElements = (allowList ? raw.split(",") : [raw]).map((element) => element.trim());
|
|
11575
|
+
if (rawElements.every((element) => element.length === 0)) {
|
|
11576
|
+
return {
|
|
11577
|
+
ok: false,
|
|
11578
|
+
message: `${spec.name} requires a value. Allowed values: ${spec.vocabulary.join(", ")}.`,
|
|
11579
|
+
invalid: []
|
|
11580
|
+
};
|
|
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
|
+
}
|
|
11589
|
+
const normalized = rawElements.map((element) => spec.normalize ? spec.normalize(element) : element);
|
|
11590
|
+
const allowed = new Set(spec.vocabulary);
|
|
11591
|
+
const invalid2 = [];
|
|
11592
|
+
for (let i = 0;i < normalized.length; i += 1) {
|
|
11593
|
+
if (!allowed.has(normalized[i]))
|
|
11594
|
+
invalid2.push(rawElements[i]);
|
|
11595
|
+
}
|
|
11596
|
+
if (invalid2.length === 0) {
|
|
11597
|
+
return { ok: true, values: [...new Set(normalized)] };
|
|
11598
|
+
}
|
|
11599
|
+
const label = invalid2.length === 1 ? "value" : "values";
|
|
11600
|
+
const parts = [
|
|
11601
|
+
`Invalid ${spec.name} ${label}: ${invalid2.join(", ")}.`,
|
|
11602
|
+
`Allowed values: ${spec.vocabulary.join(", ")}.`
|
|
11603
|
+
];
|
|
11604
|
+
const hints = new Set;
|
|
11605
|
+
const suggestions = new Set;
|
|
11606
|
+
for (let i = 0;i < normalized.length; i += 1) {
|
|
11607
|
+
const canonical = normalized[i];
|
|
11608
|
+
if (allowed.has(canonical))
|
|
11609
|
+
continue;
|
|
11610
|
+
const hint = spec.hints?.[canonical.toLowerCase()];
|
|
11611
|
+
if (hint) {
|
|
11612
|
+
hints.add(hint);
|
|
11613
|
+
continue;
|
|
11614
|
+
}
|
|
11615
|
+
for (const match of suggestVocabularyMatches(canonical, spec.vocabulary)) {
|
|
11616
|
+
suggestions.add(match);
|
|
11617
|
+
}
|
|
11618
|
+
}
|
|
11619
|
+
if (suggestions.size > 0)
|
|
11620
|
+
parts.push(`Did you mean ${[...suggestions].join(", ")}?`);
|
|
11621
|
+
for (const hint of hints)
|
|
11622
|
+
parts.push(hint);
|
|
11623
|
+
return { ok: false, message: parts.join(" "), invalid: invalid2 };
|
|
11624
|
+
}
|
|
11625
|
+
function collapseEnumValues(values) {
|
|
11626
|
+
return values.length === 1 ? values[0] : values;
|
|
11627
|
+
}
|
|
11628
|
+
|
|
11459
11629
|
// src/cli/helpers.ts
|
|
11460
11630
|
var exports_helpers = {};
|
|
11461
11631
|
__export(exports_helpers, {
|
|
@@ -11466,9 +11636,13 @@ __export(exports_helpers, {
|
|
|
11466
11636
|
resolveExplicitProject: () => resolveExplicitProject,
|
|
11467
11637
|
priorityColors: () => priorityColors,
|
|
11468
11638
|
printJson: () => printJson,
|
|
11639
|
+
parseEnumFlagList: () => parseEnumFlagList,
|
|
11640
|
+
parseEnumFlag: () => parseEnumFlag,
|
|
11641
|
+
outputRecord: () => outputRecord,
|
|
11469
11642
|
output: () => output,
|
|
11470
11643
|
normalizeStatusList: () => normalizeStatusList,
|
|
11471
11644
|
normalizeStatus: () => normalizeStatus,
|
|
11645
|
+
normalizePriority: () => normalizePriority,
|
|
11472
11646
|
jsonModeRequested: () => jsonModeRequested,
|
|
11473
11647
|
handleError: () => handleError,
|
|
11474
11648
|
getPackageVersion: () => getPackageVersion,
|
|
@@ -11476,7 +11650,10 @@ __export(exports_helpers, {
|
|
|
11476
11650
|
detectGitRoot: () => detectGitRoot,
|
|
11477
11651
|
cacheCloudTaskForIdResolution: () => cacheCloudTaskForIdResolution,
|
|
11478
11652
|
autoProject: () => autoProject,
|
|
11479
|
-
autoDetectProject: () => autoDetectProject
|
|
11653
|
+
autoDetectProject: () => autoDetectProject,
|
|
11654
|
+
TASK_STATUS_FLAG_HINTS: () => TASK_STATUS_FLAG_HINTS,
|
|
11655
|
+
TASK_STATUS_FLAG: () => TASK_STATUS_FLAG,
|
|
11656
|
+
TASK_PRIORITY_FLAG: () => TASK_PRIORITY_FLAG
|
|
11480
11657
|
});
|
|
11481
11658
|
import chalk from "chalk";
|
|
11482
11659
|
import { execSync } from "child_process";
|
|
@@ -11486,8 +11663,19 @@ import { dirname as dirname3, join as join4, resolve as resolve3, sep } from "pa
|
|
|
11486
11663
|
function jsonModeRequested(argv = process.argv) {
|
|
11487
11664
|
return argv.some((arg) => arg === "--json" || /^-[a-z]+$/i.test(arg) && arg.includes("j"));
|
|
11488
11665
|
}
|
|
11666
|
+
function remoteErrorDetail(e) {
|
|
11667
|
+
if (!e || typeof e !== "object")
|
|
11668
|
+
return null;
|
|
11669
|
+
const body = e.body;
|
|
11670
|
+
if (!body || typeof body !== "object" || Array.isArray(body))
|
|
11671
|
+
return null;
|
|
11672
|
+
const detail = body.error;
|
|
11673
|
+
return typeof detail === "string" && detail.trim() ? detail.trim() : null;
|
|
11674
|
+
}
|
|
11489
11675
|
function handleError(e) {
|
|
11490
|
-
const
|
|
11676
|
+
const baseMessage = e instanceof Error ? e.message : String(e);
|
|
11677
|
+
const detail = remoteErrorDetail(e);
|
|
11678
|
+
const message = detail && !baseMessage.includes(detail) ? `${baseMessage}: ${detail}` : baseMessage;
|
|
11491
11679
|
console.error(chalk.red(message));
|
|
11492
11680
|
if (jsonModeRequested()) {
|
|
11493
11681
|
console.log(JSON.stringify({ error: message }));
|
|
@@ -11665,7 +11853,8 @@ function autoProject(opts) {
|
|
|
11665
11853
|
return autoDetectProject(opts)?.id;
|
|
11666
11854
|
}
|
|
11667
11855
|
function normalizeStatus(s) {
|
|
11668
|
-
|
|
11856
|
+
const folded = s.toLowerCase().trim();
|
|
11857
|
+
switch (folded) {
|
|
11669
11858
|
case "done":
|
|
11670
11859
|
return "completed";
|
|
11671
11860
|
case "complete":
|
|
@@ -11679,14 +11868,33 @@ function normalizeStatus(s) {
|
|
|
11679
11868
|
case "canceled":
|
|
11680
11869
|
return "cancelled";
|
|
11681
11870
|
default:
|
|
11682
|
-
return
|
|
11871
|
+
return folded;
|
|
11683
11872
|
}
|
|
11684
11873
|
}
|
|
11874
|
+
function normalizePriority(p) {
|
|
11875
|
+
return p.toLowerCase().trim();
|
|
11876
|
+
}
|
|
11685
11877
|
function normalizeStatusList(statuses) {
|
|
11686
11878
|
if (Array.isArray(statuses))
|
|
11687
11879
|
return statuses.map(normalizeStatus);
|
|
11688
11880
|
return normalizeStatus(statuses);
|
|
11689
11881
|
}
|
|
11882
|
+
function parseEnumFlag(raw, spec) {
|
|
11883
|
+
if (raw === undefined || raw === null)
|
|
11884
|
+
return;
|
|
11885
|
+
const result = resolveEnumVocabulary(String(raw), spec);
|
|
11886
|
+
if (!result.ok)
|
|
11887
|
+
handleError(new Error(result.message));
|
|
11888
|
+
return collapseEnumValues(result.values);
|
|
11889
|
+
}
|
|
11890
|
+
function parseEnumFlagList(raw, spec) {
|
|
11891
|
+
if (raw === undefined || raw === null)
|
|
11892
|
+
return;
|
|
11893
|
+
const result = resolveEnumVocabulary(String(raw), spec);
|
|
11894
|
+
if (!result.ok)
|
|
11895
|
+
handleError(new Error(result.message));
|
|
11896
|
+
return result.values;
|
|
11897
|
+
}
|
|
11690
11898
|
function writeStdoutSync(text) {
|
|
11691
11899
|
const buffer = Buffer.from(text);
|
|
11692
11900
|
let offset = 0;
|
|
@@ -11718,6 +11926,41 @@ function output(data, jsonMode) {
|
|
|
11718
11926
|
printJson(data);
|
|
11719
11927
|
}
|
|
11720
11928
|
}
|
|
11929
|
+
function formatRecordLines(data) {
|
|
11930
|
+
if (data === null || data === undefined)
|
|
11931
|
+
return [];
|
|
11932
|
+
if (typeof data !== "object")
|
|
11933
|
+
return [String(data)];
|
|
11934
|
+
if (Array.isArray(data)) {
|
|
11935
|
+
return data.flatMap((entry, index) => [
|
|
11936
|
+
chalk.dim(`[${index}]`),
|
|
11937
|
+
...formatRecordLines(entry).map((line) => ` ${line}`)
|
|
11938
|
+
]);
|
|
11939
|
+
}
|
|
11940
|
+
const lines = [];
|
|
11941
|
+
for (const [key, value] of Object.entries(data)) {
|
|
11942
|
+
if (value === undefined)
|
|
11943
|
+
continue;
|
|
11944
|
+
const rendered = value === null ? chalk.dim("\u2014") : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
11945
|
+
lines.push(` ${chalk.dim(`${key}:`)} ${rendered}`);
|
|
11946
|
+
}
|
|
11947
|
+
return lines;
|
|
11948
|
+
}
|
|
11949
|
+
function outputRecord(data, jsonMode, heading) {
|
|
11950
|
+
if (jsonMode) {
|
|
11951
|
+
printJson(data);
|
|
11952
|
+
return;
|
|
11953
|
+
}
|
|
11954
|
+
if (heading)
|
|
11955
|
+
console.log(chalk.bold(heading));
|
|
11956
|
+
const lines = formatRecordLines(data);
|
|
11957
|
+
if (lines.length === 0) {
|
|
11958
|
+
console.log(chalk.dim("(empty)"));
|
|
11959
|
+
return;
|
|
11960
|
+
}
|
|
11961
|
+
for (const line of lines)
|
|
11962
|
+
console.log(line);
|
|
11963
|
+
}
|
|
11721
11964
|
function formatTaskLine(t) {
|
|
11722
11965
|
const statusFn = statusColors[t.status] || chalk.white;
|
|
11723
11966
|
const priorityFn = priorityColors[t.priority] || chalk.white;
|
|
@@ -11728,16 +11971,33 @@ function formatTaskLine(t) {
|
|
|
11728
11971
|
const plan = t.plan_id ? chalk.magenta(` [plan:${t.plan_id.slice(0, 8)}]`) : "";
|
|
11729
11972
|
return `${chalk.dim(t.id.slice(0, 8))} ${statusFn(t.status.padEnd(11))} ${priorityFn(t.priority.padEnd(8))} ${t.title}${assigned}${lock}${tags}${plan}`;
|
|
11730
11973
|
}
|
|
11731
|
-
var stdoutRetryBuffer, stdoutRetrySignal, TASK_UUID_RE, statusColors, priorityColors;
|
|
11974
|
+
var stdoutRetryBuffer, stdoutRetrySignal, TASK_UUID_RE, TASK_STATUS_FLAG_HINTS, TASK_STATUS_FLAG, TASK_PRIORITY_FLAG, statusColors, priorityColors;
|
|
11732
11975
|
var init_helpers = __esm(() => {
|
|
11733
11976
|
init_cloud_router();
|
|
11734
11977
|
init_database();
|
|
11735
11978
|
init_projects();
|
|
11736
11979
|
init_lock_display();
|
|
11737
11980
|
init_package_version();
|
|
11981
|
+
init_types();
|
|
11738
11982
|
stdoutRetryBuffer = new SharedArrayBuffer(4);
|
|
11739
11983
|
stdoutRetrySignal = new Int32Array(stdoutRetryBuffer);
|
|
11740
11984
|
TASK_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11985
|
+
TASK_STATUS_FLAG_HINTS = {
|
|
11986
|
+
all: "To include every status, use -a/--all instead of a --status value.",
|
|
11987
|
+
any: "To include every status, use -a/--all instead of a --status value.",
|
|
11988
|
+
open: "Unstarted and running work is pending and in_progress (the default when no --status is given)."
|
|
11989
|
+
};
|
|
11990
|
+
TASK_STATUS_FLAG = {
|
|
11991
|
+
name: "--status",
|
|
11992
|
+
vocabulary: TASK_STATUSES,
|
|
11993
|
+
normalize: normalizeStatus,
|
|
11994
|
+
hints: TASK_STATUS_FLAG_HINTS
|
|
11995
|
+
};
|
|
11996
|
+
TASK_PRIORITY_FLAG = {
|
|
11997
|
+
name: "--priority",
|
|
11998
|
+
vocabulary: TASK_PRIORITIES,
|
|
11999
|
+
normalize: normalizePriority
|
|
12000
|
+
};
|
|
11741
12001
|
statusColors = {
|
|
11742
12002
|
pending: chalk.yellow,
|
|
11743
12003
|
in_progress: chalk.blue,
|
|
@@ -12523,7 +12783,7 @@ var init_event_hooks = __esm(() => {
|
|
|
12523
12783
|
// node_modules/.bun/@hasna+events@0.1.11/node_modules/@hasna/events/dist/index.js
|
|
12524
12784
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
12525
12785
|
import { existsSync as existsSync6 } from "fs";
|
|
12526
|
-
import { homedir } from "os";
|
|
12786
|
+
import { homedir as homedir2 } from "os";
|
|
12527
12787
|
import { join as join5 } from "path";
|
|
12528
12788
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
12529
12789
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -12625,7 +12885,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
12625
12885
|
return channel.filters.some((filter) => eventMatchesFilter(event, filter));
|
|
12626
12886
|
}
|
|
12627
12887
|
function getEventsDataDir(override) {
|
|
12628
|
-
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");
|
|
12629
12889
|
}
|
|
12630
12890
|
|
|
12631
12891
|
class JsonEventsStore {
|
|
@@ -15960,11 +16220,13 @@ function updateTask(id, input, db) {
|
|
|
15960
16220
|
}
|
|
15961
16221
|
sets.push("status = ?");
|
|
15962
16222
|
params.push(input.status);
|
|
16223
|
+
if (isTerminalStatus(input.status)) {
|
|
16224
|
+
sets.push("locked_by = NULL");
|
|
16225
|
+
sets.push("locked_at = NULL");
|
|
16226
|
+
}
|
|
15963
16227
|
if (input.status === "completed") {
|
|
15964
16228
|
sets.push("completed_at = ?");
|
|
15965
16229
|
params.push(completionTimestamp);
|
|
15966
|
-
sets.push("locked_by = NULL");
|
|
15967
|
-
sets.push("locked_at = NULL");
|
|
15968
16230
|
} else if (task.status === "completed" && input.completed_at === undefined) {
|
|
15969
16231
|
sets.push("completed_at = NULL");
|
|
15970
16232
|
}
|
|
@@ -16088,6 +16350,7 @@ function updateTask(id, input, db) {
|
|
|
16088
16350
|
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
16089
16351
|
const reopened = input.status !== undefined && input.status !== "completed" && task.status === "completed" && input.completed_at === undefined;
|
|
16090
16352
|
const completedNow = input.status === "completed";
|
|
16353
|
+
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
16091
16354
|
const updatedTask = {
|
|
16092
16355
|
...task,
|
|
16093
16356
|
...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
|
|
@@ -16095,8 +16358,8 @@ function updateTask(id, input, db) {
|
|
|
16095
16358
|
metadata: input.metadata ?? task.metadata,
|
|
16096
16359
|
version: task.version + 1,
|
|
16097
16360
|
updated_at: timestamp2,
|
|
16098
|
-
locked_by:
|
|
16099
|
-
locked_at:
|
|
16361
|
+
locked_by: terminalNow ? null : task.locked_by,
|
|
16362
|
+
locked_at: terminalNow ? null : task.locked_at,
|
|
16100
16363
|
completed_at: completedNow ? completionTimestamp : reopened ? null : input.completed_at !== undefined ? input.completed_at : task.completed_at,
|
|
16101
16364
|
sla_minutes: input.sla_minutes !== undefined ? input.sla_minutes : task.sla_minutes,
|
|
16102
16365
|
actual_minutes: input.actual_minutes ?? task.actual_minutes,
|
|
@@ -19054,6 +19317,49 @@ var init_tasks = __esm(() => {
|
|
|
19054
19317
|
init_calendar();
|
|
19055
19318
|
});
|
|
19056
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
|
+
|
|
19057
19363
|
// src/cli/claim-guard.ts
|
|
19058
19364
|
function resolveClaimIdentity(verb, explicit) {
|
|
19059
19365
|
const resolved = resolveWritableIdentity(explicit);
|
|
@@ -19074,10 +19380,10 @@ function normalizeAgentNameInput(name) {
|
|
|
19074
19380
|
|
|
19075
19381
|
// src/lib/assignee-validation.ts
|
|
19076
19382
|
import { readFileSync as readFileSync4 } from "fs";
|
|
19077
|
-
import { homedir as
|
|
19383
|
+
import { homedir as homedir3 } from "os";
|
|
19078
19384
|
import { join as join8 } from "path";
|
|
19079
19385
|
function defaultSeatRosterPath() {
|
|
19080
|
-
return process.env["TODOS_SEAT_ROSTER_PATH"] || join8(
|
|
19386
|
+
return process.env["TODOS_SEAT_ROSTER_PATH"] || join8(homedir3(), ".hasna", "identities", "hasna-seats.roster.json");
|
|
19081
19387
|
}
|
|
19082
19388
|
function loadSeatSlugs(path = defaultSeatRosterPath()) {
|
|
19083
19389
|
try {
|
|
@@ -20211,14 +20517,30 @@ function resolveProjectIdOrSlug(input) {
|
|
|
20211
20517
|
return row.id;
|
|
20212
20518
|
handleError(new Error(`Project not found: ${input}`));
|
|
20213
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
|
+
}
|
|
20214
20533
|
function parseStatus(value) {
|
|
20215
20534
|
if (!value)
|
|
20216
20535
|
return;
|
|
20217
|
-
|
|
20218
|
-
|
|
20219
|
-
|
|
20220
|
-
|
|
20221
|
-
|
|
20536
|
+
return parseEnumFlagList(value, { ...TASK_STATUS_FLAG, allowList: false })?.[0];
|
|
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;
|
|
20222
20544
|
}
|
|
20223
20545
|
function parseIntOption(value, flag) {
|
|
20224
20546
|
if (value === undefined)
|
|
@@ -20243,10 +20565,7 @@ function resolvePlanId(input) {
|
|
|
20243
20565
|
function parsePriority(value) {
|
|
20244
20566
|
if (!value)
|
|
20245
20567
|
return;
|
|
20246
|
-
|
|
20247
|
-
handleError(new Error("--priority must be one of: low, medium, high, critical"));
|
|
20248
|
-
}
|
|
20249
|
-
return value;
|
|
20568
|
+
return parseEnumFlagList(value, { ...TASK_PRIORITY_FLAG, allowList: false })?.[0];
|
|
20250
20569
|
}
|
|
20251
20570
|
function parseJsonObject3(value, flag) {
|
|
20252
20571
|
if (!value)
|
|
@@ -20360,7 +20679,7 @@ function computeLocalReparent(current, opts) {
|
|
|
20360
20679
|
return patch;
|
|
20361
20680
|
}
|
|
20362
20681
|
function registerTaskCommands(program2) {
|
|
20363
|
-
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) => {
|
|
20364
20683
|
const globalOpts = program2.opts();
|
|
20365
20684
|
opts.tags = opts.tags || opts.tag;
|
|
20366
20685
|
opts.list = opts.list || opts.taskList;
|
|
@@ -20375,8 +20694,9 @@ function registerTaskCommands(program2) {
|
|
|
20375
20694
|
if (cloud) {
|
|
20376
20695
|
let task3;
|
|
20377
20696
|
try {
|
|
20378
|
-
const cloudProjectRef = opts
|
|
20697
|
+
const cloudProjectRef = projectRefFromOpts(opts, globalOpts);
|
|
20379
20698
|
const cloudProjectId = cloudProjectRef ? await cloudResolveProjectRef(cloud, cloudProjectRef) : undefined;
|
|
20699
|
+
warnMissingProject(cloudProjectId, projectOptOut(opts));
|
|
20380
20700
|
const cloudTaskListId = opts.list ? await cloudResolveTaskListRef(cloud, opts.list, cloudProjectId) : undefined;
|
|
20381
20701
|
if (opts.list && !cloudTaskListId) {
|
|
20382
20702
|
throw new Error(`Could not resolve task list ID or slug: ${opts.list}`);
|
|
@@ -20399,6 +20719,7 @@ function registerTaskCommands(program2) {
|
|
|
20399
20719
|
created_by: creator.agent_id || undefined,
|
|
20400
20720
|
session_id: globalOpts.session,
|
|
20401
20721
|
project_id: cloudProjectId,
|
|
20722
|
+
working_dir: process.cwd(),
|
|
20402
20723
|
estimated_minutes: opts.estimated !== undefined ? parseIntOption(opts.estimated, "--estimated") : undefined,
|
|
20403
20724
|
sla_minutes: opts.slaMinutes !== undefined || opts.sla !== undefined ? parseIntOption(opts.slaMinutes ?? opts.sla, "--sla-minutes") : undefined,
|
|
20404
20725
|
requires_approval: opts.approval || undefined,
|
|
@@ -20417,8 +20738,9 @@ function registerTaskCommands(program2) {
|
|
|
20417
20738
|
}
|
|
20418
20739
|
return;
|
|
20419
20740
|
}
|
|
20420
|
-
const explicitProject = opts
|
|
20421
|
-
const projectId = explicitProject ? resolveProjectIdOrSlug(explicitProject) : autoProject(globalOpts);
|
|
20741
|
+
const explicitProject = projectRefFromOpts(opts, globalOpts);
|
|
20742
|
+
const projectId = explicitProject ? resolveProjectIdOrSlug(explicitProject) : projectOptOut(opts) ? undefined : autoProject(globalOpts);
|
|
20743
|
+
warnMissingProject(projectId, projectOptOut(opts));
|
|
20422
20744
|
opts.tags = opts.tags || opts.tag;
|
|
20423
20745
|
opts.list = opts.list || opts.taskList;
|
|
20424
20746
|
const taskListId = opts.list ? (() => {
|
|
@@ -20630,15 +20952,19 @@ function registerTaskCommands(program2) {
|
|
|
20630
20952
|
}
|
|
20631
20953
|
filter["task_list_id"] = listId;
|
|
20632
20954
|
}
|
|
20633
|
-
if (opts.status) {
|
|
20634
|
-
filter["status"] =
|
|
20955
|
+
if (opts.status !== undefined) {
|
|
20956
|
+
filter["status"] = parseEnumFlag(opts.status, TASK_STATUS_FLAG);
|
|
20635
20957
|
} else if (!opts.all) {
|
|
20636
20958
|
filter["status"] = ["pending", "in_progress"];
|
|
20637
20959
|
}
|
|
20638
|
-
if (opts.priority)
|
|
20639
|
-
filter["priority"] = opts.priority;
|
|
20640
|
-
|
|
20641
|
-
|
|
20960
|
+
if (opts.priority !== undefined)
|
|
20961
|
+
filter["priority"] = parseEnumFlag(opts.priority, TASK_PRIORITY_FLAG);
|
|
20962
|
+
let assignedFilter;
|
|
20963
|
+
let assignedWasTyped = false;
|
|
20964
|
+
if (opts.assigned) {
|
|
20965
|
+
assignedFilter = opts.assigned;
|
|
20966
|
+
assignedWasTyped = true;
|
|
20967
|
+
}
|
|
20642
20968
|
if (opts.createdBy)
|
|
20643
20969
|
filter["created_by"] = canonicalAgentRef(opts.createdBy);
|
|
20644
20970
|
if (opts.notCreatedBy)
|
|
@@ -20649,7 +20975,8 @@ function registerTaskCommands(program2) {
|
|
|
20649
20975
|
console.error(chalk3.red("--inbox needs an agent identity. Run `todos init <name>` or pass --agent <id>."));
|
|
20650
20976
|
process.exit(1);
|
|
20651
20977
|
}
|
|
20652
|
-
|
|
20978
|
+
assignedFilter = me.agent_id;
|
|
20979
|
+
assignedWasTyped = false;
|
|
20653
20980
|
filter["not_created_by"] = me.agent_id;
|
|
20654
20981
|
}
|
|
20655
20982
|
if (opts.tags)
|
|
@@ -20665,8 +20992,11 @@ function registerTaskCommands(program2) {
|
|
|
20665
20992
|
}
|
|
20666
20993
|
}
|
|
20667
20994
|
if (opts.agentName) {
|
|
20668
|
-
|
|
20995
|
+
assignedFilter = opts.agentName;
|
|
20996
|
+
assignedWasTyped = true;
|
|
20669
20997
|
}
|
|
20998
|
+
if (assignedFilter !== undefined)
|
|
20999
|
+
filter["assigned_to"] = assignedFilter;
|
|
20670
21000
|
if (opts.recurring)
|
|
20671
21001
|
filter["has_recurrence"] = true;
|
|
20672
21002
|
if (opts.limit !== undefined) {
|
|
@@ -20678,11 +21008,23 @@ function registerTaskCommands(program2) {
|
|
|
20678
21008
|
}
|
|
20679
21009
|
const creatorFilterActive = Boolean(filter["created_by"] || filter["not_created_by"]);
|
|
20680
21010
|
const requestedLimit = filter["limit"];
|
|
20681
|
-
const
|
|
20682
|
-
|
|
20683
|
-
|
|
20684
|
-
|
|
21011
|
+
const reordersAfterQuery = Boolean(opts.sort);
|
|
21012
|
+
const narrowsAfterQuery = Boolean(opts.dueToday) || Boolean(opts.overdue) || creatorFilterActive && cloud;
|
|
21013
|
+
const withholdLimit = requestedLimit !== undefined && (reordersAfterQuery || narrowsAfterQuery);
|
|
21014
|
+
const scanCeiling = cloud && (withholdLimit || requestedLimit === undefined) ? Math.max(requestedLimit ?? 0, listScanLimit()) : undefined;
|
|
21015
|
+
const serverFilter = (() => {
|
|
21016
|
+
const base = withholdLimit ? (() => {
|
|
21017
|
+
const { limit: _dropped, ...rest } = filter;
|
|
21018
|
+
return rest;
|
|
21019
|
+
})() : filter;
|
|
21020
|
+
return scanCeiling === undefined ? base : { ...base, limit: scanCeiling };
|
|
21021
|
+
})();
|
|
20685
21022
|
let tasks = cloud ? await cloudListTasks(cloud, serverFilter) : listTasks(serverFilter);
|
|
21023
|
+
if (scanCeiling !== undefined && tasks.length >= scanCeiling) {
|
|
21024
|
+
console.error(chalk3.yellow(`Warning: this query reached its ${scanCeiling}-row scan limit, so the rows below were
|
|
21025
|
+
` + ` selected from a truncated set and may not be the true result.
|
|
21026
|
+
` + ` Narrow it (--project, --status, --assigned) or raise TODOS_LIST_SCAN_LIMIT.`));
|
|
21027
|
+
}
|
|
20686
21028
|
if (cloud && creatorFilterActive) {
|
|
20687
21029
|
if (tasks.length > 0 && tasks.every((t) => !("created_by" in t))) {
|
|
20688
21030
|
console.error(chalk3.yellow(`Warning: this server does not record task authorship, so the creator filter matched nothing to exclude.
|
|
@@ -20699,8 +21041,6 @@ function registerTaskCommands(program2) {
|
|
|
20699
21041
|
return false;
|
|
20700
21042
|
return true;
|
|
20701
21043
|
});
|
|
20702
|
-
if (requestedLimit !== undefined)
|
|
20703
|
-
tasks = tasks.slice(0, requestedLimit);
|
|
20704
21044
|
}
|
|
20705
21045
|
if (opts.dueToday) {
|
|
20706
21046
|
const todayEnd = new Date;
|
|
@@ -20725,6 +21065,21 @@ function registerTaskCommands(program2) {
|
|
|
20725
21065
|
return 0;
|
|
20726
21066
|
});
|
|
20727
21067
|
}
|
|
21068
|
+
if (withholdLimit && requestedLimit !== undefined)
|
|
21069
|
+
tasks = tasks.slice(0, requestedLimit);
|
|
21070
|
+
if (assignedWasTyped && assignedFilter && tasks.length === 0) {
|
|
21071
|
+
try {
|
|
21072
|
+
const roster = await loadAssigneeContext(() => cloud ? cloudListAgents(cloud) : listAgents(), true);
|
|
21073
|
+
if (!roster.degraded) {
|
|
21074
|
+
const target = canonicalAgentRef(assignedFilter);
|
|
21075
|
+
const known = roster.agents.some((a) => canonicalAgentRef(a.name) === target || canonicalAgentRef(a.id) === target);
|
|
21076
|
+
if (!known) {
|
|
21077
|
+
console.error(chalk3.yellow(`Warning: no agent named '${assignedFilter}' is registered, so this empty result may be a
|
|
21078
|
+
` + ` mistyped name rather than an empty queue. Check with 'todos agents'.`));
|
|
21079
|
+
}
|
|
21080
|
+
}
|
|
21081
|
+
} catch {}
|
|
21082
|
+
}
|
|
20728
21083
|
const fmt = opts.format || (globalOpts.json ? "json" : "table");
|
|
20729
21084
|
const outputTasks = redactBroadTasks(tasks);
|
|
20730
21085
|
if (fmt === "json") {
|
|
@@ -21496,17 +21851,26 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21496
21851
|
handleError(new Error("Task not found."));
|
|
21497
21852
|
}
|
|
21498
21853
|
});
|
|
21499
|
-
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) => {
|
|
21854
|
+
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) => {
|
|
21500
21855
|
const globalOpts = program2.opts();
|
|
21501
21856
|
const results = [];
|
|
21502
21857
|
const cloud = getTodosCloudClient();
|
|
21503
21858
|
const isPlanAction = action === "plan" || action === "move-plan";
|
|
21859
|
+
const isTagAction = action === "tag" || action === "untag";
|
|
21504
21860
|
if (isPlanAction && Boolean(opts.plan) === Boolean(opts.clearPlan)) {
|
|
21505
21861
|
handleError(new Error("Use exactly one of --plan or --clear-plan with bulk plan."));
|
|
21506
21862
|
}
|
|
21507
|
-
const knownActions = new Set(["done", "complete", "start", "delete", "plan", "move-plan"]);
|
|
21863
|
+
const knownActions = new Set(["done", "complete", "start", "delete", "plan", "move-plan", "tag", "untag"]);
|
|
21508
21864
|
if (!knownActions.has(action)) {
|
|
21509
|
-
handleError(new Error(`Unknown action: ${action}. Use: done, start, delete, plan (alias: move-plan)`));
|
|
21865
|
+
handleError(new Error(`Unknown action: ${action}. Use: done, start, delete, plan (alias: move-plan), tag, untag`));
|
|
21866
|
+
}
|
|
21867
|
+
const tagArgument = resolveTagArgument(opts.tag, opts.tags);
|
|
21868
|
+
if (isTagAction && !tagArgument.ok) {
|
|
21869
|
+
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."));
|
|
21870
|
+
}
|
|
21871
|
+
const bulkTags = isTagAction && tagArgument.ok ? parseTagList(tagArgument.raw) : [];
|
|
21872
|
+
if (isTagAction && bulkTags.length === 0) {
|
|
21873
|
+
handleError(new Error(`bulk ${action} needs --tag. Example: todos bulk ${action} <ids...> --tag directive:k_msd4cz8t_ste6f4`));
|
|
21510
21874
|
}
|
|
21511
21875
|
const bulkClaimAgentId = action === "start" ? resolveClaimIdentity("start", globalOpts.agent) : undefined;
|
|
21512
21876
|
if (cloud) {
|
|
@@ -21535,6 +21899,14 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21535
21899
|
await cloudTaskAction(cloud, resolvedId, "start", { agent_id: bulkClaimAgentId });
|
|
21536
21900
|
} else if (action === "delete") {
|
|
21537
21901
|
await cloudDeleteTask(cloud, resolvedId);
|
|
21902
|
+
} else if (isTagAction) {
|
|
21903
|
+
const current = await cloudGetTask(cloud, resolvedId);
|
|
21904
|
+
if (!current)
|
|
21905
|
+
throw new Error(`Task not found: ${rawId}`);
|
|
21906
|
+
const resolution = resolveBulkTags(current.tags, action, bulkTags);
|
|
21907
|
+
if (resolution.changed) {
|
|
21908
|
+
await cloudUpdateTask(cloud, resolvedId, { version: current.version, tags: resolution.tags });
|
|
21909
|
+
}
|
|
21538
21910
|
} else {
|
|
21539
21911
|
const current = await cloudGetTask(cloud, resolvedId);
|
|
21540
21912
|
if (!current)
|
|
@@ -21571,6 +21943,16 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21571
21943
|
} else if (action === "delete") {
|
|
21572
21944
|
deleteTask(resolvedId);
|
|
21573
21945
|
results.push({ id: resolvedId, success: true });
|
|
21946
|
+
} else if (isTagAction) {
|
|
21947
|
+
const current = getTask(resolvedId);
|
|
21948
|
+
if (!current) {
|
|
21949
|
+
throw new Error(`Task not found: ${rawId}`);
|
|
21950
|
+
}
|
|
21951
|
+
const resolution = resolveBulkTags(current.tags, action, bulkTags);
|
|
21952
|
+
if (resolution.changed) {
|
|
21953
|
+
updateTask(resolvedId, { version: current.version, tags: resolution.tags });
|
|
21954
|
+
}
|
|
21955
|
+
results.push({ id: resolvedId, success: true });
|
|
21574
21956
|
} else if (isPlanAction) {
|
|
21575
21957
|
const current = getTask(resolvedId);
|
|
21576
21958
|
if (!current) {
|
|
@@ -21579,7 +21961,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21579
21961
|
updateTask(resolvedId, { version: current.version, plan_id: planId });
|
|
21580
21962
|
results.push({ id: resolvedId, success: true });
|
|
21581
21963
|
} else {
|
|
21582
|
-
handleError(new Error(`Unknown action: ${action}. Use: done, start, delete, plan (alias: move-plan)`));
|
|
21964
|
+
handleError(new Error(`Unknown action: ${action}. Use: done, start, delete, plan (alias: move-plan), tag, untag`));
|
|
21583
21965
|
}
|
|
21584
21966
|
} catch (e) {
|
|
21585
21967
|
results.push({ id: rawId, success: false, error: e instanceof Error ? e.message : String(e) });
|
|
@@ -21597,6 +21979,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
|
|
|
21597
21979
|
}
|
|
21598
21980
|
});
|
|
21599
21981
|
}
|
|
21982
|
+
var DEFAULT_LIST_SCAN_LIMIT = 1e4;
|
|
21600
21983
|
var init_task_commands = __esm(() => {
|
|
21601
21984
|
init_database();
|
|
21602
21985
|
init_projects();
|
|
@@ -21607,9 +21990,10 @@ var init_task_commands = __esm(() => {
|
|
|
21607
21990
|
init_lock_display();
|
|
21608
21991
|
init_claim_guard();
|
|
21609
21992
|
init_assignee_guard();
|
|
21993
|
+
init_assignee_context();
|
|
21994
|
+
init_agents();
|
|
21610
21995
|
init_helpers();
|
|
21611
21996
|
init_output_redaction();
|
|
21612
|
-
init_types();
|
|
21613
21997
|
});
|
|
21614
21998
|
|
|
21615
21999
|
// src/lib/plan-artifacts.ts
|
|
@@ -23441,7 +23825,8 @@ __export(exports_saved_search_views, {
|
|
|
23441
23825
|
normalizeScope: () => normalizeScope,
|
|
23442
23826
|
listSearchViews: () => listSearchViews,
|
|
23443
23827
|
getSearchView: () => getSearchView,
|
|
23444
|
-
deleteSearchView: () => deleteSearchView
|
|
23828
|
+
deleteSearchView: () => deleteSearchView,
|
|
23829
|
+
SAVED_SEARCH_SCOPES: () => SAVED_SEARCH_SCOPES
|
|
23445
23830
|
});
|
|
23446
23831
|
function parseFilters(value) {
|
|
23447
23832
|
if (!value)
|
|
@@ -23461,10 +23846,8 @@ function rowToSavedSearchView(row) {
|
|
|
23461
23846
|
};
|
|
23462
23847
|
}
|
|
23463
23848
|
function normalizeScope(scope) {
|
|
23464
|
-
|
|
23465
|
-
|
|
23466
|
-
}
|
|
23467
|
-
return "tasks";
|
|
23849
|
+
const candidate = (scope ?? "").trim().toLowerCase();
|
|
23850
|
+
return SAVED_SEARCH_SCOPES.includes(candidate) ? candidate : "tasks";
|
|
23468
23851
|
}
|
|
23469
23852
|
function normalizeName(name) {
|
|
23470
23853
|
const normalized = name.trim();
|
|
@@ -23767,10 +24150,12 @@ function runSearchView(idOrName, db) {
|
|
|
23767
24150
|
throw new Error(`Saved search view not found: ${idOrName}`);
|
|
23768
24151
|
return { ...runSavedSearch(view.filters, view.scope, d), view };
|
|
23769
24152
|
}
|
|
24153
|
+
var SAVED_SEARCH_SCOPES;
|
|
23770
24154
|
var init_saved_search_views = __esm(() => {
|
|
23771
24155
|
init_database();
|
|
23772
24156
|
init_local_fields();
|
|
23773
24157
|
init_search();
|
|
24158
|
+
SAVED_SEARCH_SCOPES = ["all", "tasks", "projects", "plans", "runs", "comments"];
|
|
23774
24159
|
});
|
|
23775
24160
|
|
|
23776
24161
|
// src/lib/claude-tasks.ts
|
|
@@ -26340,12 +26725,11 @@ function buildSearchFilters(query, opts, projectId) {
|
|
|
26340
26725
|
const customFields = parseJsonObjectOption(opts.fieldCustom, "--field-custom");
|
|
26341
26726
|
const labels = splitList(opts.fieldLabel);
|
|
26342
26727
|
const tags = splitList(opts.tag);
|
|
26343
|
-
const statuses = splitList(opts.status)?.map(normalizeStatus);
|
|
26344
26728
|
const filters = {
|
|
26345
26729
|
query: query || opts.query,
|
|
26346
26730
|
project_id: opts.allProjects ? undefined : projectId,
|
|
26347
|
-
status:
|
|
26348
|
-
priority:
|
|
26731
|
+
status: parseEnumFlagList(opts.status, TASK_STATUS_FLAG),
|
|
26732
|
+
priority: parseEnumFlagList(opts.priority, TASK_PRIORITY_FLAG),
|
|
26349
26733
|
assigned_to: opts.assigned,
|
|
26350
26734
|
agent_id: opts.agentId,
|
|
26351
26735
|
task_list_id: filterPatch?.task_list_id === undefined ? resolveTaskListFilter(opts.taskList, projectId) : undefined,
|
|
@@ -26450,7 +26834,7 @@ function registerProjectCommands(program2) {
|
|
|
26450
26834
|
try {
|
|
26451
26835
|
const cloud = getTodosCloudClient();
|
|
26452
26836
|
const projectId = opts.allProjects ? undefined : cloud ? undefined : autoProject(globalOpts);
|
|
26453
|
-
const scope =
|
|
26837
|
+
const scope = parseEnumFlag(opts.scope, SEARCH_SCOPE_FLAG) ?? "tasks";
|
|
26454
26838
|
const searchOpts = buildSearchFilters(query, opts, projectId);
|
|
26455
26839
|
if (opts.saveAs) {
|
|
26456
26840
|
const view = saveSearchView({
|
|
@@ -26542,7 +26926,7 @@ function registerProjectCommands(program2) {
|
|
|
26542
26926
|
const view = saveSearchView({
|
|
26543
26927
|
name,
|
|
26544
26928
|
description: opts.description,
|
|
26545
|
-
scope:
|
|
26929
|
+
scope: parseEnumFlag(opts.scope, SEARCH_SCOPE_FLAG) ?? "tasks",
|
|
26546
26930
|
filters: buildSearchFilters(opts.query, opts, projectId)
|
|
26547
26931
|
});
|
|
26548
26932
|
output(view, Boolean(globalOpts.json));
|
|
@@ -26555,7 +26939,7 @@ function registerProjectCommands(program2) {
|
|
|
26555
26939
|
views.command("list").description("List local saved search views").option("--scope <scope>", "Filter by scope").action((opts) => {
|
|
26556
26940
|
const globalOpts = program2.opts();
|
|
26557
26941
|
try {
|
|
26558
|
-
const rows = listSearchViews(
|
|
26942
|
+
const rows = listSearchViews(parseEnumFlag(opts.scope, SEARCH_SCOPE_FLAG));
|
|
26559
26943
|
output(rows, Boolean(globalOpts.json));
|
|
26560
26944
|
if (!globalOpts.json) {
|
|
26561
26945
|
if (rows.length === 0) {
|
|
@@ -26751,7 +27135,7 @@ function registerProjectCommands(program2) {
|
|
|
26751
27135
|
const cloud = getTodosCloudClient();
|
|
26752
27136
|
if (opts.show) {
|
|
26753
27137
|
const project = cloud ? await cloudResolveProject(cloud, opts.show) : resolveExplicitProject(opts.show);
|
|
26754
|
-
|
|
27138
|
+
outputRecord(project, Boolean(globalOpts.json), "Project:");
|
|
26755
27139
|
return;
|
|
26756
27140
|
}
|
|
26757
27141
|
if (opts.update) {
|
|
@@ -26765,7 +27149,7 @@ function registerProjectCommands(program2) {
|
|
|
26765
27149
|
}
|
|
26766
27150
|
const current = cloud ? await cloudResolveProject(cloud, opts.update) : resolveExplicitProject(opts.update);
|
|
26767
27151
|
const project = cloud ? await cloudUpdateProject(cloud, current.id, patch) : updateProject(current.id, patch);
|
|
26768
|
-
|
|
27152
|
+
outputRecord(project, Boolean(globalOpts.json), "Project updated:");
|
|
26769
27153
|
return;
|
|
26770
27154
|
}
|
|
26771
27155
|
if (opts.deregister) {
|
|
@@ -27073,6 +27457,7 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
|
|
|
27073
27457
|
program2.command("export").description("Export tasks").option("-f, --format <format>", "Format: json, md, todos.md, or bridge", "json").option("-o, --output <path>", "Write export output to a file").option("--encrypt", "Encrypt bridge exports with a local encryption profile").option("--encryption-profile <name>", "Encryption profile name", "default").option("--allow-plaintext-sensitive", "Deprecated; bridge exports are redacted unless --encrypt is used").action(async (opts) => {
|
|
27074
27458
|
const { listTasks: listTasks2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
|
|
27075
27459
|
const globalOpts = program2.opts();
|
|
27460
|
+
opts.format = parseEnumFlag(opts.format, EXPORT_FORMAT_FLAG) ?? "json";
|
|
27076
27461
|
const projectId = autoProject(globalOpts);
|
|
27077
27462
|
const writeOutput = async (content) => {
|
|
27078
27463
|
if (opts.output) {
|
|
@@ -27234,7 +27619,7 @@ function resolveTaskListId(partialId) {
|
|
|
27234
27619
|
}
|
|
27235
27620
|
return id;
|
|
27236
27621
|
}
|
|
27237
|
-
var CLOUD_GRAPH_EDGE_CONCURRENCY = 6;
|
|
27622
|
+
var SEARCH_SCOPE_FLAG, EXPORT_FORMATS, EXPORT_FORMAT_FLAG, CLOUD_GRAPH_EDGE_CONCURRENCY = 6;
|
|
27238
27623
|
var init_project_commands = __esm(() => {
|
|
27239
27624
|
init_database();
|
|
27240
27625
|
init_projects();
|
|
@@ -27246,12 +27631,26 @@ var init_project_commands = __esm(() => {
|
|
|
27246
27631
|
init_config();
|
|
27247
27632
|
init_helpers();
|
|
27248
27633
|
init_output_redaction();
|
|
27634
|
+
SEARCH_SCOPE_FLAG = {
|
|
27635
|
+
name: "--scope",
|
|
27636
|
+
vocabulary: SAVED_SEARCH_SCOPES,
|
|
27637
|
+
normalize: (value) => value.toLowerCase().trim(),
|
|
27638
|
+
allowList: false
|
|
27639
|
+
};
|
|
27640
|
+
EXPORT_FORMATS = ["json", "md", "markdown", "todos.md", "todos-md", "bridge"];
|
|
27641
|
+
EXPORT_FORMAT_FLAG = {
|
|
27642
|
+
name: "--format",
|
|
27643
|
+
vocabulary: EXPORT_FORMATS,
|
|
27644
|
+
normalize: (value) => value.toLowerCase().trim(),
|
|
27645
|
+
allowList: false
|
|
27646
|
+
};
|
|
27249
27647
|
});
|
|
27250
27648
|
|
|
27251
27649
|
// src/cli/commands/agent-commands.ts
|
|
27252
27650
|
var exports_agent_commands = {};
|
|
27253
27651
|
__export(exports_agent_commands, {
|
|
27254
|
-
registerAgentCommands: () => registerAgentCommands
|
|
27652
|
+
registerAgentCommands: () => registerAgentCommands,
|
|
27653
|
+
findCaseVariantRows: () => findCaseVariantRows
|
|
27255
27654
|
});
|
|
27256
27655
|
import chalk6 from "chalk";
|
|
27257
27656
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -27265,6 +27664,15 @@ function resolveCloudAgentByNameOrId(agents, nameOrId) {
|
|
|
27265
27664
|
return null;
|
|
27266
27665
|
return matches.reduce((freshest, candidate) => new Date(candidate.last_seen_at).getTime() > new Date(freshest.last_seen_at).getTime() ? candidate : freshest);
|
|
27267
27666
|
}
|
|
27667
|
+
function findCaseVariantRows(agents, requested) {
|
|
27668
|
+
const raw = requested.trim();
|
|
27669
|
+
const target = normalizeAgentNameInput(raw);
|
|
27670
|
+
if (!target)
|
|
27671
|
+
return [];
|
|
27672
|
+
if (agents.some((agent) => agent.name === raw))
|
|
27673
|
+
return [];
|
|
27674
|
+
return agents.filter((agent) => normalizeAgentNameInput(agent.name) === target);
|
|
27675
|
+
}
|
|
27268
27676
|
function clearIdentityIfMine(agentId, agentName) {
|
|
27269
27677
|
const persisted = readPersistedIdentity();
|
|
27270
27678
|
if (!persisted)
|
|
@@ -27278,7 +27686,22 @@ function registerAgentCommands(program2) {
|
|
|
27278
27686
|
const globalOpts = program2.opts();
|
|
27279
27687
|
try {
|
|
27280
27688
|
const cloud = getTodosCloudClient();
|
|
27281
|
-
const
|
|
27689
|
+
const registrationName = name.trim();
|
|
27690
|
+
if (cloud) {
|
|
27691
|
+
let roster = null;
|
|
27692
|
+
try {
|
|
27693
|
+
roster = await cloudListAgents(cloud);
|
|
27694
|
+
} catch {
|
|
27695
|
+
console.error(chalk6.yellow("Warning: could not read the shared roster, so the case-variant check was skipped for this registration."));
|
|
27696
|
+
}
|
|
27697
|
+
const variants = roster ? findCaseVariantRows(roster, registrationName) : [];
|
|
27698
|
+
if (variants.length > 0) {
|
|
27699
|
+
const listed = variants.map((a) => `${a.name} (${a.id})`).join(", ");
|
|
27700
|
+
console.error(chalk6.red(`'${name}' is a case variant of an agent that already exists: ${listed}. ` + `Agent names are ONE case-insensitive identity, so registering this spelling would create a second row ` + `and split the identity in two \u2014 tasks assigned to one spelling are invisible to the other. ` + `Register with the existing spelling instead.`));
|
|
27701
|
+
process.exit(1);
|
|
27702
|
+
}
|
|
27703
|
+
}
|
|
27704
|
+
const result = cloud ? await cloudRegisterAgent(cloud, { name: registrationName, description: opts.description }) : (await Promise.resolve().then(() => (init_agents(), exports_agents))).registerAgent({ name: registrationName, description: opts.description });
|
|
27282
27705
|
const { isAgentConflict: isAgentConflict2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
27283
27706
|
if (isAgentConflict2(result)) {
|
|
27284
27707
|
console.error(chalk6.red("CONFLICT:"), result.message);
|
|
@@ -27623,7 +28046,7 @@ ${isOnline ? chalk6.green("\u25CF") : chalk6.dim("\u25CB")} ${chalk6.bold(agent.
|
|
|
27623
28046
|
const list2 = cloud ? await cloudGetTaskList(cloud, resolved) : getTaskList(resolved);
|
|
27624
28047
|
if (!list2)
|
|
27625
28048
|
throw new Error(`Task list not found: ${ref}`);
|
|
27626
|
-
|
|
28049
|
+
outputRecord(list2, Boolean(globalOpts.json), "Task list:");
|
|
27627
28050
|
return;
|
|
27628
28051
|
}
|
|
27629
28052
|
const patch = {
|
|
@@ -27634,7 +28057,7 @@ ${isOnline ? chalk6.green("\u25CF") : chalk6.dim("\u25CB")} ${chalk6.bold(agent.
|
|
|
27634
28057
|
if (Object.keys(patch).length === 0)
|
|
27635
28058
|
throw new Error("lists --update requires --name, --slug, or --description");
|
|
27636
28059
|
const list = cloud ? await cloudUpdateTaskList(cloud, resolved, patch) : updateTaskList(resolved, patch);
|
|
27637
|
-
|
|
28060
|
+
outputRecord(list, Boolean(globalOpts.json), "Task list updated:");
|
|
27638
28061
|
return;
|
|
27639
28062
|
}
|
|
27640
28063
|
if (opts.delete) {
|
|
@@ -32185,9 +32608,11 @@ async function updateTask2(id, input, store) {
|
|
|
32185
32608
|
throw new Error(`Task ${id} version conflict: expected ${existing.version}, got ${input.version}`);
|
|
32186
32609
|
}
|
|
32187
32610
|
const reopened = existing.status === "completed" && input.status !== undefined && input.status !== "completed" && input.completed_at === undefined;
|
|
32611
|
+
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
32188
32612
|
const task = {
|
|
32189
32613
|
...existing,
|
|
32190
32614
|
...definedPatch(input),
|
|
32615
|
+
...terminalNow ? { locked_by: null, locked_at: null } : {},
|
|
32191
32616
|
version: existing.version + 1,
|
|
32192
32617
|
updated_at: new Date().toISOString(),
|
|
32193
32618
|
tags: input.tags ?? existing.tags,
|
|
@@ -34528,8 +34953,19 @@ function handleStats(_ctx, json2) {
|
|
|
34528
34953
|
recurring_tasks: countRecurringTasks()
|
|
34529
34954
|
});
|
|
34530
34955
|
}
|
|
34956
|
+
function taskStatusQueryParam(url) {
|
|
34957
|
+
const raw = url.searchParams.get("status");
|
|
34958
|
+
if (!raw)
|
|
34959
|
+
return { ok: true, value: undefined };
|
|
34960
|
+
const result = resolveEnumVocabulary(raw, { name: "status", vocabulary: TASK_STATUSES });
|
|
34961
|
+
if (!result.ok)
|
|
34962
|
+
return { ok: false, message: result.message };
|
|
34963
|
+
return { ok: true, value: collapseEnumValues(result.values) };
|
|
34964
|
+
}
|
|
34531
34965
|
async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
|
|
34532
|
-
const
|
|
34966
|
+
const statusParam = taskStatusQueryParam(url);
|
|
34967
|
+
if (!statusParam.ok)
|
|
34968
|
+
return json2({ error: statusParam.message }, 400);
|
|
34533
34969
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
34534
34970
|
const sessionId = url.searchParams.get("session_id") || undefined;
|
|
34535
34971
|
const agentId = url.searchParams.get("agent_id") || undefined;
|
|
@@ -34537,7 +34973,7 @@ async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
|
|
|
34537
34973
|
const offsetParam = url.searchParams.get("offset");
|
|
34538
34974
|
const fields = parseFieldsParam(url);
|
|
34539
34975
|
const tasks = listTasks({
|
|
34540
|
-
status,
|
|
34976
|
+
status: statusParam.value,
|
|
34541
34977
|
project_id: projectId,
|
|
34542
34978
|
session_id: sessionId,
|
|
34543
34979
|
agent_id: agentId,
|
|
@@ -34602,9 +35038,19 @@ async function handleUpsertTask(req, ctx, json2, taskToSummary2) {
|
|
|
34602
35038
|
}
|
|
34603
35039
|
function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
|
|
34604
35040
|
const format = url.searchParams.get("format") || "json";
|
|
34605
|
-
const
|
|
35041
|
+
const statusParam = taskStatusQueryParam(url);
|
|
35042
|
+
if (!statusParam.ok) {
|
|
35043
|
+
return new Response(JSON.stringify({ error: statusParam.message }), {
|
|
35044
|
+
status: 400,
|
|
35045
|
+
headers: { "Content-Type": "application/json" }
|
|
35046
|
+
});
|
|
35047
|
+
}
|
|
34606
35048
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
34607
|
-
const tasks = listTasks({
|
|
35049
|
+
const tasks = listTasks({
|
|
35050
|
+
status: statusParam.value,
|
|
35051
|
+
project_id: projectId,
|
|
35052
|
+
limit: 1e4
|
|
35053
|
+
});
|
|
34608
35054
|
const summaries = tasks.map((t) => taskToSummary2(t));
|
|
34609
35055
|
if (format === "csv") {
|
|
34610
35056
|
const headers = ["id", "short_id", "title", "status", "priority", "project_id", "assigned_to", "agent_id", "created_at", "updated_at", "completed_at", "due_at"];
|
|
@@ -35186,6 +35632,7 @@ var init_routes = __esm(() => {
|
|
|
35186
35632
|
init_tasks();
|
|
35187
35633
|
init_database();
|
|
35188
35634
|
init_types();
|
|
35635
|
+
init_types();
|
|
35189
35636
|
init_projects();
|
|
35190
35637
|
init_agents();
|
|
35191
35638
|
init_plans();
|
|
@@ -37070,8 +37517,38 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
37070
37517
|
operationId: "listTasks",
|
|
37071
37518
|
summary: "List tasks",
|
|
37072
37519
|
parameters: [
|
|
37073
|
-
{
|
|
37074
|
-
|
|
37520
|
+
{
|
|
37521
|
+
name: "status",
|
|
37522
|
+
in: "query",
|
|
37523
|
+
description: `Task status, or a comma-separated list of statuses. Allowed values: ${TASK_STATUSES.join(", ")}.`,
|
|
37524
|
+
style: "form",
|
|
37525
|
+
explode: false,
|
|
37526
|
+
schema: {
|
|
37527
|
+
oneOf: [
|
|
37528
|
+
{ type: "string", enum: [...TASK_STATUSES] },
|
|
37529
|
+
{
|
|
37530
|
+
type: "array",
|
|
37531
|
+
items: { type: "string", enum: [...TASK_STATUSES] }
|
|
37532
|
+
}
|
|
37533
|
+
]
|
|
37534
|
+
}
|
|
37535
|
+
},
|
|
37536
|
+
{
|
|
37537
|
+
name: "priority",
|
|
37538
|
+
in: "query",
|
|
37539
|
+
description: `Task priority, or a comma-separated list of priorities. Allowed values: ${TASK_PRIORITIES.join(", ")}.`,
|
|
37540
|
+
style: "form",
|
|
37541
|
+
explode: false,
|
|
37542
|
+
schema: {
|
|
37543
|
+
oneOf: [
|
|
37544
|
+
{ type: "string", enum: [...TASK_PRIORITIES] },
|
|
37545
|
+
{
|
|
37546
|
+
type: "array",
|
|
37547
|
+
items: { type: "string", enum: [...TASK_PRIORITIES] }
|
|
37548
|
+
}
|
|
37549
|
+
]
|
|
37550
|
+
}
|
|
37551
|
+
},
|
|
37075
37552
|
{ name: "project_id", in: "query", schema: { type: "string" } },
|
|
37076
37553
|
{ name: "parent_id", in: "query", schema: { type: "string", nullable: true } },
|
|
37077
37554
|
{ name: "include_subtasks", in: "query", schema: { type: "boolean" } },
|
|
@@ -37583,6 +38060,7 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
37583
38060
|
var taskSchema, projectSchema, taskListSchema, taskCommentSchema, planSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
|
|
37584
38061
|
var init_openapi = __esm(() => {
|
|
37585
38062
|
init_package_version();
|
|
38063
|
+
init_types();
|
|
37586
38064
|
taskSchema = {
|
|
37587
38065
|
type: "object",
|
|
37588
38066
|
properties: {
|
|
@@ -37850,6 +38328,15 @@ function json3(body, status = 200) {
|
|
|
37850
38328
|
function error(status, message, extra) {
|
|
37851
38329
|
return json3({ error: message, ...extra ?? {} }, status);
|
|
37852
38330
|
}
|
|
38331
|
+
function enumQueryParam(url, name, vocabulary) {
|
|
38332
|
+
const raw = url.searchParams.get(name);
|
|
38333
|
+
if (raw === null || raw === "")
|
|
38334
|
+
return { ok: true, value: undefined };
|
|
38335
|
+
const result = resolveEnumVocabulary(raw, { name, vocabulary });
|
|
38336
|
+
if (!result.ok)
|
|
38337
|
+
return { ok: false, response: error(400, result.message) };
|
|
38338
|
+
return { ok: true, value: collapseEnumValues(result.values) };
|
|
38339
|
+
}
|
|
37853
38340
|
function validateTaskCompletion(value) {
|
|
37854
38341
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
37855
38342
|
return { ok: false, message: "completion body must be an object" };
|
|
@@ -38270,14 +38757,16 @@ async function handleV1Request(req, url, dependencies = {}) {
|
|
|
38270
38757
|
return error(400, "include_subtasks must be true or false");
|
|
38271
38758
|
}
|
|
38272
38759
|
const hasParentFilter = url.searchParams.has("parent_id");
|
|
38760
|
+
const statusParam = enumQueryParam(url, "status", TASK_STATUSES);
|
|
38761
|
+
if (!statusParam.ok)
|
|
38762
|
+
return statusParam.response;
|
|
38763
|
+
const priorityParam = enumQueryParam(url, "priority", TASK_PRIORITIES);
|
|
38764
|
+
if (!priorityParam.ok)
|
|
38765
|
+
return priorityParam.response;
|
|
38273
38766
|
const filter = {
|
|
38274
38767
|
...url.searchParams.get("q") ? { query: url.searchParams.get("q") } : {},
|
|
38275
|
-
...
|
|
38276
|
-
|
|
38277
|
-
} : {},
|
|
38278
|
-
...url.searchParams.get("priority") ? {
|
|
38279
|
-
priority: url.searchParams.get("priority").includes(",") ? url.searchParams.get("priority").split(",") : url.searchParams.get("priority")
|
|
38280
|
-
} : {},
|
|
38768
|
+
...statusParam.value !== undefined ? { status: statusParam.value } : {},
|
|
38769
|
+
...priorityParam.value !== undefined ? { priority: priorityParam.value } : {},
|
|
38281
38770
|
...url.searchParams.get("project_id") ? { project_id: url.searchParams.get("project_id") } : {},
|
|
38282
38771
|
...hasParentFilter ? { parent_id: url.searchParams.get("parent_id") || null, include_subtasks: true } : includeSubtasks !== null ? { include_subtasks: includeSubtasks === "true" } : {},
|
|
38283
38772
|
...url.searchParams.get("plan_id") ? { plan_id: url.searchParams.get("plan_id") } : {},
|
|
@@ -38911,7 +39400,13 @@ async function handleV1Request(req, url, dependencies = {}) {
|
|
|
38911
39400
|
return error(405, `method ${method} not allowed on /v1/refs/:ref`);
|
|
38912
39401
|
if (!store.gitRefs)
|
|
38913
39402
|
return error(501, "git ref links are not supported by this storage backend");
|
|
38914
|
-
|
|
39403
|
+
let decodedRef;
|
|
39404
|
+
try {
|
|
39405
|
+
decodedRef = decodeURIComponent(id);
|
|
39406
|
+
} catch {
|
|
39407
|
+
return error(400, "ref path segment has invalid percent encoding");
|
|
39408
|
+
}
|
|
39409
|
+
const refs = await store.gitRefs.find(decodedRef);
|
|
38915
39410
|
return json3({ refs, count: refs.length });
|
|
38916
39411
|
}
|
|
38917
39412
|
if (resource === "next" && !id) {
|
|
@@ -43509,7 +44004,7 @@ async function validateMcpAssignee(value, allowSeat) {
|
|
|
43509
44004
|
return verdict.assignee;
|
|
43510
44005
|
}
|
|
43511
44006
|
function registerTaskCrudTools(server, ctx) {
|
|
43512
|
-
const { shouldRegisterTool, resolveId, formatError, formatTask } = ctx;
|
|
44007
|
+
const { shouldRegisterTool, resolveId, formatError, formatTask, applyFocus } = ctx;
|
|
43513
44008
|
function mutationTaskResponse(task) {
|
|
43514
44009
|
const compact = compactTask(task, 240);
|
|
43515
44010
|
compact["version"] = task.version;
|
|
@@ -43585,6 +44080,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
43585
44080
|
payload.max_retries = retry_count;
|
|
43586
44081
|
if (deadline)
|
|
43587
44082
|
payload.due_at = deadline;
|
|
44083
|
+
applyFocus(payload, router.agent_id || undefined);
|
|
43588
44084
|
const created = await cloudCreateTask(cloud, payload);
|
|
43589
44085
|
return { content: [{ type: "text", text: mutationTaskResponse(created) }] };
|
|
43590
44086
|
}
|
|
@@ -43611,6 +44107,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
43611
44107
|
resolved.max_retries = retry_count;
|
|
43612
44108
|
if (deadline)
|
|
43613
44109
|
resolved.due_at = deadline;
|
|
44110
|
+
applyFocus(resolved, router.agent_id || undefined);
|
|
43614
44111
|
const task = createTask(resolved);
|
|
43615
44112
|
return { content: [{ type: "text", text: mutationTaskResponse(task) }] };
|
|
43616
44113
|
} catch (e) {
|
|
@@ -50421,10 +50918,23 @@ function registerTaskAutoTools(server, ctx) {
|
|
|
50421
50918
|
return { content: [{ type: "text", text: "No active agents available for rebalancing." }] };
|
|
50422
50919
|
const activeTasks = listTasks4({ project_id: resolvedProjectId, status: ["pending", "in_progress"], limit: 1000 }, undefined);
|
|
50423
50920
|
const agentKeyByAlias = new Map;
|
|
50921
|
+
const ambiguousAgentAliases = new Set;
|
|
50922
|
+
const indexAgentAlias = (alias, agentId) => {
|
|
50923
|
+
const normalizedAlias2 = alias.toLowerCase();
|
|
50924
|
+
if (ambiguousAgentAliases.has(normalizedAlias2))
|
|
50925
|
+
return;
|
|
50926
|
+
const existingAgentId = agentKeyByAlias.get(normalizedAlias2);
|
|
50927
|
+
if (existingAgentId !== undefined && existingAgentId !== agentId) {
|
|
50928
|
+
agentKeyByAlias.delete(normalizedAlias2);
|
|
50929
|
+
ambiguousAgentAliases.add(normalizedAlias2);
|
|
50930
|
+
return;
|
|
50931
|
+
}
|
|
50932
|
+
agentKeyByAlias.set(normalizedAlias2, agentId);
|
|
50933
|
+
};
|
|
50424
50934
|
for (const agent of agents) {
|
|
50425
|
-
|
|
50935
|
+
indexAgentAlias(String(agent.id), agent.id);
|
|
50426
50936
|
if (agent.name)
|
|
50427
|
-
|
|
50937
|
+
indexAgentAlias(String(agent.name), agent.id);
|
|
50428
50938
|
}
|
|
50429
50939
|
const canonicalAgentKey = (assignedTo) => assignedTo ? agentKeyByAlias.get(assignedTo.toLowerCase()) : undefined;
|
|
50430
50940
|
const load = new Map(agents.map((agent) => [agent.id, activeTasks.filter((t) => canonicalAgentKey(t.assigned_to) === agent.id).length]));
|
|
@@ -61810,9 +62320,16 @@ ${text2}` }] };
|
|
|
61810
62320
|
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
61811
62321
|
}
|
|
61812
62322
|
const oldName = agent.name;
|
|
61813
|
-
const updated = updateAgent(agent.id, { name: new_name });
|
|
61814
62323
|
const db = getDatabase();
|
|
61815
|
-
|
|
62324
|
+
let oldNameUniquelyIdentifiesAgent = false;
|
|
62325
|
+
try {
|
|
62326
|
+
oldNameUniquelyIdentifiesAgent = resolvePartialId(db, "agents", oldName) === agent.id;
|
|
62327
|
+
} catch (error2) {
|
|
62328
|
+
if (!(error2 instanceof IdentityAliasAmbiguousError))
|
|
62329
|
+
throw error2;
|
|
62330
|
+
}
|
|
62331
|
+
const updated = updateAgent(agent.id, { name: new_name });
|
|
62332
|
+
const tasksResult = db.run(oldNameUniquelyIdentifiesAgent ? "UPDATE tasks SET assigned_to = ? WHERE LOWER(assigned_to) = LOWER(?)" : "UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
|
|
61816
62333
|
const taskNote = tasksResult.changes > 0 ? `
|
|
61817
62334
|
Updated assigned_to on ${tasksResult.changes} task(s).` : "";
|
|
61818
62335
|
return {
|
|
@@ -61996,6 +62513,7 @@ var init_agents2 = __esm(() => {
|
|
|
61996
62513
|
init_agents();
|
|
61997
62514
|
init_config();
|
|
61998
62515
|
init_database();
|
|
62516
|
+
init_types();
|
|
61999
62517
|
init_cloud_router();
|
|
62000
62518
|
});
|
|
62001
62519
|
|
|
@@ -62828,7 +63346,8 @@ function buildServer() {
|
|
|
62828
63346
|
formatError,
|
|
62829
63347
|
formatTask,
|
|
62830
63348
|
formatTaskDetail,
|
|
62831
|
-
getAgentFocus
|
|
63349
|
+
getAgentFocus,
|
|
63350
|
+
applyFocus
|
|
62832
63351
|
};
|
|
62833
63352
|
registerTaskCrudTools(server, toolContext);
|
|
62834
63353
|
registerTaskProjectTools(server, toolContext);
|
|
@@ -66275,7 +66794,7 @@ ${error2.message}
|
|
|
66275
66794
|
const globalOpts = program2.opts();
|
|
66276
66795
|
const projectId = autoProject(globalOpts);
|
|
66277
66796
|
const interval = parseInt(opts.interval, 10) * 1000;
|
|
66278
|
-
const statusFilter = opts.status
|
|
66797
|
+
const statusFilter = parseEnumFlagList(opts.status, TASK_STATUS_FLAG) ?? ["pending", "in_progress"];
|
|
66279
66798
|
function render2() {
|
|
66280
66799
|
const tasks = listTasks({ project_id: projectId, status: statusFilter });
|
|
66281
66800
|
const all = listTasks({ project_id: projectId });
|
|
@@ -68882,8 +69401,8 @@ Findings`));
|
|
|
68882
69401
|
const row = db.query("SELECT COUNT(*) as count FROM tasks").get();
|
|
68883
69402
|
const { statSync: statSync12 } = await import("fs");
|
|
68884
69403
|
const { join: join27 } = await import("path");
|
|
68885
|
-
const
|
|
68886
|
-
const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join27(
|
|
69404
|
+
const { getHomeDir: getHomeDir2 } = await Promise.resolve().then(() => (init_sync_utils(), exports_sync_utils));
|
|
69405
|
+
const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join27(getHomeDir2(), ".hasna", "todos", "todos.db");
|
|
68887
69406
|
let size = "unknown";
|
|
68888
69407
|
try {
|
|
68889
69408
|
size = `${(statSync12(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
|
|
@@ -72048,7 +72567,8 @@ var init_mcp_hooks_commands = __esm(() => {
|
|
|
72048
72567
|
init_tasks();
|
|
72049
72568
|
init_helpers();
|
|
72050
72569
|
init_cloud_router();
|
|
72051
|
-
|
|
72570
|
+
init_sync_utils();
|
|
72571
|
+
HOME2 = getHomeDir();
|
|
72052
72572
|
});
|
|
72053
72573
|
|
|
72054
72574
|
// src/cli/commands/dispatch.tsx
|
|
@@ -72161,7 +72681,11 @@ function registerDispatchCommands(program2) {
|
|
|
72161
72681
|
process.exit(1);
|
|
72162
72682
|
}
|
|
72163
72683
|
}
|
|
72164
|
-
const statuses = opts.status
|
|
72684
|
+
const statuses = parseEnumFlagList(opts.status, {
|
|
72685
|
+
name: "--status",
|
|
72686
|
+
vocabulary: DISPATCH_STATUSES,
|
|
72687
|
+
normalize: (value) => value.toLowerCase().trim()
|
|
72688
|
+
});
|
|
72165
72689
|
const dispatches = listDispatches({ status: statuses, limit: opts.limit ?? 20 });
|
|
72166
72690
|
if (useJson) {
|
|
72167
72691
|
console.log(JSON.stringify(dispatches, null, 2));
|
|
@@ -72194,6 +72718,8 @@ var init_dispatch3 = __esm(() => {
|
|
|
72194
72718
|
init_dispatch();
|
|
72195
72719
|
init_dispatch_formatter();
|
|
72196
72720
|
init_tmux();
|
|
72721
|
+
init_types();
|
|
72722
|
+
init_helpers();
|
|
72197
72723
|
});
|
|
72198
72724
|
|
|
72199
72725
|
// src/cli/commands/machines.tsx
|