@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/index.js
CHANGED
|
@@ -3430,6 +3430,9 @@ var init_machines = __esm(() => {
|
|
|
3430
3430
|
function isBlockingDependencyStatus(status) {
|
|
3431
3431
|
return status !== "completed" && status !== "cancelled";
|
|
3432
3432
|
}
|
|
3433
|
+
function isTerminalStatus(status) {
|
|
3434
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
3435
|
+
}
|
|
3433
3436
|
var TASK_STATUSES, TASK_PRIORITIES, PLAN_STATUSES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanNotFoundError, LockError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
|
|
3434
3437
|
var init_types = __esm(() => {
|
|
3435
3438
|
TASK_STATUSES = [
|
|
@@ -3787,6 +3790,103 @@ var init_identity_mapping = __esm(() => {
|
|
|
3787
3790
|
});
|
|
3788
3791
|
});
|
|
3789
3792
|
|
|
3793
|
+
// src/lib/sync-utils.ts
|
|
3794
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
3795
|
+
import { createHash } from "crypto";
|
|
3796
|
+
import { homedir } from "os";
|
|
3797
|
+
import { join } from "path";
|
|
3798
|
+
function getHomeDir() {
|
|
3799
|
+
return process.env["HOME"] || process.env["USERPROFILE"] || homedir();
|
|
3800
|
+
}
|
|
3801
|
+
function getTodosGlobalDir() {
|
|
3802
|
+
return join(getHomeDir(), ".hasna", "todos");
|
|
3803
|
+
}
|
|
3804
|
+
function ensureDir(dir) {
|
|
3805
|
+
if (!existsSync2(dir))
|
|
3806
|
+
mkdirSync(dir, { recursive: true });
|
|
3807
|
+
}
|
|
3808
|
+
function listJsonFiles(dir) {
|
|
3809
|
+
if (!existsSync2(dir))
|
|
3810
|
+
return [];
|
|
3811
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
3812
|
+
}
|
|
3813
|
+
function readJsonFile(path) {
|
|
3814
|
+
try {
|
|
3815
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
3816
|
+
} catch {
|
|
3817
|
+
return null;
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
function writeJsonFile(path, data) {
|
|
3821
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + `
|
|
3822
|
+
`);
|
|
3823
|
+
}
|
|
3824
|
+
function readHighWaterMark(dir) {
|
|
3825
|
+
const path = join(dir, ".highwatermark");
|
|
3826
|
+
if (!existsSync2(path))
|
|
3827
|
+
return 1;
|
|
3828
|
+
const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
|
|
3829
|
+
return isNaN(val) ? 1 : val;
|
|
3830
|
+
}
|
|
3831
|
+
function writeHighWaterMark(dir, value) {
|
|
3832
|
+
writeFileSync(join(dir, ".highwatermark"), String(value));
|
|
3833
|
+
}
|
|
3834
|
+
function getFileMtimeMs(path) {
|
|
3835
|
+
try {
|
|
3836
|
+
return statSync(path).mtimeMs;
|
|
3837
|
+
} catch {
|
|
3838
|
+
return null;
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
function parseTimestamp(value) {
|
|
3842
|
+
if (typeof value !== "string")
|
|
3843
|
+
return null;
|
|
3844
|
+
const parsed = Date.parse(value);
|
|
3845
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
3846
|
+
}
|
|
3847
|
+
function appendSyncConflict(metadata, conflict, limit = 5) {
|
|
3848
|
+
const current = Array.isArray(metadata["sync_conflicts"]) ? metadata["sync_conflicts"] : [];
|
|
3849
|
+
const next = [conflict, ...current].slice(0, limit);
|
|
3850
|
+
return { ...metadata, sync_conflicts: next };
|
|
3851
|
+
}
|
|
3852
|
+
function canonicalize(value) {
|
|
3853
|
+
if (Array.isArray(value))
|
|
3854
|
+
return value.map(canonicalize);
|
|
3855
|
+
if (!value || typeof value !== "object")
|
|
3856
|
+
return value;
|
|
3857
|
+
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).sort(([a], [b]) => a.localeCompare(b));
|
|
3858
|
+
return Object.fromEntries(entries.map(([key, entryValue]) => [key, canonicalize(entryValue)]));
|
|
3859
|
+
}
|
|
3860
|
+
function withoutSyncFingerprintMetadata(metadata) {
|
|
3861
|
+
const { [TODO_SYNC_FINGERPRINT_KEY]: _fingerprint, ...rest } = metadata;
|
|
3862
|
+
return rest;
|
|
3863
|
+
}
|
|
3864
|
+
function syncFingerprint(record) {
|
|
3865
|
+
const metadata = withoutSyncFingerprintMetadata(record.metadata || {});
|
|
3866
|
+
const canonical = canonicalize({ ...record, metadata });
|
|
3867
|
+
return `sha256:${createHash("sha256").update(JSON.stringify(canonical)).digest("hex")}`;
|
|
3868
|
+
}
|
|
3869
|
+
function withSyncFingerprint(record) {
|
|
3870
|
+
const metadata = withoutSyncFingerprintMetadata(record.metadata);
|
|
3871
|
+
return {
|
|
3872
|
+
...record,
|
|
3873
|
+
metadata: {
|
|
3874
|
+
...metadata,
|
|
3875
|
+
[TODO_SYNC_FINGERPRINT_KEY]: syncFingerprint({ ...record, metadata })
|
|
3876
|
+
}
|
|
3877
|
+
};
|
|
3878
|
+
}
|
|
3879
|
+
function hasSyncFingerprintChanged(record) {
|
|
3880
|
+
const stored = record.metadata?.[TODO_SYNC_FINGERPRINT_KEY];
|
|
3881
|
+
if (typeof stored !== "string" || stored.length === 0)
|
|
3882
|
+
return null;
|
|
3883
|
+
return stored !== syncFingerprint(record);
|
|
3884
|
+
}
|
|
3885
|
+
var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
|
|
3886
|
+
var init_sync_utils = __esm(() => {
|
|
3887
|
+
HOME = getHomeDir();
|
|
3888
|
+
});
|
|
3889
|
+
|
|
3790
3890
|
// src/storage/config.ts
|
|
3791
3891
|
var exports_config = {};
|
|
3792
3892
|
__export(exports_config, {
|
|
@@ -4117,8 +4217,8 @@ __export(exports_database, {
|
|
|
4117
4217
|
LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
|
|
4118
4218
|
});
|
|
4119
4219
|
import { Database } from "bun:sqlite";
|
|
4120
|
-
import { existsSync as
|
|
4121
|
-
import { dirname, join, resolve as resolve2 } from "path";
|
|
4220
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
4221
|
+
import { dirname, join as join2, resolve as resolve2 } from "path";
|
|
4122
4222
|
function isInMemoryDb(path) {
|
|
4123
4223
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
4124
4224
|
}
|
|
@@ -4127,8 +4227,8 @@ function findNearestProjectDb(startDir) {
|
|
|
4127
4227
|
const stopAt = gitRoot ? resolve2(gitRoot) : resolve2(startDir);
|
|
4128
4228
|
let dir = resolve2(startDir);
|
|
4129
4229
|
while (true) {
|
|
4130
|
-
const candidate =
|
|
4131
|
-
if (
|
|
4230
|
+
const candidate = join2(dir, ".hasna", "todos", "todos.db");
|
|
4231
|
+
if (existsSync3(candidate))
|
|
4132
4232
|
return candidate;
|
|
4133
4233
|
if (dir === stopAt)
|
|
4134
4234
|
break;
|
|
@@ -4142,7 +4242,7 @@ function findNearestProjectDb(startDir) {
|
|
|
4142
4242
|
function findGitRoot(startDir) {
|
|
4143
4243
|
let dir = resolve2(startDir);
|
|
4144
4244
|
while (true) {
|
|
4145
|
-
if (
|
|
4245
|
+
if (existsSync3(join2(dir, ".git")))
|
|
4146
4246
|
return dir;
|
|
4147
4247
|
const parent = dirname(dir);
|
|
4148
4248
|
if (parent === dir)
|
|
@@ -4152,8 +4252,7 @@ function findGitRoot(startDir) {
|
|
|
4152
4252
|
return null;
|
|
4153
4253
|
}
|
|
4154
4254
|
function getGlobalDbPath() {
|
|
4155
|
-
|
|
4156
|
-
return join(home, ".hasna", "todos", "todos.db");
|
|
4255
|
+
return join2(getHomeDir(), ".hasna", "todos", "todos.db");
|
|
4157
4256
|
}
|
|
4158
4257
|
function hasExplicitProjectArg(args = process.argv.slice(2)) {
|
|
4159
4258
|
return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
|
|
@@ -4191,7 +4290,7 @@ function getDbPath() {
|
|
|
4191
4290
|
if (process.env["TODOS_DB_SCOPE"] === "project") {
|
|
4192
4291
|
const gitRoot = findGitRoot(cwd);
|
|
4193
4292
|
if (gitRoot && canCreateScopedProjectDb()) {
|
|
4194
|
-
return
|
|
4293
|
+
return join2(gitRoot, ".hasna", "todos", "todos.db");
|
|
4195
4294
|
}
|
|
4196
4295
|
}
|
|
4197
4296
|
return getGlobalDbPath();
|
|
@@ -4199,16 +4298,16 @@ function getDbPath() {
|
|
|
4199
4298
|
function getDatabasePath() {
|
|
4200
4299
|
return getDbPath();
|
|
4201
4300
|
}
|
|
4202
|
-
function
|
|
4301
|
+
function ensureDir2(filePath) {
|
|
4203
4302
|
if (isInMemoryDb(filePath))
|
|
4204
4303
|
return;
|
|
4205
4304
|
const dir = dirname(resolve2(filePath));
|
|
4206
|
-
if (!
|
|
4207
|
-
|
|
4305
|
+
if (!existsSync3(dir)) {
|
|
4306
|
+
mkdirSync2(dir, { recursive: true });
|
|
4208
4307
|
}
|
|
4209
4308
|
}
|
|
4210
4309
|
function openDatabase(path) {
|
|
4211
|
-
|
|
4310
|
+
ensureDir2(path);
|
|
4212
4311
|
const db = new Database(path);
|
|
4213
4312
|
db.run("PRAGMA busy_timeout = 5000");
|
|
4214
4313
|
db.run("PRAGMA journal_mode = WAL");
|
|
@@ -4375,105 +4474,10 @@ var init_database = __esm(() => {
|
|
|
4375
4474
|
init_machines();
|
|
4376
4475
|
init_identity_mapping();
|
|
4377
4476
|
init_types();
|
|
4477
|
+
init_sync_utils();
|
|
4378
4478
|
ALLOWED_TABLES = new Set(["tasks", "projects", "agents", "plans", "task_lists", "task_templates", "project_knowledge_records", "project_risks", "local_retrospectives"]);
|
|
4379
4479
|
});
|
|
4380
4480
|
|
|
4381
|
-
// src/lib/sync-utils.ts
|
|
4382
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
4383
|
-
import { createHash } from "crypto";
|
|
4384
|
-
import { join as join2 } from "path";
|
|
4385
|
-
function getHomeDir() {
|
|
4386
|
-
return process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
4387
|
-
}
|
|
4388
|
-
function getTodosGlobalDir() {
|
|
4389
|
-
return join2(getHomeDir(), ".hasna", "todos");
|
|
4390
|
-
}
|
|
4391
|
-
function ensureDir2(dir) {
|
|
4392
|
-
if (!existsSync3(dir))
|
|
4393
|
-
mkdirSync2(dir, { recursive: true });
|
|
4394
|
-
}
|
|
4395
|
-
function listJsonFiles(dir) {
|
|
4396
|
-
if (!existsSync3(dir))
|
|
4397
|
-
return [];
|
|
4398
|
-
return readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
4399
|
-
}
|
|
4400
|
-
function readJsonFile(path) {
|
|
4401
|
-
try {
|
|
4402
|
-
return JSON.parse(readFileSync(path, "utf-8"));
|
|
4403
|
-
} catch {
|
|
4404
|
-
return null;
|
|
4405
|
-
}
|
|
4406
|
-
}
|
|
4407
|
-
function writeJsonFile(path, data) {
|
|
4408
|
-
writeFileSync(path, JSON.stringify(data, null, 2) + `
|
|
4409
|
-
`);
|
|
4410
|
-
}
|
|
4411
|
-
function readHighWaterMark(dir) {
|
|
4412
|
-
const path = join2(dir, ".highwatermark");
|
|
4413
|
-
if (!existsSync3(path))
|
|
4414
|
-
return 1;
|
|
4415
|
-
const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
|
|
4416
|
-
return isNaN(val) ? 1 : val;
|
|
4417
|
-
}
|
|
4418
|
-
function writeHighWaterMark(dir, value) {
|
|
4419
|
-
writeFileSync(join2(dir, ".highwatermark"), String(value));
|
|
4420
|
-
}
|
|
4421
|
-
function getFileMtimeMs(path) {
|
|
4422
|
-
try {
|
|
4423
|
-
return statSync(path).mtimeMs;
|
|
4424
|
-
} catch {
|
|
4425
|
-
return null;
|
|
4426
|
-
}
|
|
4427
|
-
}
|
|
4428
|
-
function parseTimestamp(value) {
|
|
4429
|
-
if (typeof value !== "string")
|
|
4430
|
-
return null;
|
|
4431
|
-
const parsed = Date.parse(value);
|
|
4432
|
-
return Number.isNaN(parsed) ? null : parsed;
|
|
4433
|
-
}
|
|
4434
|
-
function appendSyncConflict(metadata, conflict, limit = 5) {
|
|
4435
|
-
const current = Array.isArray(metadata["sync_conflicts"]) ? metadata["sync_conflicts"] : [];
|
|
4436
|
-
const next = [conflict, ...current].slice(0, limit);
|
|
4437
|
-
return { ...metadata, sync_conflicts: next };
|
|
4438
|
-
}
|
|
4439
|
-
function canonicalize(value) {
|
|
4440
|
-
if (Array.isArray(value))
|
|
4441
|
-
return value.map(canonicalize);
|
|
4442
|
-
if (!value || typeof value !== "object")
|
|
4443
|
-
return value;
|
|
4444
|
-
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).sort(([a], [b]) => a.localeCompare(b));
|
|
4445
|
-
return Object.fromEntries(entries.map(([key, entryValue]) => [key, canonicalize(entryValue)]));
|
|
4446
|
-
}
|
|
4447
|
-
function withoutSyncFingerprintMetadata(metadata) {
|
|
4448
|
-
const { [TODO_SYNC_FINGERPRINT_KEY]: _fingerprint, ...rest } = metadata;
|
|
4449
|
-
return rest;
|
|
4450
|
-
}
|
|
4451
|
-
function syncFingerprint(record) {
|
|
4452
|
-
const metadata = withoutSyncFingerprintMetadata(record.metadata || {});
|
|
4453
|
-
const canonical = canonicalize({ ...record, metadata });
|
|
4454
|
-
return `sha256:${createHash("sha256").update(JSON.stringify(canonical)).digest("hex")}`;
|
|
4455
|
-
}
|
|
4456
|
-
function withSyncFingerprint(record) {
|
|
4457
|
-
const metadata = withoutSyncFingerprintMetadata(record.metadata);
|
|
4458
|
-
return {
|
|
4459
|
-
...record,
|
|
4460
|
-
metadata: {
|
|
4461
|
-
...metadata,
|
|
4462
|
-
[TODO_SYNC_FINGERPRINT_KEY]: syncFingerprint({ ...record, metadata })
|
|
4463
|
-
}
|
|
4464
|
-
};
|
|
4465
|
-
}
|
|
4466
|
-
function hasSyncFingerprintChanged(record) {
|
|
4467
|
-
const stored = record.metadata?.[TODO_SYNC_FINGERPRINT_KEY];
|
|
4468
|
-
if (typeof stored !== "string" || stored.length === 0)
|
|
4469
|
-
return null;
|
|
4470
|
-
return stored !== syncFingerprint(record);
|
|
4471
|
-
}
|
|
4472
|
-
var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
|
|
4473
|
-
var init_sync_utils = __esm(() => {
|
|
4474
|
-
HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
4475
|
-
});
|
|
4476
|
-
|
|
4477
4481
|
// src/lib/config.ts
|
|
4478
4482
|
import { existsSync as existsSync4 } from "fs";
|
|
4479
4483
|
import { dirname as dirname2, join as join3 } from "path";
|
|
@@ -4499,7 +4503,7 @@ function loadConfig() {
|
|
|
4499
4503
|
}
|
|
4500
4504
|
function saveConfig(config) {
|
|
4501
4505
|
const configPath = getConfigPath();
|
|
4502
|
-
|
|
4506
|
+
ensureDir(dirname2(configPath));
|
|
4503
4507
|
writeJsonFile(configPath, config);
|
|
4504
4508
|
cached = config;
|
|
4505
4509
|
return config;
|
|
@@ -5844,7 +5848,7 @@ var init_event_hooks = __esm(() => {
|
|
|
5844
5848
|
// node_modules/.bun/@hasna+events@0.1.11/node_modules/@hasna/events/dist/index.js
|
|
5845
5849
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
5846
5850
|
import { existsSync as existsSync5 } from "fs";
|
|
5847
|
-
import { homedir } from "os";
|
|
5851
|
+
import { homedir as homedir2 } from "os";
|
|
5848
5852
|
import { join as join4 } from "path";
|
|
5849
5853
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
5850
5854
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -5946,7 +5950,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
5946
5950
|
return channel.filters.some((filter) => eventMatchesFilter(event, filter));
|
|
5947
5951
|
}
|
|
5948
5952
|
function getEventsDataDir(override) {
|
|
5949
|
-
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join4(
|
|
5953
|
+
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join4(homedir2(), ".hasna", "events");
|
|
5950
5954
|
}
|
|
5951
5955
|
|
|
5952
5956
|
class JsonEventsStore {
|
|
@@ -9286,11 +9290,13 @@ function updateTask(id, input, db) {
|
|
|
9286
9290
|
}
|
|
9287
9291
|
sets.push("status = ?");
|
|
9288
9292
|
params.push(input.status);
|
|
9293
|
+
if (isTerminalStatus(input.status)) {
|
|
9294
|
+
sets.push("locked_by = NULL");
|
|
9295
|
+
sets.push("locked_at = NULL");
|
|
9296
|
+
}
|
|
9289
9297
|
if (input.status === "completed") {
|
|
9290
9298
|
sets.push("completed_at = ?");
|
|
9291
9299
|
params.push(completionTimestamp);
|
|
9292
|
-
sets.push("locked_by = NULL");
|
|
9293
|
-
sets.push("locked_at = NULL");
|
|
9294
9300
|
} else if (task.status === "completed" && input.completed_at === undefined) {
|
|
9295
9301
|
sets.push("completed_at = NULL");
|
|
9296
9302
|
}
|
|
@@ -9414,6 +9420,7 @@ function updateTask(id, input, db) {
|
|
|
9414
9420
|
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
9415
9421
|
const reopened = input.status !== undefined && input.status !== "completed" && task.status === "completed" && input.completed_at === undefined;
|
|
9416
9422
|
const completedNow = input.status === "completed";
|
|
9423
|
+
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
9417
9424
|
const updatedTask = {
|
|
9418
9425
|
...task,
|
|
9419
9426
|
...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
|
|
@@ -9421,8 +9428,8 @@ function updateTask(id, input, db) {
|
|
|
9421
9428
|
metadata: input.metadata ?? task.metadata,
|
|
9422
9429
|
version: task.version + 1,
|
|
9423
9430
|
updated_at: timestamp2,
|
|
9424
|
-
locked_by:
|
|
9425
|
-
locked_at:
|
|
9431
|
+
locked_by: terminalNow ? null : task.locked_by,
|
|
9432
|
+
locked_at: terminalNow ? null : task.locked_at,
|
|
9426
9433
|
completed_at: completedNow ? completionTimestamp : reopened ? null : input.completed_at !== undefined ? input.completed_at : task.completed_at,
|
|
9427
9434
|
sla_minutes: input.sla_minutes !== undefined ? input.sla_minutes : task.sla_minutes,
|
|
9428
9435
|
actual_minutes: input.actual_minutes ?? task.actual_minutes,
|
|
@@ -12138,7 +12145,7 @@ var init_dispatches = __esm(() => {
|
|
|
12138
12145
|
// package.json
|
|
12139
12146
|
var package_default = {
|
|
12140
12147
|
name: "@hasna/todos",
|
|
12141
|
-
version: "0.
|
|
12148
|
+
version: "0.15.1",
|
|
12142
12149
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
12143
12150
|
type: "module",
|
|
12144
12151
|
main: "dist/index.js",
|
|
@@ -26735,9 +26742,11 @@ async function updateTask2(id, input, store) {
|
|
|
26735
26742
|
throw new Error(`Task ${id} version conflict: expected ${existing.version}, got ${input.version}`);
|
|
26736
26743
|
}
|
|
26737
26744
|
const reopened = existing.status === "completed" && input.status !== undefined && input.status !== "completed" && input.completed_at === undefined;
|
|
26745
|
+
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
26738
26746
|
const task2 = {
|
|
26739
26747
|
...existing,
|
|
26740
26748
|
...definedPatch(input),
|
|
26749
|
+
...terminalNow ? { locked_by: null, locked_at: null } : {},
|
|
26741
26750
|
version: existing.version + 1,
|
|
26742
26751
|
updated_at: new Date().toISOString(),
|
|
26743
26752
|
tags: input.tags ?? existing.tags,
|
|
@@ -36059,6 +36068,7 @@ function resourceDiagnostics() {
|
|
|
36059
36068
|
};
|
|
36060
36069
|
}
|
|
36061
36070
|
// src/lib/sandbox-profiles.ts
|
|
36071
|
+
init_sync_utils();
|
|
36062
36072
|
import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
|
|
36063
36073
|
import { join as join12, dirname as dirname9 } from "path";
|
|
36064
36074
|
var SANDBOX_PROFILE_VERSION = "todos.sandbox-profile.v1";
|
|
@@ -36072,8 +36082,7 @@ function getProfilesPath() {
|
|
|
36072
36082
|
return local;
|
|
36073
36083
|
if (existsSync14(local))
|
|
36074
36084
|
return local;
|
|
36075
|
-
|
|
36076
|
-
return join12(home, ".hasna", "todos", "sandbox-profiles.json");
|
|
36085
|
+
return join12(getHomeDir(), ".hasna", "todos", "sandbox-profiles.json");
|
|
36077
36086
|
}
|
|
36078
36087
|
var cached2 = null;
|
|
36079
36088
|
function resetSandboxProfileCache() {
|
|
@@ -39758,6 +39767,7 @@ todos import issues ./linear.json --source linear --dry-run
|
|
|
39758
39767
|
`;
|
|
39759
39768
|
}
|
|
39760
39769
|
// src/lib/run-records.ts
|
|
39770
|
+
init_sync_utils();
|
|
39761
39771
|
init_database();
|
|
39762
39772
|
init_secret_redaction();
|
|
39763
39773
|
import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
@@ -39997,8 +40007,7 @@ function getDefaultReplayDir() {
|
|
|
39997
40007
|
const local = join14(process.cwd(), ".todos", "replays");
|
|
39998
40008
|
if (existsSync19(join14(process.cwd(), ".todos")))
|
|
39999
40009
|
return local;
|
|
40000
|
-
|
|
40001
|
-
return join14(home, ".hasna", "todos", "replays");
|
|
40010
|
+
return join14(getHomeDir(), ".hasna", "todos", "replays");
|
|
40002
40011
|
}
|
|
40003
40012
|
// src/lib/release-checks.ts
|
|
40004
40013
|
init_secret_redaction();
|
|
@@ -44371,7 +44380,7 @@ function captureEnvironmentSnapshot(input = {}) {
|
|
|
44371
44380
|
}
|
|
44372
44381
|
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
44373
44382
|
const path = outputPath ? resolve17(outputPath) : join18(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
44374
|
-
|
|
44383
|
+
ensureDir(dirname15(path));
|
|
44375
44384
|
writeJsonFile(path, snapshot);
|
|
44376
44385
|
return path;
|
|
44377
44386
|
}
|
|
@@ -48156,6 +48165,7 @@ function queryTasksByLocalFields(query, db) {
|
|
|
48156
48165
|
}
|
|
48157
48166
|
|
|
48158
48167
|
// src/lib/saved-search-views.ts
|
|
48168
|
+
var SAVED_SEARCH_SCOPES = ["all", "tasks", "projects", "plans", "runs", "comments"];
|
|
48159
48169
|
function parseFilters(value) {
|
|
48160
48170
|
if (!value)
|
|
48161
48171
|
return {};
|
|
@@ -48174,10 +48184,8 @@ function rowToSavedSearchView(row) {
|
|
|
48174
48184
|
};
|
|
48175
48185
|
}
|
|
48176
48186
|
function normalizeScope(scope) {
|
|
48177
|
-
|
|
48178
|
-
|
|
48179
|
-
}
|
|
48180
|
-
return "tasks";
|
|
48187
|
+
const candidate = (scope ?? "").trim().toLowerCase();
|
|
48188
|
+
return SAVED_SEARCH_SCOPES.includes(candidate) ? candidate : "tasks";
|
|
48181
48189
|
}
|
|
48182
48190
|
function normalizeName3(name) {
|
|
48183
48191
|
const normalized = name.trim();
|
|
@@ -48540,7 +48548,7 @@ function taskToClaudeTask(task2, claudeTaskId, existingMeta) {
|
|
|
48540
48548
|
function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
48541
48549
|
const dir = getTaskListDir(taskListId);
|
|
48542
48550
|
if (!existsSync25(dir))
|
|
48543
|
-
|
|
48551
|
+
ensureDir(dir);
|
|
48544
48552
|
const filter = {};
|
|
48545
48553
|
if (projectId)
|
|
48546
48554
|
filter["project_id"] = projectId;
|
|
@@ -48767,7 +48775,7 @@ function metadataKey(agent) {
|
|
|
48767
48775
|
function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
48768
48776
|
const dir = getTaskListDir2(agent, taskListId);
|
|
48769
48777
|
if (!existsSync26(dir))
|
|
48770
|
-
|
|
48778
|
+
ensureDir(dir);
|
|
48771
48779
|
const filter = {};
|
|
48772
48780
|
if (projectId)
|
|
48773
48781
|
filter["project_id"] = projectId;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag resolution for `todos bulk tag|untag`.
|
|
3
|
+
*
|
|
4
|
+
* Kept as pure functions with no database or transport dependency so both the
|
|
5
|
+
* remote (/v1) and local (SQLite) bulk paths resolve tags identically. A tag
|
|
6
|
+
* that merges on one transport and replaces on the other would corrupt rows
|
|
7
|
+
* depending on which machine ran the backfill.
|
|
8
|
+
*/
|
|
9
|
+
export type BulkTagAction = "tag" | "untag";
|
|
10
|
+
export interface BulkTagResolution {
|
|
11
|
+
/** The full tag list to persist. */
|
|
12
|
+
tags: string[];
|
|
13
|
+
/**
|
|
14
|
+
* False when the row already satisfies the request. Callers skip the write
|
|
15
|
+
* entirely, so a re-run after a partial failure does not bump row versions
|
|
16
|
+
* or emit audit noise for rows that were already correct.
|
|
17
|
+
*/
|
|
18
|
+
changed: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse a comma-separated `--tag` argument.
|
|
22
|
+
*
|
|
23
|
+
* Splits ONLY on commas: `:` and `/` are legal inside a tag and are load
|
|
24
|
+
* bearing for the namespaced tags this fleet already stores (`repo:secrets`,
|
|
25
|
+
* `gh:hasna/todos`, `directive:k_msd4cz8t_ste6f4`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseTagList(raw: string | undefined | null): string[];
|
|
28
|
+
export type TagArgumentResolution = {
|
|
29
|
+
ok: true;
|
|
30
|
+
raw: string | undefined;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
conflict: {
|
|
34
|
+
tag: string;
|
|
35
|
+
tags: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Reconcile the `--tag` / `--tags` spellings.
|
|
40
|
+
*
|
|
41
|
+
* Both are accepted because the rest of the CLI accepts both, but when they are
|
|
42
|
+
* both present and name DIFFERENT sets the run is refused rather than picking a
|
|
43
|
+
* winner. Silently dropping one of two explicitly-passed tag arguments is the
|
|
44
|
+
* wrong failure mode for a command whose purpose is stamping thousands of rows:
|
|
45
|
+
* the operator sees rc=0 and a success count while the tags they asked for are
|
|
46
|
+
* simply absent.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveTagArgument(tag: string | undefined, tags: string | undefined): TagArgumentResolution;
|
|
49
|
+
/**
|
|
50
|
+
* Apply `action` to `current`, returning the tag list to persist.
|
|
51
|
+
*
|
|
52
|
+
* `tag` MERGES — it never replaces. `todos update --tags` replaces the list,
|
|
53
|
+
* and reusing that semantic for a bulk provenance backfill would strip every
|
|
54
|
+
* unrelated tag from every row it touched.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveBulkTags(current: readonly string[] | undefined | null, action: BulkTagAction, tags: readonly string[]): BulkTagResolution;
|
|
57
|
+
//# sourceMappingURL=bulk-tags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk-tags.d.ts","sourceRoot":"","sources":["../../src/lib/bulk-tags.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,OAAO,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,EAAE,CAWrE;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAE3D;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,qBAAqB,CAUvB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,EAC7C,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,iBAAiB,CAiBnB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Closed-vocabulary (enum) value validation for CLI flags and API query params.
|
|
3
|
+
*
|
|
4
|
+
* Why this module exists
|
|
5
|
+
* ----------------------
|
|
6
|
+
* An unrecognised enum value used to flow straight into the storage filter, where
|
|
7
|
+
* it matched no rows. The command then printed "No tasks found." and exited 0 — a
|
|
8
|
+
* silent empty result set that reads as "there is no work here". `todos list
|
|
9
|
+
* --status open` (`open` is not in the vocabulary) reported zero tasks on a project
|
|
10
|
+
* that had 27, and that empty set was relayed to a human as fact.
|
|
11
|
+
*
|
|
12
|
+
* The rule this module enforces: an out-of-vocabulary value is an ERROR, never an
|
|
13
|
+
* empty result. Callers surface it non-zero (CLI) or as HTTP 400 (API).
|
|
14
|
+
*
|
|
15
|
+
* The vocabulary is ALWAYS supplied by the caller from the single exported source
|
|
16
|
+
* of truth (`TASK_STATUSES`, `TASK_PRIORITIES`, `PLAN_STATUSES`, `DISPATCH_STATUSES`
|
|
17
|
+
* in src/types/index.ts, or a subsystem's own exported constant). Nothing in this
|
|
18
|
+
* file re-types a vocabulary, so a value added to a constant is accepted here with
|
|
19
|
+
* no further edit.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Vocabulary members plausibly meant by `value`, nearest first.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately conservative: a substring/prefix relation, or an edit distance
|
|
25
|
+
* within a third of the word's length. A wrong suggestion is worse than none,
|
|
26
|
+
* because the error already lists every valid value.
|
|
27
|
+
*/
|
|
28
|
+
export declare function suggestVocabularyMatches(value: string, vocabulary: readonly string[], limit?: number): string[];
|
|
29
|
+
export interface EnumVocabularySpec<T extends string> {
|
|
30
|
+
/** Flag or query-param name as the user typed it, e.g. "--status" or "status". */
|
|
31
|
+
readonly name: string;
|
|
32
|
+
/** The single source of truth for this vocabulary. Never re-typed locally. */
|
|
33
|
+
readonly vocabulary: readonly T[];
|
|
34
|
+
/**
|
|
35
|
+
* Canonicalizer applied per element before the membership check — this is where
|
|
36
|
+
* documented aliases (`done` -> `completed`) and case folding live. Must be a
|
|
37
|
+
* pure string mapping; a value it does not recognise is returned unchanged so it
|
|
38
|
+
* still fails the membership check rather than being silently rewritten.
|
|
39
|
+
*/
|
|
40
|
+
readonly normalize?: (value: string) => string;
|
|
41
|
+
/**
|
|
42
|
+
* Remediation text for inputs that are NOT vocabulary members but that operators
|
|
43
|
+
* plausibly type, keyed by the normalized input. These are not accepted values —
|
|
44
|
+
* they only make the rejection actionable (e.g. `all` -> point at `--all`).
|
|
45
|
+
*/
|
|
46
|
+
readonly hints?: Readonly<Record<string, string>>;
|
|
47
|
+
/** Whether a comma-separated list is accepted. Defaults to true. */
|
|
48
|
+
readonly allowList?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export type EnumVocabularyResult<T extends string> = {
|
|
51
|
+
readonly ok: true;
|
|
52
|
+
readonly values: T[];
|
|
53
|
+
} | {
|
|
54
|
+
readonly ok: false;
|
|
55
|
+
readonly message: string;
|
|
56
|
+
readonly invalid: string[];
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Split, canonicalize and validate a raw flag/query value against its vocabulary.
|
|
60
|
+
*
|
|
61
|
+
* Every element of a comma-separated list is validated: a single bad element fails
|
|
62
|
+
* the whole value rather than being dropped, because dropping it produced a result
|
|
63
|
+
* set that looked authoritative but silently ignored part of what was asked for.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveEnumVocabulary<T extends string>(raw: string, spec: EnumVocabularySpec<T>): EnumVocabularyResult<T>;
|
|
66
|
+
/**
|
|
67
|
+
* Collapse a validated list back to the shape the storage filter expects: a bare
|
|
68
|
+
* string for a single value, an array for a real list. Keeps the emitted SQL/query
|
|
69
|
+
* identical to what single-value callers produced before validation was added.
|
|
70
|
+
*/
|
|
71
|
+
export declare function collapseEnumValues<T extends string>(values: T[]): T | T[];
|
|
72
|
+
//# sourceMappingURL=enum-vocabulary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enum-vocabulary.d.ts","sourceRoot":"","sources":["../../src/lib/enum-vocabulary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAsBH;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,KAAK,SAAI,GACR,MAAM,EAAE,CAuBV;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,MAAM;IAClD,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,oEAAoE;IACpE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,MAAM,IAC7C;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,CAAA;CAAE,GAC3C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEjF;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,EACpD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC1B,oBAAoB,CAAC,CAAC,CAAC,CAqEzB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAEzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-records.d.ts","sourceRoot":"","sources":["../../src/lib/run-records.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"run-records.d.ts","sourceRoot":"","sources":["../../src/lib/run-records.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-profiles.d.ts","sourceRoot":"","sources":["../../src/lib/sandbox-profiles.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"sandbox-profiles.d.ts","sourceRoot":"","sources":["../../src/lib/sandbox-profiles.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAeD,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,wBAAgB,yBAAyB,IAAI,cAAc,EAAE,CAqB5D;AAED,wBAAgB,mBAAmB,IAAI,cAAc,EAAE,CAUtD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAErE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,IAAI,CAKpE;AAcD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,iBAAiB,EACxB,WAAW,SAAY,EACvB,MAAM,UAAQ,GACb,kBAAkB,CA0CpB"}
|
|
@@ -2,7 +2,12 @@ import type { Database } from "bun:sqlite";
|
|
|
2
2
|
import type { Plan, Project, Task, TaskComment, TaskPriority } from "../types/index.js";
|
|
3
3
|
import type { TaskRun } from "../db/task-runs.js";
|
|
4
4
|
import { type LocalTaskFieldQuery } from "./local-fields.js";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The single source of truth for search scopes. Both the type and `normalizeScope`
|
|
7
|
+
* derive from it, so `--scope` validation cannot drift from what the union allows.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SAVED_SEARCH_SCOPES: readonly ["all", "tasks", "projects", "plans", "runs", "comments"];
|
|
10
|
+
export type SavedSearchScope = (typeof SAVED_SEARCH_SCOPES)[number];
|
|
6
11
|
export interface SavedSearchFilters {
|
|
7
12
|
query?: string;
|
|
8
13
|
project_id?: string;
|
|
@@ -50,6 +55,12 @@ export interface SaveSearchViewInput {
|
|
|
50
55
|
scope?: SavedSearchScope;
|
|
51
56
|
filters?: SavedSearchFilters;
|
|
52
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Coerce a stored/incoming scope to a member of `SAVED_SEARCH_SCOPES`, defaulting
|
|
60
|
+
* to "tasks". The default keeps persisted views readable if a scope is ever
|
|
61
|
+
* retired; it is NOT a substitute for validating user input, which the CLI does
|
|
62
|
+
* up front so a mistyped `--scope` fails loudly instead of silently searching tasks.
|
|
63
|
+
*/
|
|
53
64
|
export declare function normalizeScope(scope: string | undefined | null): SavedSearchScope;
|
|
54
65
|
export declare function runSavedSearch(filters?: SavedSearchFilters, scope?: SavedSearchScope, db?: Database): SavedSearchRunResult;
|
|
55
66
|
export declare function saveSearchView(input: SaveSearchViewInput, db?: Database): SavedSearchView;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saved-search-views.d.ts","sourceRoot":"","sources":["../../src/lib/saved-search-views.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAE7D,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAc,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGtF,
|
|
1
|
+
{"version":3,"file":"saved-search-views.d.ts","sourceRoot":"","sources":["../../src/lib/saved-search-views.ts"],"names":[],"mappings":"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"}
|