@hasna/todos 0.11.41 → 0.11.43
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 +37 -9
- package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
- package/dist/cli/commands/project-commands.d.ts.map +1 -1
- package/dist/cli/index.js +1331 -264
- package/dist/cli-mcp-parity.d.ts +1 -1
- package/dist/cli-mcp-parity.d.ts.map +1 -1
- package/dist/contracts.js +73 -6
- package/dist/db/builtin-templates.d.ts +17 -0
- package/dist/db/builtin-templates.d.ts.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +785 -168
- package/dist/json-contracts.d.ts.map +1 -1
- package/dist/lib/local-bridge.d.ts +2 -0
- package/dist/lib/local-bridge.d.ts.map +1 -1
- package/dist/lib/saved-search-views.d.ts +60 -0
- package/dist/lib/saved-search-views.d.ts.map +1 -0
- package/dist/lib/todos-md.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1485 -99
- package/dist/mcp/token-utils.d.ts.map +1 -1
- package/dist/mcp/tools/task-meta-tools.d.ts.map +1 -1
- package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
- package/dist/mcp/tools/templates.d.ts.map +1 -1
- package/dist/mcp.js +7 -1
- package/dist/registry.js +122 -8
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +24 -0
- package/dist/storage.js +24 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -979,6 +979,19 @@ var init_migrations = __esm(() => {
|
|
|
979
979
|
);
|
|
980
980
|
CREATE INDEX IF NOT EXISTS idx_handoff_acks_agent ON handoff_acknowledgements(agent_id, acknowledged_at);
|
|
981
981
|
INSERT OR IGNORE INTO _migrations (id) VALUES (54);
|
|
982
|
+
`,
|
|
983
|
+
`
|
|
984
|
+
CREATE TABLE IF NOT EXISTS saved_search_views (
|
|
985
|
+
id TEXT PRIMARY KEY,
|
|
986
|
+
name TEXT NOT NULL UNIQUE,
|
|
987
|
+
description TEXT,
|
|
988
|
+
scope TEXT NOT NULL DEFAULT 'tasks' CHECK(scope IN ('all', 'tasks', 'projects', 'plans', 'runs', 'comments')),
|
|
989
|
+
filters TEXT NOT NULL DEFAULT '{}',
|
|
990
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
991
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
992
|
+
);
|
|
993
|
+
CREATE INDEX IF NOT EXISTS idx_saved_search_views_scope ON saved_search_views(scope);
|
|
994
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (55);
|
|
982
995
|
`
|
|
983
996
|
];
|
|
984
997
|
});
|
|
@@ -1155,6 +1168,17 @@ function ensureSchema(db) {
|
|
|
1155
1168
|
PRIMARY KEY (handoff_id, agent_id)
|
|
1156
1169
|
)`);
|
|
1157
1170
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_handoff_acks_agent ON handoff_acknowledgements(agent_id, acknowledged_at)");
|
|
1171
|
+
ensureTable("saved_search_views", `
|
|
1172
|
+
CREATE TABLE saved_search_views (
|
|
1173
|
+
id TEXT PRIMARY KEY,
|
|
1174
|
+
name TEXT NOT NULL UNIQUE,
|
|
1175
|
+
description TEXT,
|
|
1176
|
+
scope TEXT NOT NULL DEFAULT 'tasks' CHECK(scope IN ('all', 'tasks', 'projects', 'plans', 'runs', 'comments')),
|
|
1177
|
+
filters TEXT NOT NULL DEFAULT '{}',
|
|
1178
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1179
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1180
|
+
)`);
|
|
1181
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_saved_search_views_scope ON saved_search_views(scope)");
|
|
1158
1182
|
ensureTable("task_relationships", `
|
|
1159
1183
|
CREATE TABLE task_relationships (
|
|
1160
1184
|
id TEXT PRIMARY KEY,
|
|
@@ -2713,6 +2737,39 @@ var TODOS_JSON_CONTRACTS = [
|
|
|
2713
2737
|
created: field("object", "Flags and source types created by this bootstrap run.")
|
|
2714
2738
|
},
|
|
2715
2739
|
optional: {}
|
|
2740
|
+
}),
|
|
2741
|
+
contract({
|
|
2742
|
+
id: "saved_search_view",
|
|
2743
|
+
name: "Saved Search View",
|
|
2744
|
+
description: "Local saved search view for repeatable task, project, plan, run, comment, or cross-entity searches.",
|
|
2745
|
+
surfaces: ["cli", "mcp", "sdk"],
|
|
2746
|
+
stability: "stable",
|
|
2747
|
+
required: {
|
|
2748
|
+
id: idField,
|
|
2749
|
+
name: field("string", "Human-readable unique view name."),
|
|
2750
|
+
description: field(["string", "null"], "Optional view description.", true),
|
|
2751
|
+
scope: field("string", "Search scope: all, tasks, projects, plans, runs, or comments."),
|
|
2752
|
+
filters: field("object", "Saved local filter object."),
|
|
2753
|
+
created_at: isoDateField,
|
|
2754
|
+
updated_at: isoDateField
|
|
2755
|
+
},
|
|
2756
|
+
optional: {}
|
|
2757
|
+
}),
|
|
2758
|
+
contract({
|
|
2759
|
+
id: "saved_search_run_result",
|
|
2760
|
+
name: "Saved Search Run Result",
|
|
2761
|
+
description: "Stable JSON envelope returned when running a saved search view or cross-entity search.",
|
|
2762
|
+
surfaces: ["cli", "mcp", "sdk"],
|
|
2763
|
+
stability: "stable",
|
|
2764
|
+
required: {
|
|
2765
|
+
scope: field("string", "Search scope used for the run."),
|
|
2766
|
+
filters: field("object", "Applied local filters."),
|
|
2767
|
+
count: field("integer", "Number of returned result records."),
|
|
2768
|
+
results: field("array", "Result records with entity_type and entity.")
|
|
2769
|
+
},
|
|
2770
|
+
optional: {
|
|
2771
|
+
view: field("object", "Saved view metadata when the run came from a named view.")
|
|
2772
|
+
}
|
|
2716
2773
|
})
|
|
2717
2774
|
];
|
|
2718
2775
|
function expectedTypes(contract2) {
|
|
@@ -6247,7 +6304,8 @@ var dataKeys = [
|
|
|
6247
6304
|
"task_files",
|
|
6248
6305
|
"task_commits",
|
|
6249
6306
|
"task_git_refs",
|
|
6250
|
-
"task_verifications"
|
|
6307
|
+
"task_verifications",
|
|
6308
|
+
"saved_views"
|
|
6251
6309
|
];
|
|
6252
6310
|
var insertColumns = {
|
|
6253
6311
|
projects: ["id", "name", "path", "description", "task_list_id", "task_prefix", "task_counter", "created_at", "updated_at", "machine_id", "synced_at"],
|
|
@@ -6319,7 +6377,8 @@ var insertColumns = {
|
|
|
6319
6377
|
task_files: ["id", "task_id", "path", "status", "agent_id", "note", "created_at", "updated_at", "machine_id"],
|
|
6320
6378
|
task_commits: ["id", "task_id", "sha", "message", "author", "files_changed", "committed_at", "created_at"],
|
|
6321
6379
|
task_git_refs: ["id", "task_id", "ref_type", "name", "url", "provider", "metadata", "created_at", "updated_at"],
|
|
6322
|
-
task_verifications: ["id", "task_id", "command", "status", "output_summary", "artifact_path", "agent_id", "run_at", "created_at"]
|
|
6380
|
+
task_verifications: ["id", "task_id", "command", "status", "output_summary", "artifact_path", "agent_id", "run_at", "created_at"],
|
|
6381
|
+
saved_views: ["id", "name", "description", "scope", "filters", "created_at", "updated_at"]
|
|
6323
6382
|
};
|
|
6324
6383
|
var tableByKey = {
|
|
6325
6384
|
projects: "projects",
|
|
@@ -6335,9 +6394,10 @@ var tableByKey = {
|
|
|
6335
6394
|
task_files: "task_files",
|
|
6336
6395
|
task_commits: "task_commits",
|
|
6337
6396
|
task_git_refs: "task_git_refs",
|
|
6338
|
-
task_verifications: "task_verifications"
|
|
6397
|
+
task_verifications: "task_verifications",
|
|
6398
|
+
saved_views: "saved_search_views"
|
|
6339
6399
|
};
|
|
6340
|
-
var jsonColumns = new Set(["metadata", "tags", "data", "files_changed"]);
|
|
6400
|
+
var jsonColumns = new Set(["metadata", "tags", "data", "files_changed", "filters"]);
|
|
6341
6401
|
function packageSource(version) {
|
|
6342
6402
|
return {
|
|
6343
6403
|
packageName: "@hasna/todos",
|
|
@@ -6410,6 +6470,9 @@ function rowToRunEvent(row) {
|
|
|
6410
6470
|
function rowToCommit(row) {
|
|
6411
6471
|
return { ...row, files_changed: row.files_changed ? parseJsonArray(row.files_changed) : null };
|
|
6412
6472
|
}
|
|
6473
|
+
function rowToSavedView(row) {
|
|
6474
|
+
return { ...row, filters: parseJsonObject(row.filters) };
|
|
6475
|
+
}
|
|
6413
6476
|
function bridgeStats(data) {
|
|
6414
6477
|
return Object.fromEntries(dataKeys.map((key) => [key, data[key].length]));
|
|
6415
6478
|
}
|
|
@@ -6433,7 +6496,8 @@ function createLocalBridgeBundle(options = {}, db) {
|
|
|
6433
6496
|
task_files: queryByTaskIds(d, "SELECT * FROM task_files WHERE task_id IN (__TASK_IDS__) ORDER BY path, id", taskIds),
|
|
6434
6497
|
task_commits: queryByTaskIds(d, "SELECT * FROM task_commits WHERE task_id IN (__TASK_IDS__) ORDER BY created_at, id", taskIds).map(rowToCommit),
|
|
6435
6498
|
task_git_refs: queryByTaskIds(d, "SELECT * FROM task_git_refs WHERE task_id IN (__TASK_IDS__) ORDER BY created_at, id", taskIds).map(rowWithMetadata),
|
|
6436
|
-
task_verifications: queryByTaskIds(d, "SELECT * FROM task_verifications WHERE task_id IN (__TASK_IDS__) ORDER BY run_at, id", taskIds)
|
|
6499
|
+
task_verifications: queryByTaskIds(d, "SELECT * FROM task_verifications WHERE task_id IN (__TASK_IDS__) ORDER BY run_at, id", taskIds),
|
|
6500
|
+
saved_views: (options.project_id ? d.query("SELECT * FROM saved_search_views WHERE json_extract(filters, '$.project_id') = ? ORDER BY name").all(options.project_id) : d.query("SELECT * FROM saved_search_views ORDER BY name").all()).map(rowToSavedView)
|
|
6437
6501
|
};
|
|
6438
6502
|
const artifactContents = data.run_artifacts.map((artifact) => exportStoredArtifactContent({
|
|
6439
6503
|
id: artifact.id,
|
|
@@ -6472,6 +6536,8 @@ function validateLocalBridgeBundle(value) {
|
|
|
6472
6536
|
issues.push("data must be an object");
|
|
6473
6537
|
} else {
|
|
6474
6538
|
for (const key of dataKeys) {
|
|
6539
|
+
if (key === "saved_views" && data[key] === undefined)
|
|
6540
|
+
continue;
|
|
6475
6541
|
if (!Array.isArray(data[key]))
|
|
6476
6542
|
issues.push(`data.${key} must be an array`);
|
|
6477
6543
|
}
|
|
@@ -6644,7 +6710,8 @@ function importLocalBridgeBundle(bundle, options = {}, db) {
|
|
|
6644
6710
|
const conflictStrategy = options.conflictStrategy ?? "skip";
|
|
6645
6711
|
const data = {
|
|
6646
6712
|
...bundle.data,
|
|
6647
|
-
tasks: sortedTasks(bundle.data.tasks)
|
|
6713
|
+
tasks: sortedTasks(bundle.data.tasks),
|
|
6714
|
+
saved_views: bundle.data.saved_views ?? []
|
|
6648
6715
|
};
|
|
6649
6716
|
for (const key of dataKeys) {
|
|
6650
6717
|
for (const row of data[key]) {
|
|
@@ -7255,6 +7322,10 @@ var MCP_TOOL_GROUPS = {
|
|
|
7255
7322
|
"request_task_review",
|
|
7256
7323
|
"reschedule_task",
|
|
7257
7324
|
"search_tasks",
|
|
7325
|
+
"save_search_view",
|
|
7326
|
+
"list_search_views",
|
|
7327
|
+
"run_search_view",
|
|
7328
|
+
"delete_search_view",
|
|
7258
7329
|
"standup",
|
|
7259
7330
|
"set_task_contract",
|
|
7260
7331
|
"task_context",
|
|
@@ -7444,10 +7515,12 @@ var MCP_TOOL_GROUPS = {
|
|
|
7444
7515
|
"export_template",
|
|
7445
7516
|
"import_template",
|
|
7446
7517
|
"init_templates",
|
|
7518
|
+
"list_template_library",
|
|
7447
7519
|
"list_templates",
|
|
7448
7520
|
"preview_template",
|
|
7449
7521
|
"template_history",
|
|
7450
|
-
"update_template"
|
|
7522
|
+
"update_template",
|
|
7523
|
+
"write_template_library"
|
|
7451
7524
|
],
|
|
7452
7525
|
webhooks: ["create_webhook", "delete_webhook", "list_webhooks"],
|
|
7453
7526
|
machines: [
|
|
@@ -7962,6 +8035,39 @@ var TODOS_CLI_MCP_PARITY = [
|
|
|
7962
8035
|
mcpTool: "create_plan"
|
|
7963
8036
|
}
|
|
7964
8037
|
},
|
|
8038
|
+
{
|
|
8039
|
+
domain: "templates",
|
|
8040
|
+
cliCommands: [
|
|
8041
|
+
"todos template-library",
|
|
8042
|
+
"todos template-init",
|
|
8043
|
+
"todos template-preview",
|
|
8044
|
+
"todos templates --use",
|
|
8045
|
+
"todos template-export",
|
|
8046
|
+
"todos template-import",
|
|
8047
|
+
"todos template-history"
|
|
8048
|
+
],
|
|
8049
|
+
mcpTools: [
|
|
8050
|
+
"list_template_library",
|
|
8051
|
+
"write_template_library",
|
|
8052
|
+
"init_templates",
|
|
8053
|
+
"preview_template",
|
|
8054
|
+
"create_task_from_template",
|
|
8055
|
+
"create_template",
|
|
8056
|
+
"list_templates",
|
|
8057
|
+
"update_template",
|
|
8058
|
+
"delete_template",
|
|
8059
|
+
"export_template",
|
|
8060
|
+
"import_template",
|
|
8061
|
+
"template_history"
|
|
8062
|
+
],
|
|
8063
|
+
jsonContracts: ["template", "task", "structured_error", "api_error"],
|
|
8064
|
+
errorContracts: ["structured_error", "api_error"],
|
|
8065
|
+
status: "matched",
|
|
8066
|
+
example: {
|
|
8067
|
+
cli: "todos template-library --json",
|
|
8068
|
+
mcpTool: "list_template_library"
|
|
8069
|
+
}
|
|
8070
|
+
},
|
|
7965
8071
|
{
|
|
7966
8072
|
domain: "workspace-trust",
|
|
7967
8073
|
cliCommands: [
|
|
@@ -8214,6 +8320,10 @@ var TODOS_CLI_MCP_PARITY = [
|
|
|
8214
8320
|
domain: "search",
|
|
8215
8321
|
cliCommands: [
|
|
8216
8322
|
"todos search",
|
|
8323
|
+
"todos views save",
|
|
8324
|
+
"todos views list",
|
|
8325
|
+
"todos views run",
|
|
8326
|
+
"todos views delete",
|
|
8217
8327
|
"todos status",
|
|
8218
8328
|
"todos recap",
|
|
8219
8329
|
"todos standup",
|
|
@@ -8225,6 +8335,10 @@ var TODOS_CLI_MCP_PARITY = [
|
|
|
8225
8335
|
],
|
|
8226
8336
|
mcpTools: [
|
|
8227
8337
|
"search_tasks",
|
|
8338
|
+
"save_search_view",
|
|
8339
|
+
"list_search_views",
|
|
8340
|
+
"run_search_view",
|
|
8341
|
+
"delete_search_view",
|
|
8228
8342
|
"get_status",
|
|
8229
8343
|
"standup",
|
|
8230
8344
|
"get_task_stats",
|
|
@@ -8233,7 +8347,7 @@ var TODOS_CLI_MCP_PARITY = [
|
|
|
8233
8347
|
"get_task_graph",
|
|
8234
8348
|
"get_recent_activity"
|
|
8235
8349
|
],
|
|
8236
|
-
jsonContracts: ["task", "status_summary", "audit_history", "structured_error", "api_error"],
|
|
8350
|
+
jsonContracts: ["task", "saved_search_view", "saved_search_run_result", "status_summary", "audit_history", "structured_error", "api_error"],
|
|
8237
8351
|
errorContracts: ["structured_error", "api_error"],
|
|
8238
8352
|
status: "matched",
|
|
8239
8353
|
example: {
|
|
@@ -10302,10 +10416,141 @@ function clearActiveModel() {
|
|
|
10302
10416
|
}
|
|
10303
10417
|
// src/db/builtin-templates.ts
|
|
10304
10418
|
init_database();
|
|
10419
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
10420
|
+
import { join as join7 } from "path";
|
|
10421
|
+
var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21";
|
|
10422
|
+
var BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library";
|
|
10305
10423
|
var BUILTIN_TEMPLATES = [
|
|
10424
|
+
{
|
|
10425
|
+
name: "bug-fix",
|
|
10426
|
+
description: "Reproduce, diagnose, fix, test, and release a defect.",
|
|
10427
|
+
category: "bug-fix",
|
|
10428
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10429
|
+
variables: [{ name: "bug", required: true, description: "Bug description" }],
|
|
10430
|
+
tasks: [
|
|
10431
|
+
{ position: 0, title_pattern: "Reproduce: {bug}", priority: "critical", tags: ["bug", "repro"] },
|
|
10432
|
+
{ position: 1, title_pattern: "Diagnose root cause of {bug}", priority: "critical", tags: ["bug", "diagnosis"], depends_on_positions: [0] },
|
|
10433
|
+
{ position: 2, title_pattern: "Write regression test for {bug}", priority: "high", tags: ["bug", "test"], depends_on_positions: [1] },
|
|
10434
|
+
{ position: 3, title_pattern: "Implement fix for {bug}", priority: "critical", tags: ["bug", "implementation"], depends_on_positions: [2] },
|
|
10435
|
+
{ position: 4, title_pattern: "Run full verification for {bug}", priority: "high", tags: ["bug", "verification"], depends_on_positions: [3] },
|
|
10436
|
+
{ position: 5, title_pattern: "Publish and smoke test fix for {bug}", priority: "high", tags: ["bug", "release"], depends_on_positions: [4] }
|
|
10437
|
+
]
|
|
10438
|
+
},
|
|
10439
|
+
{
|
|
10440
|
+
name: "feature-implementation",
|
|
10441
|
+
description: "Plan, build, test, document, and release a product feature.",
|
|
10442
|
+
category: "feature",
|
|
10443
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10444
|
+
variables: [
|
|
10445
|
+
{ name: "feature", required: true, description: "Feature name" },
|
|
10446
|
+
{ name: "scope", required: false, default: "medium", description: "Implementation size or risk" }
|
|
10447
|
+
],
|
|
10448
|
+
tasks: [
|
|
10449
|
+
{ position: 0, title_pattern: "Define acceptance criteria for {feature}", priority: "high", tags: ["feature", "spec"] },
|
|
10450
|
+
{ position: 1, title_pattern: "Design {scope} implementation plan for {feature}", priority: "high", tags: ["feature", "design"], depends_on_positions: [0] },
|
|
10451
|
+
{ position: 2, title_pattern: "Implement {feature}", priority: "critical", tags: ["feature", "implementation"], depends_on_positions: [1] },
|
|
10452
|
+
{ position: 3, title_pattern: "Add tests for {feature}", priority: "high", tags: ["feature", "test"], depends_on_positions: [2] },
|
|
10453
|
+
{ position: 4, title_pattern: "Update docs for {feature}", priority: "medium", tags: ["feature", "docs"], depends_on_positions: [2] },
|
|
10454
|
+
{ position: 5, title_pattern: "Run release checks for {feature}", priority: "high", tags: ["feature", "verification"], depends_on_positions: [3, 4] }
|
|
10455
|
+
]
|
|
10456
|
+
},
|
|
10457
|
+
{
|
|
10458
|
+
name: "security-review",
|
|
10459
|
+
description: "Threat model, test, remediate, and report on security posture.",
|
|
10460
|
+
category: "security",
|
|
10461
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10462
|
+
variables: [{ name: "target", required: true, description: "System, package, or change under review" }],
|
|
10463
|
+
tasks: [
|
|
10464
|
+
{ position: 0, title_pattern: "Map trust boundaries for {target}", priority: "critical", tags: ["security", "threat-model"] },
|
|
10465
|
+
{ position: 1, title_pattern: "Scan {target} for vulnerabilities and secret exposure", priority: "critical", tags: ["security", "scan"], depends_on_positions: [0] },
|
|
10466
|
+
{ position: 2, title_pattern: "Review authz, data access, and dependency risks in {target}", priority: "critical", tags: ["security", "review"], depends_on_positions: [0] },
|
|
10467
|
+
{ position: 3, title_pattern: "Fix critical security findings in {target}", priority: "critical", tags: ["security", "fix"], depends_on_positions: [1, 2] },
|
|
10468
|
+
{ position: 4, title_pattern: "Retest {target} security fixes", priority: "high", tags: ["security", "verification"], depends_on_positions: [3] },
|
|
10469
|
+
{ position: 5, title_pattern: "Write local security review report for {target}", priority: "medium", tags: ["security", "report"], depends_on_positions: [4] }
|
|
10470
|
+
]
|
|
10471
|
+
},
|
|
10472
|
+
{
|
|
10473
|
+
name: "release",
|
|
10474
|
+
description: "Prepare, verify, publish, install, and smoke test a package release.",
|
|
10475
|
+
category: "release",
|
|
10476
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10477
|
+
variables: [
|
|
10478
|
+
{ name: "package", required: true, description: "Package name" },
|
|
10479
|
+
{ name: "version", required: false, default: "patch", description: "Release version or bump type" }
|
|
10480
|
+
],
|
|
10481
|
+
tasks: [
|
|
10482
|
+
{ position: 0, title_pattern: "Prepare {package} {version} release notes", priority: "medium", tags: ["release", "docs"] },
|
|
10483
|
+
{ position: 1, title_pattern: "Run full tests for {package}", priority: "critical", tags: ["release", "test"] },
|
|
10484
|
+
{ position: 2, title_pattern: "Run build and release verification for {package}", priority: "critical", tags: ["release", "verification"], depends_on_positions: [1] },
|
|
10485
|
+
{ position: 3, title_pattern: "Scan {package} release diff for secrets", priority: "critical", tags: ["release", "security"], depends_on_positions: [2] },
|
|
10486
|
+
{ position: 4, title_pattern: "Publish {package} {version}", priority: "high", tags: ["release", "publish"], depends_on_positions: [3] },
|
|
10487
|
+
{ position: 5, title_pattern: "Install and smoke test published {package}", priority: "high", tags: ["release", "smoke"], depends_on_positions: [4] }
|
|
10488
|
+
]
|
|
10489
|
+
},
|
|
10490
|
+
{
|
|
10491
|
+
name: "migration",
|
|
10492
|
+
description: "Plan, test, apply, and verify a schema or data migration.",
|
|
10493
|
+
category: "migration",
|
|
10494
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10495
|
+
variables: [{ name: "migration", required: true, description: "Migration name or objective" }],
|
|
10496
|
+
tasks: [
|
|
10497
|
+
{ position: 0, title_pattern: "Design migration plan for {migration}", priority: "critical", tags: ["migration", "plan"] },
|
|
10498
|
+
{ position: 1, title_pattern: "Write rollback plan for {migration}", priority: "critical", tags: ["migration", "rollback"], depends_on_positions: [0] },
|
|
10499
|
+
{ position: 2, title_pattern: "Implement migration {migration}", priority: "critical", tags: ["migration", "implementation"], depends_on_positions: [0] },
|
|
10500
|
+
{ position: 3, title_pattern: "Test migration {migration} on fixture data", priority: "high", tags: ["migration", "test"], depends_on_positions: [2] },
|
|
10501
|
+
{ position: 4, title_pattern: "Run migration {migration} verification and drift checks", priority: "high", tags: ["migration", "verification"], depends_on_positions: [1, 3] },
|
|
10502
|
+
{ position: 5, title_pattern: "Document migration {migration} evidence", priority: "medium", tags: ["migration", "docs"], depends_on_positions: [4] }
|
|
10503
|
+
]
|
|
10504
|
+
},
|
|
10505
|
+
{
|
|
10506
|
+
name: "incident",
|
|
10507
|
+
description: "Triage, mitigate, repair, verify, and retrospect an incident.",
|
|
10508
|
+
category: "incident",
|
|
10509
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10510
|
+
variables: [{ name: "incident", required: true, description: "Incident summary" }],
|
|
10511
|
+
tasks: [
|
|
10512
|
+
{ position: 0, title_pattern: "Triage incident: {incident}", priority: "critical", tags: ["incident", "triage"] },
|
|
10513
|
+
{ position: 1, title_pattern: "Mitigate customer impact for {incident}", priority: "critical", tags: ["incident", "mitigation"], depends_on_positions: [0] },
|
|
10514
|
+
{ position: 2, title_pattern: "Diagnose root cause for {incident}", priority: "critical", tags: ["incident", "diagnosis"], depends_on_positions: [0] },
|
|
10515
|
+
{ position: 3, title_pattern: "Implement durable repair for {incident}", priority: "critical", tags: ["incident", "repair"], depends_on_positions: [2] },
|
|
10516
|
+
{ position: 4, title_pattern: "Verify recovery from {incident}", priority: "high", tags: ["incident", "verification"], depends_on_positions: [1, 3] },
|
|
10517
|
+
{ position: 5, title_pattern: "Write retrospective for {incident}", priority: "medium", tags: ["incident", "retro"], depends_on_positions: [4] }
|
|
10518
|
+
]
|
|
10519
|
+
},
|
|
10520
|
+
{
|
|
10521
|
+
name: "docs-refresh",
|
|
10522
|
+
description: "Audit, update, validate, and publish documentation changes.",
|
|
10523
|
+
category: "docs",
|
|
10524
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10525
|
+
variables: [{ name: "area", required: true, description: "Documentation area or product surface" }],
|
|
10526
|
+
tasks: [
|
|
10527
|
+
{ position: 0, title_pattern: "Audit current docs for {area}", priority: "medium", tags: ["docs", "audit"] },
|
|
10528
|
+
{ position: 1, title_pattern: "Update examples and commands for {area}", priority: "medium", tags: ["docs", "examples"], depends_on_positions: [0] },
|
|
10529
|
+
{ position: 2, title_pattern: "Validate docs snippets for {area}", priority: "high", tags: ["docs", "verification"], depends_on_positions: [1] },
|
|
10530
|
+
{ position: 3, title_pattern: "Refresh screenshots or generated artifacts for {area}", priority: "medium", tags: ["docs", "assets"], depends_on_positions: [1] },
|
|
10531
|
+
{ position: 4, title_pattern: "Publish docs refresh for {area}", priority: "medium", tags: ["docs", "release"], depends_on_positions: [2, 3] }
|
|
10532
|
+
]
|
|
10533
|
+
},
|
|
10534
|
+
{
|
|
10535
|
+
name: "qa",
|
|
10536
|
+
description: "Build a focused QA plan, execute checks, file defects, and sign off.",
|
|
10537
|
+
category: "qa",
|
|
10538
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10539
|
+
variables: [{ name: "target", required: true, description: "Feature, release, or workflow under QA" }],
|
|
10540
|
+
tasks: [
|
|
10541
|
+
{ position: 0, title_pattern: "Create QA matrix for {target}", priority: "high", tags: ["qa", "plan"] },
|
|
10542
|
+
{ position: 1, title_pattern: "Run happy-path QA for {target}", priority: "high", tags: ["qa", "manual"], depends_on_positions: [0] },
|
|
10543
|
+
{ position: 2, title_pattern: "Run edge-case QA for {target}", priority: "high", tags: ["qa", "edge-cases"], depends_on_positions: [0] },
|
|
10544
|
+
{ position: 3, title_pattern: "File and dedupe QA defects for {target}", priority: "medium", tags: ["qa", "bugs"], depends_on_positions: [1, 2] },
|
|
10545
|
+
{ position: 4, title_pattern: "Verify QA fixes for {target}", priority: "high", tags: ["qa", "verification"], depends_on_positions: [3] },
|
|
10546
|
+
{ position: 5, title_pattern: "Record QA signoff for {target}", priority: "medium", tags: ["qa", "signoff"], depends_on_positions: [4] }
|
|
10547
|
+
]
|
|
10548
|
+
},
|
|
10306
10549
|
{
|
|
10307
10550
|
name: "open-source-project",
|
|
10308
10551
|
description: "Full open-source project bootstrap \u2014 scaffold to publish",
|
|
10552
|
+
category: "open-source",
|
|
10553
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10309
10554
|
variables: [
|
|
10310
10555
|
{ name: "name", required: true, description: "Service name" },
|
|
10311
10556
|
{ name: "org", required: false, default: "hasna", description: "GitHub org" }
|
|
@@ -10325,47 +10570,73 @@ var BUILTIN_TEMPLATES = [
|
|
|
10325
10570
|
{ position: 11, title_pattern: "Publish @hasna/{name} to npm", priority: "high", depends_on_positions: [6, 7, 8] },
|
|
10326
10571
|
{ position: 12, title_pattern: "Install @hasna/{name} globally and verify", priority: "medium", depends_on_positions: [11] }
|
|
10327
10572
|
]
|
|
10328
|
-
},
|
|
10329
|
-
{
|
|
10330
|
-
name: "bug-fix",
|
|
10331
|
-
description: "Standard bug fix workflow",
|
|
10332
|
-
variables: [{ name: "bug", required: true, description: "Bug description" }],
|
|
10333
|
-
tasks: [
|
|
10334
|
-
{ position: 0, title_pattern: "Reproduce: {bug}", priority: "critical" },
|
|
10335
|
-
{ position: 1, title_pattern: "Diagnose root cause of {bug}", priority: "critical", depends_on_positions: [0] },
|
|
10336
|
-
{ position: 2, title_pattern: "Implement fix for {bug}", priority: "critical", depends_on_positions: [1] },
|
|
10337
|
-
{ position: 3, title_pattern: "Write regression test for {bug}", priority: "high", depends_on_positions: [2] },
|
|
10338
|
-
{ position: 4, title_pattern: "Publish fix and verify in production", priority: "high", depends_on_positions: [3] }
|
|
10339
|
-
]
|
|
10340
|
-
},
|
|
10341
|
-
{
|
|
10342
|
-
name: "feature",
|
|
10343
|
-
description: "Standard feature development workflow",
|
|
10344
|
-
variables: [{ name: "feature", required: true }, { name: "scope", required: false, default: "medium" }],
|
|
10345
|
-
tasks: [
|
|
10346
|
-
{ position: 0, title_pattern: "Write spec for {feature}", priority: "high" },
|
|
10347
|
-
{ position: 1, title_pattern: "Design implementation approach for {feature}", priority: "high", depends_on_positions: [0] },
|
|
10348
|
-
{ position: 2, title_pattern: "Implement {feature}", priority: "critical", depends_on_positions: [1] },
|
|
10349
|
-
{ position: 3, title_pattern: "Write tests for {feature}", priority: "high", depends_on_positions: [2] },
|
|
10350
|
-
{ position: 4, title_pattern: "Code review for {feature}", priority: "medium", depends_on_positions: [3] },
|
|
10351
|
-
{ position: 5, title_pattern: "Update docs for {feature}", priority: "medium", depends_on_positions: [2] },
|
|
10352
|
-
{ position: 6, title_pattern: "Deploy {feature}", priority: "high", depends_on_positions: [4] }
|
|
10353
|
-
]
|
|
10354
|
-
},
|
|
10355
|
-
{
|
|
10356
|
-
name: "security-audit",
|
|
10357
|
-
description: "Security audit workflow",
|
|
10358
|
-
variables: [{ name: "target", required: true }],
|
|
10359
|
-
tasks: [
|
|
10360
|
-
{ position: 0, title_pattern: "Scan {target} for vulnerabilities", priority: "critical" },
|
|
10361
|
-
{ position: 1, title_pattern: "Review {target} security findings", priority: "critical", depends_on_positions: [0] },
|
|
10362
|
-
{ position: 2, title_pattern: "Fix critical issues in {target}", priority: "critical", depends_on_positions: [1] },
|
|
10363
|
-
{ position: 3, title_pattern: "Retest {target} after fixes", priority: "high", depends_on_positions: [2] },
|
|
10364
|
-
{ position: 4, title_pattern: "Write security report for {target}", priority: "medium", depends_on_positions: [3] },
|
|
10365
|
-
{ position: 5, title_pattern: "Close audit for {target}", priority: "low", depends_on_positions: [4] }
|
|
10366
|
-
]
|
|
10367
10573
|
}
|
|
10368
10574
|
];
|
|
10575
|
+
function templateMetadata(template) {
|
|
10576
|
+
return {
|
|
10577
|
+
source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
|
|
10578
|
+
library_version: template.version,
|
|
10579
|
+
category: template.category,
|
|
10580
|
+
template_file: `${template.name}.json`,
|
|
10581
|
+
local_only: true,
|
|
10582
|
+
marketplace_free: true
|
|
10583
|
+
};
|
|
10584
|
+
}
|
|
10585
|
+
function listBuiltinTemplates() {
|
|
10586
|
+
return BUILTIN_TEMPLATES.map((template) => ({
|
|
10587
|
+
...template,
|
|
10588
|
+
variables: template.variables.map((variable) => ({ ...variable })),
|
|
10589
|
+
tasks: template.tasks.map((task) => ({ ...task, tags: task.tags ? [...task.tags] : undefined }))
|
|
10590
|
+
}));
|
|
10591
|
+
}
|
|
10592
|
+
function getBuiltinTemplate(name) {
|
|
10593
|
+
return listBuiltinTemplates().find((template) => template.name === name) ?? null;
|
|
10594
|
+
}
|
|
10595
|
+
function exportBuiltinTemplate(name) {
|
|
10596
|
+
const template = getBuiltinTemplate(name);
|
|
10597
|
+
if (!template)
|
|
10598
|
+
throw new Error(`Built-in template not found: ${name}`);
|
|
10599
|
+
return {
|
|
10600
|
+
name: template.name,
|
|
10601
|
+
title_pattern: `${template.name}: {${template.variables[0]?.name ?? "name"}}`,
|
|
10602
|
+
description: template.description,
|
|
10603
|
+
priority: "medium",
|
|
10604
|
+
tags: [template.category, "local-template"],
|
|
10605
|
+
variables: template.variables,
|
|
10606
|
+
project_id: null,
|
|
10607
|
+
plan_id: null,
|
|
10608
|
+
metadata: templateMetadata(template),
|
|
10609
|
+
tasks: template.tasks.map((task) => ({
|
|
10610
|
+
position: task.position,
|
|
10611
|
+
title_pattern: task.title_pattern,
|
|
10612
|
+
description: task.description ?? null,
|
|
10613
|
+
priority: task.priority ?? "medium",
|
|
10614
|
+
tags: task.tags ?? [template.category],
|
|
10615
|
+
task_type: task.task_type ?? null,
|
|
10616
|
+
condition: task.condition ?? null,
|
|
10617
|
+
include_template_id: task.include_template_id ?? null,
|
|
10618
|
+
depends_on_positions: task.depends_on_positions ?? task.depends_on ?? [],
|
|
10619
|
+
metadata: task.metadata ?? { category: template.category }
|
|
10620
|
+
}))
|
|
10621
|
+
};
|
|
10622
|
+
}
|
|
10623
|
+
function exportBuiltinTemplateFiles() {
|
|
10624
|
+
return BUILTIN_TEMPLATES.map((template) => ({
|
|
10625
|
+
filename: `${template.name}.json`,
|
|
10626
|
+
template: exportBuiltinTemplate(template.name)
|
|
10627
|
+
}));
|
|
10628
|
+
}
|
|
10629
|
+
function writeBuiltinTemplateFiles(directory) {
|
|
10630
|
+
mkdirSync6(directory, { recursive: true });
|
|
10631
|
+
const files = [];
|
|
10632
|
+
for (const entry of exportBuiltinTemplateFiles()) {
|
|
10633
|
+
const path = join7(directory, entry.filename);
|
|
10634
|
+
writeFileSync4(path, `${JSON.stringify(entry.template, null, 2)}
|
|
10635
|
+
`, "utf-8");
|
|
10636
|
+
files.push(path);
|
|
10637
|
+
}
|
|
10638
|
+
return { directory, written: files.length, files };
|
|
10639
|
+
}
|
|
10369
10640
|
function initBuiltinTemplates(db) {
|
|
10370
10641
|
const d = db || getDatabase();
|
|
10371
10642
|
const existing = listTemplates(d);
|
|
@@ -10391,7 +10662,9 @@ function initBuiltinTemplates(db) {
|
|
|
10391
10662
|
name: bt.name,
|
|
10392
10663
|
title_pattern: `${bt.name}: {${bt.variables[0]?.name || "name"}}`,
|
|
10393
10664
|
description: bt.description,
|
|
10665
|
+
tags: [bt.category, "local-template"],
|
|
10394
10666
|
variables: bt.variables,
|
|
10667
|
+
metadata: templateMetadata(bt),
|
|
10395
10668
|
tasks
|
|
10396
10669
|
}, d);
|
|
10397
10670
|
created++;
|
|
@@ -11359,17 +11632,437 @@ function searchTasks(options, projectId, taskListId, db) {
|
|
|
11359
11632
|
const rows = d.query(sql).all(...params);
|
|
11360
11633
|
return rows.map(rowToTask4);
|
|
11361
11634
|
}
|
|
11635
|
+
// src/lib/saved-search-views.ts
|
|
11636
|
+
init_database();
|
|
11637
|
+
|
|
11638
|
+
// src/lib/local-fields.ts
|
|
11639
|
+
init_database();
|
|
11640
|
+
init_types();
|
|
11641
|
+
var LOCAL_FIELDS_KEY = "local_fields";
|
|
11642
|
+
function normalizeList(values) {
|
|
11643
|
+
return [...new Set((values || []).map((value) => value.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
11644
|
+
}
|
|
11645
|
+
function metadataFields(task) {
|
|
11646
|
+
const value = task.metadata[LOCAL_FIELDS_KEY];
|
|
11647
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
11648
|
+
}
|
|
11649
|
+
function sameCustomValue(actual, expected) {
|
|
11650
|
+
return JSON.stringify(actual) === JSON.stringify(expected);
|
|
11651
|
+
}
|
|
11652
|
+
function hasOwnField(fields, key) {
|
|
11653
|
+
return Object.prototype.hasOwnProperty.call(fields, key);
|
|
11654
|
+
}
|
|
11655
|
+
function getTaskLocalFields(taskId, db) {
|
|
11656
|
+
const d = db || getDatabase();
|
|
11657
|
+
const task = getTask(taskId, d);
|
|
11658
|
+
if (!task)
|
|
11659
|
+
throw new TaskNotFoundError(taskId);
|
|
11660
|
+
const fields = metadataFields(task);
|
|
11661
|
+
return {
|
|
11662
|
+
labels: normalizeList(fields.labels),
|
|
11663
|
+
priority: task.priority,
|
|
11664
|
+
severity: typeof fields.severity === "string" ? fields.severity : null,
|
|
11665
|
+
owner: hasOwnField(fields, "owner") ? typeof fields.owner === "string" ? fields.owner : null : task.assigned_to,
|
|
11666
|
+
area: typeof fields.area === "string" ? fields.area : null,
|
|
11667
|
+
custom: fields.custom && typeof fields.custom === "object" && !Array.isArray(fields.custom) ? fields.custom : {}
|
|
11668
|
+
};
|
|
11669
|
+
}
|
|
11670
|
+
function setTaskLocalFields(taskId, input, db) {
|
|
11671
|
+
const d = db || getDatabase();
|
|
11672
|
+
const task = getTask(taskId, d);
|
|
11673
|
+
if (!task)
|
|
11674
|
+
throw new TaskNotFoundError(taskId);
|
|
11675
|
+
const currentFields = getTaskLocalFields(taskId, d);
|
|
11676
|
+
const labels = input.labels !== undefined ? normalizeList(input.labels) : currentFields.labels;
|
|
11677
|
+
const custom = input.custom !== undefined ? redactValue(input.merge_custom === false ? input.custom : { ...currentFields.custom, ...input.custom }) : currentFields.custom;
|
|
11678
|
+
const nextFields = {
|
|
11679
|
+
labels,
|
|
11680
|
+
priority: input.priority || task.priority,
|
|
11681
|
+
severity: input.severity !== undefined ? input.severity : currentFields.severity,
|
|
11682
|
+
owner: input.owner !== undefined ? input.owner : currentFields.owner,
|
|
11683
|
+
area: input.area !== undefined ? input.area : currentFields.area,
|
|
11684
|
+
custom
|
|
11685
|
+
};
|
|
11686
|
+
const nextMetadata = {
|
|
11687
|
+
...task.metadata,
|
|
11688
|
+
[LOCAL_FIELDS_KEY]: nextFields
|
|
11689
|
+
};
|
|
11690
|
+
const previousLabels = new Set(currentFields.labels);
|
|
11691
|
+
const nextTags = normalizeList([...task.tags.filter((tag) => !previousLabels.has(tag)), ...labels]);
|
|
11692
|
+
const updates = {
|
|
11693
|
+
version: task.version,
|
|
11694
|
+
priority: input.priority,
|
|
11695
|
+
tags: nextTags,
|
|
11696
|
+
metadata: nextMetadata
|
|
11697
|
+
};
|
|
11698
|
+
if (input.owner !== undefined)
|
|
11699
|
+
updates.assigned_to = nextFields.owner;
|
|
11700
|
+
return updateTask(taskId, updates, d);
|
|
11701
|
+
}
|
|
11702
|
+
function queryTasksByLocalFields(query, db) {
|
|
11703
|
+
const d = db || getDatabase();
|
|
11704
|
+
const tasks = listTasks({
|
|
11705
|
+
priority: query.priority,
|
|
11706
|
+
tags: query.labels,
|
|
11707
|
+
limit: 1e4
|
|
11708
|
+
}, d);
|
|
11709
|
+
const matches = tasks.filter((task) => {
|
|
11710
|
+
const fields = getTaskLocalFields(task.id, d);
|
|
11711
|
+
if (query.labels && !query.labels.every((label) => fields.labels.includes(label)))
|
|
11712
|
+
return false;
|
|
11713
|
+
if (query.severity && fields.severity !== query.severity)
|
|
11714
|
+
return false;
|
|
11715
|
+
if (query.owner && fields.owner !== query.owner)
|
|
11716
|
+
return false;
|
|
11717
|
+
if (query.area && fields.area !== query.area)
|
|
11718
|
+
return false;
|
|
11719
|
+
if (query.custom) {
|
|
11720
|
+
for (const [key, expected] of Object.entries(query.custom)) {
|
|
11721
|
+
if (!sameCustomValue(fields.custom[key], expected))
|
|
11722
|
+
return false;
|
|
11723
|
+
}
|
|
11724
|
+
}
|
|
11725
|
+
return true;
|
|
11726
|
+
});
|
|
11727
|
+
return matches.slice(0, query.limit || 100);
|
|
11728
|
+
}
|
|
11729
|
+
|
|
11730
|
+
// src/lib/saved-search-views.ts
|
|
11731
|
+
function parseFilters(value) {
|
|
11732
|
+
if (!value)
|
|
11733
|
+
return {};
|
|
11734
|
+
try {
|
|
11735
|
+
const parsed = JSON.parse(value);
|
|
11736
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
11737
|
+
} catch {
|
|
11738
|
+
return {};
|
|
11739
|
+
}
|
|
11740
|
+
}
|
|
11741
|
+
function rowToSavedSearchView(row) {
|
|
11742
|
+
return {
|
|
11743
|
+
...row,
|
|
11744
|
+
scope: normalizeScope(row.scope),
|
|
11745
|
+
filters: parseFilters(row.filters)
|
|
11746
|
+
};
|
|
11747
|
+
}
|
|
11748
|
+
function normalizeScope(scope) {
|
|
11749
|
+
if (scope === "all" || scope === "tasks" || scope === "projects" || scope === "plans" || scope === "runs" || scope === "comments") {
|
|
11750
|
+
return scope;
|
|
11751
|
+
}
|
|
11752
|
+
return "tasks";
|
|
11753
|
+
}
|
|
11754
|
+
function normalizeName(name) {
|
|
11755
|
+
const normalized = name.trim();
|
|
11756
|
+
if (!normalized)
|
|
11757
|
+
throw new Error("Saved view name is required");
|
|
11758
|
+
return normalized;
|
|
11759
|
+
}
|
|
11760
|
+
function normalizeLimit(limit) {
|
|
11761
|
+
if (!Number.isFinite(limit) || !limit || limit <= 0)
|
|
11762
|
+
return 100;
|
|
11763
|
+
return Math.min(Math.trunc(limit), 1000);
|
|
11764
|
+
}
|
|
11765
|
+
function valuesList(value) {
|
|
11766
|
+
if (value === undefined)
|
|
11767
|
+
return [];
|
|
11768
|
+
return (Array.isArray(value) ? value : [value]).map((item) => item.trim()).filter(Boolean);
|
|
11769
|
+
}
|
|
11770
|
+
function addStatusFilter(sql, params, column, value) {
|
|
11771
|
+
const values = valuesList(value);
|
|
11772
|
+
if (values.length === 0)
|
|
11773
|
+
return sql;
|
|
11774
|
+
params.push(...values);
|
|
11775
|
+
return `${sql} AND ${column} IN (${values.map(() => "?").join(",")})`;
|
|
11776
|
+
}
|
|
11777
|
+
function addDateFilter(sql, params, column, value) {
|
|
11778
|
+
if (!value)
|
|
11779
|
+
return sql;
|
|
11780
|
+
params.push(value);
|
|
11781
|
+
return `${sql} AND ${column} > ?`;
|
|
11782
|
+
}
|
|
11783
|
+
function likePattern(query) {
|
|
11784
|
+
const trimmed = query?.trim();
|
|
11785
|
+
if (!trimmed || trimmed === "*")
|
|
11786
|
+
return null;
|
|
11787
|
+
return `%${trimmed}%`;
|
|
11788
|
+
}
|
|
11789
|
+
function parseJsonObject2(value) {
|
|
11790
|
+
if (!value)
|
|
11791
|
+
return {};
|
|
11792
|
+
if (typeof value === "object" && !Array.isArray(value))
|
|
11793
|
+
return value;
|
|
11794
|
+
if (typeof value !== "string")
|
|
11795
|
+
return {};
|
|
11796
|
+
try {
|
|
11797
|
+
const parsed = JSON.parse(value);
|
|
11798
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
11799
|
+
} catch {
|
|
11800
|
+
return {};
|
|
11801
|
+
}
|
|
11802
|
+
}
|
|
11803
|
+
function rowToTaskRun(row) {
|
|
11804
|
+
return { ...row, metadata: parseJsonObject2(row.metadata) };
|
|
11805
|
+
}
|
|
11806
|
+
function taskMatchesSavedFilters(task, filters, db) {
|
|
11807
|
+
if (filters.plan_id && task.plan_id !== filters.plan_id)
|
|
11808
|
+
return false;
|
|
11809
|
+
if (filters.tags && !filters.tags.every((tag) => task.tags.includes(tag)))
|
|
11810
|
+
return false;
|
|
11811
|
+
if (filters.depends_on) {
|
|
11812
|
+
const row = db.query("SELECT 1 FROM task_dependencies WHERE task_id = ? AND depends_on = ?").get(task.id, filters.depends_on);
|
|
11813
|
+
if (!row)
|
|
11814
|
+
return false;
|
|
11815
|
+
}
|
|
11816
|
+
if (filters.blocks) {
|
|
11817
|
+
const row = db.query("SELECT 1 FROM task_dependencies WHERE task_id = ? AND depends_on = ?").get(filters.blocks, task.id);
|
|
11818
|
+
if (!row)
|
|
11819
|
+
return false;
|
|
11820
|
+
}
|
|
11821
|
+
return true;
|
|
11822
|
+
}
|
|
11823
|
+
function searchTaskEntities(filters, db) {
|
|
11824
|
+
let tasks;
|
|
11825
|
+
if (filters.local_fields) {
|
|
11826
|
+
const localMatches = queryTasksByLocalFields({ ...filters.local_fields, limit: 1e4 }, db);
|
|
11827
|
+
const allowedIds = new Set(localMatches.map((task) => task.id));
|
|
11828
|
+
tasks = searchTasks({
|
|
11829
|
+
query: filters.query,
|
|
11830
|
+
project_id: filters.project_id,
|
|
11831
|
+
task_list_id: filters.task_list_id,
|
|
11832
|
+
status: filters.status,
|
|
11833
|
+
priority: filters.priority,
|
|
11834
|
+
assigned_to: filters.assigned_to,
|
|
11835
|
+
agent_id: filters.agent_id,
|
|
11836
|
+
created_after: filters.created_after,
|
|
11837
|
+
updated_after: filters.updated_after,
|
|
11838
|
+
has_dependencies: filters.has_dependencies,
|
|
11839
|
+
is_blocked: filters.is_blocked
|
|
11840
|
+
}, undefined, undefined, db).filter((task) => allowedIds.has(task.id));
|
|
11841
|
+
} else {
|
|
11842
|
+
tasks = searchTasks({
|
|
11843
|
+
query: filters.query,
|
|
11844
|
+
project_id: filters.project_id,
|
|
11845
|
+
task_list_id: filters.task_list_id,
|
|
11846
|
+
status: filters.status,
|
|
11847
|
+
priority: filters.priority,
|
|
11848
|
+
assigned_to: filters.assigned_to,
|
|
11849
|
+
agent_id: filters.agent_id,
|
|
11850
|
+
created_after: filters.created_after,
|
|
11851
|
+
updated_after: filters.updated_after,
|
|
11852
|
+
has_dependencies: filters.has_dependencies,
|
|
11853
|
+
is_blocked: filters.is_blocked
|
|
11854
|
+
}, undefined, undefined, db);
|
|
11855
|
+
}
|
|
11856
|
+
return tasks.filter((task) => taskMatchesSavedFilters(task, filters, db)).slice(0, normalizeLimit(filters.limit));
|
|
11857
|
+
}
|
|
11858
|
+
function searchProjects(filters, db) {
|
|
11859
|
+
const params = [];
|
|
11860
|
+
let sql = "SELECT * FROM projects WHERE 1=1";
|
|
11861
|
+
if (filters.project_id) {
|
|
11862
|
+
sql += " AND id = ?";
|
|
11863
|
+
params.push(filters.project_id);
|
|
11864
|
+
}
|
|
11865
|
+
const pattern = likePattern(filters.query);
|
|
11866
|
+
if (pattern) {
|
|
11867
|
+
sql += " AND (name LIKE ? OR description LIKE ? OR path LIKE ?)";
|
|
11868
|
+
params.push(pattern, pattern, pattern);
|
|
11869
|
+
}
|
|
11870
|
+
sql = addDateFilter(sql, params, "created_at", filters.created_after);
|
|
11871
|
+
sql = addDateFilter(sql, params, "updated_at", filters.updated_after);
|
|
11872
|
+
sql += " ORDER BY name LIMIT ?";
|
|
11873
|
+
params.push(normalizeLimit(filters.limit));
|
|
11874
|
+
return db.query(sql).all(...params);
|
|
11875
|
+
}
|
|
11876
|
+
function searchPlans(filters, db) {
|
|
11877
|
+
const params = [];
|
|
11878
|
+
let sql = "SELECT * FROM plans WHERE 1=1";
|
|
11879
|
+
if (filters.project_id) {
|
|
11880
|
+
sql += " AND project_id = ?";
|
|
11881
|
+
params.push(filters.project_id);
|
|
11882
|
+
}
|
|
11883
|
+
if (filters.task_list_id) {
|
|
11884
|
+
sql += " AND task_list_id = ?";
|
|
11885
|
+
params.push(filters.task_list_id);
|
|
11886
|
+
}
|
|
11887
|
+
if (filters.agent_id) {
|
|
11888
|
+
sql += " AND agent_id = ?";
|
|
11889
|
+
params.push(filters.agent_id);
|
|
11890
|
+
}
|
|
11891
|
+
sql = addStatusFilter(sql, params, "status", filters.status);
|
|
11892
|
+
const pattern = likePattern(filters.query);
|
|
11893
|
+
if (pattern) {
|
|
11894
|
+
sql += " AND (name LIKE ? OR description LIKE ?)";
|
|
11895
|
+
params.push(pattern, pattern);
|
|
11896
|
+
}
|
|
11897
|
+
sql = addDateFilter(sql, params, "created_at", filters.created_after);
|
|
11898
|
+
sql = addDateFilter(sql, params, "updated_at", filters.updated_after);
|
|
11899
|
+
sql += " ORDER BY updated_at DESC, created_at DESC LIMIT ?";
|
|
11900
|
+
params.push(normalizeLimit(filters.limit));
|
|
11901
|
+
return db.query(sql).all(...params);
|
|
11902
|
+
}
|
|
11903
|
+
function searchRuns(filters, db) {
|
|
11904
|
+
const params = [];
|
|
11905
|
+
let sql = `SELECT r.* FROM task_runs r
|
|
11906
|
+
JOIN tasks t ON t.id = r.task_id
|
|
11907
|
+
WHERE 1=1`;
|
|
11908
|
+
if (filters.project_id) {
|
|
11909
|
+
sql += " AND t.project_id = ?";
|
|
11910
|
+
params.push(filters.project_id);
|
|
11911
|
+
}
|
|
11912
|
+
if (filters.task_list_id) {
|
|
11913
|
+
sql += " AND t.task_list_id = ?";
|
|
11914
|
+
params.push(filters.task_list_id);
|
|
11915
|
+
}
|
|
11916
|
+
if (filters.plan_id) {
|
|
11917
|
+
sql += " AND t.plan_id = ?";
|
|
11918
|
+
params.push(filters.plan_id);
|
|
11919
|
+
}
|
|
11920
|
+
if (filters.task_id) {
|
|
11921
|
+
sql += " AND r.task_id = ?";
|
|
11922
|
+
params.push(filters.task_id);
|
|
11923
|
+
}
|
|
11924
|
+
if (filters.agent_id) {
|
|
11925
|
+
sql += " AND r.agent_id = ?";
|
|
11926
|
+
params.push(filters.agent_id);
|
|
11927
|
+
}
|
|
11928
|
+
sql = addStatusFilter(sql, params, "r.status", filters.status);
|
|
11929
|
+
const pattern = likePattern(filters.query);
|
|
11930
|
+
if (pattern) {
|
|
11931
|
+
sql += " AND (r.title LIKE ? OR r.summary LIKE ? OR t.title LIKE ?)";
|
|
11932
|
+
params.push(pattern, pattern, pattern);
|
|
11933
|
+
}
|
|
11934
|
+
sql = addDateFilter(sql, params, "r.created_at", filters.created_after);
|
|
11935
|
+
sql = addDateFilter(sql, params, "r.updated_at", filters.updated_after);
|
|
11936
|
+
sql += " ORDER BY r.started_at DESC, r.created_at DESC LIMIT ?";
|
|
11937
|
+
params.push(normalizeLimit(filters.limit));
|
|
11938
|
+
return db.query(sql).all(...params).map(rowToTaskRun);
|
|
11939
|
+
}
|
|
11940
|
+
function searchComments(filters, db) {
|
|
11941
|
+
const params = [];
|
|
11942
|
+
let sql = `SELECT c.* FROM task_comments c
|
|
11943
|
+
JOIN tasks t ON t.id = c.task_id
|
|
11944
|
+
WHERE 1=1`;
|
|
11945
|
+
if (filters.project_id) {
|
|
11946
|
+
sql += " AND t.project_id = ?";
|
|
11947
|
+
params.push(filters.project_id);
|
|
11948
|
+
}
|
|
11949
|
+
if (filters.task_list_id) {
|
|
11950
|
+
sql += " AND t.task_list_id = ?";
|
|
11951
|
+
params.push(filters.task_list_id);
|
|
11952
|
+
}
|
|
11953
|
+
if (filters.plan_id) {
|
|
11954
|
+
sql += " AND t.plan_id = ?";
|
|
11955
|
+
params.push(filters.plan_id);
|
|
11956
|
+
}
|
|
11957
|
+
if (filters.task_id) {
|
|
11958
|
+
sql += " AND c.task_id = ?";
|
|
11959
|
+
params.push(filters.task_id);
|
|
11960
|
+
}
|
|
11961
|
+
if (filters.agent_id) {
|
|
11962
|
+
sql += " AND c.agent_id = ?";
|
|
11963
|
+
params.push(filters.agent_id);
|
|
11964
|
+
}
|
|
11965
|
+
const pattern = likePattern(filters.query);
|
|
11966
|
+
if (pattern) {
|
|
11967
|
+
sql += " AND (c.content LIKE ? OR t.title LIKE ?)";
|
|
11968
|
+
params.push(pattern, pattern);
|
|
11969
|
+
}
|
|
11970
|
+
sql = addDateFilter(sql, params, "c.created_at", filters.created_after);
|
|
11971
|
+
sql += " ORDER BY c.created_at DESC, c.id LIMIT ?";
|
|
11972
|
+
params.push(normalizeLimit(filters.limit));
|
|
11973
|
+
return db.query(sql).all(...params);
|
|
11974
|
+
}
|
|
11975
|
+
function toResults(entityType, rows) {
|
|
11976
|
+
return rows.map((entity) => ({ entity_type: entityType, entity }));
|
|
11977
|
+
}
|
|
11978
|
+
function runSavedSearch(filters = {}, scope = "tasks", db) {
|
|
11979
|
+
const d = db || getDatabase();
|
|
11980
|
+
const normalizedScope = normalizeScope(scope);
|
|
11981
|
+
const scopes = normalizedScope === "all" ? ["tasks", "projects", "plans", "runs", "comments"] : [normalizedScope];
|
|
11982
|
+
const results = [];
|
|
11983
|
+
for (const item of scopes) {
|
|
11984
|
+
if (item === "tasks")
|
|
11985
|
+
results.push(...toResults("tasks", searchTaskEntities(filters, d)));
|
|
11986
|
+
if (item === "projects")
|
|
11987
|
+
results.push(...toResults("projects", searchProjects(filters, d)));
|
|
11988
|
+
if (item === "plans")
|
|
11989
|
+
results.push(...toResults("plans", searchPlans(filters, d)));
|
|
11990
|
+
if (item === "runs")
|
|
11991
|
+
results.push(...toResults("runs", searchRuns(filters, d)));
|
|
11992
|
+
if (item === "comments")
|
|
11993
|
+
results.push(...toResults("comments", searchComments(filters, d)));
|
|
11994
|
+
}
|
|
11995
|
+
return {
|
|
11996
|
+
scope: normalizedScope,
|
|
11997
|
+
filters,
|
|
11998
|
+
count: results.length,
|
|
11999
|
+
results: results.slice(0, normalizeLimit(filters.limit))
|
|
12000
|
+
};
|
|
12001
|
+
}
|
|
12002
|
+
function saveSearchView(input, db) {
|
|
12003
|
+
const d = db || getDatabase();
|
|
12004
|
+
const name = normalizeName(input.name);
|
|
12005
|
+
const timestamp = now();
|
|
12006
|
+
const existing = getSearchView(name, d);
|
|
12007
|
+
if (existing) {
|
|
12008
|
+
d.run(`UPDATE saved_search_views
|
|
12009
|
+
SET description = ?, scope = ?, filters = ?, updated_at = ?
|
|
12010
|
+
WHERE id = ?`, [
|
|
12011
|
+
input.description ?? existing.description,
|
|
12012
|
+
normalizeScope(input.scope ?? existing.scope),
|
|
12013
|
+
JSON.stringify(input.filters ?? existing.filters),
|
|
12014
|
+
timestamp,
|
|
12015
|
+
existing.id
|
|
12016
|
+
]);
|
|
12017
|
+
return getSearchView(existing.id, d);
|
|
12018
|
+
}
|
|
12019
|
+
const id = uuid();
|
|
12020
|
+
d.run(`INSERT INTO saved_search_views (id, name, description, scope, filters, created_at, updated_at)
|
|
12021
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [
|
|
12022
|
+
id,
|
|
12023
|
+
name,
|
|
12024
|
+
input.description ?? null,
|
|
12025
|
+
normalizeScope(input.scope),
|
|
12026
|
+
JSON.stringify(input.filters ?? {}),
|
|
12027
|
+
timestamp,
|
|
12028
|
+
timestamp
|
|
12029
|
+
]);
|
|
12030
|
+
return getSearchView(id, d);
|
|
12031
|
+
}
|
|
12032
|
+
function getSearchView(idOrName, db) {
|
|
12033
|
+
const d = db || getDatabase();
|
|
12034
|
+
const row = d.query("SELECT * FROM saved_search_views WHERE id = ? OR name = ?").get(idOrName, idOrName);
|
|
12035
|
+
return row ? rowToSavedSearchView(row) : null;
|
|
12036
|
+
}
|
|
12037
|
+
function listSearchViews(scope, db) {
|
|
12038
|
+
const d = db || getDatabase();
|
|
12039
|
+
const normalizedScope = scope ? normalizeScope(scope) : null;
|
|
12040
|
+
const rows = normalizedScope ? d.query("SELECT * FROM saved_search_views WHERE scope = ? ORDER BY name").all(normalizedScope) : d.query("SELECT * FROM saved_search_views ORDER BY name").all();
|
|
12041
|
+
return rows.map(rowToSavedSearchView);
|
|
12042
|
+
}
|
|
12043
|
+
function deleteSearchView(idOrName, db) {
|
|
12044
|
+
const d = db || getDatabase();
|
|
12045
|
+
const result = d.run("DELETE FROM saved_search_views WHERE id = ? OR name = ?", [idOrName, idOrName]);
|
|
12046
|
+
return result.changes > 0;
|
|
12047
|
+
}
|
|
12048
|
+
function runSearchView(idOrName, db) {
|
|
12049
|
+
const d = db || getDatabase();
|
|
12050
|
+
const view = getSearchView(idOrName, d);
|
|
12051
|
+
if (!view)
|
|
12052
|
+
throw new Error(`Saved search view not found: ${idOrName}`);
|
|
12053
|
+
return { ...runSavedSearch(view.filters, view.scope, d), view };
|
|
12054
|
+
}
|
|
11362
12055
|
// src/lib/claude-tasks.ts
|
|
11363
|
-
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync2, writeFileSync as
|
|
11364
|
-
import { join as
|
|
12056
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
12057
|
+
import { join as join8 } from "path";
|
|
11365
12058
|
function getTaskListDir(taskListId) {
|
|
11366
|
-
return
|
|
12059
|
+
return join8(HOME, ".claude", "tasks", taskListId);
|
|
11367
12060
|
}
|
|
11368
12061
|
function readClaudeTask(dir, filename) {
|
|
11369
|
-
return readJsonFile(
|
|
12062
|
+
return readJsonFile(join8(dir, filename));
|
|
11370
12063
|
}
|
|
11371
12064
|
function writeClaudeTask(dir, task) {
|
|
11372
|
-
writeJsonFile(
|
|
12065
|
+
writeJsonFile(join8(dir, `${task.id}.json`), task);
|
|
11373
12066
|
}
|
|
11374
12067
|
function toClaudeStatus(status) {
|
|
11375
12068
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -11381,14 +12074,14 @@ function toSqliteStatus(status) {
|
|
|
11381
12074
|
return status;
|
|
11382
12075
|
}
|
|
11383
12076
|
function readPrefixCounter(dir) {
|
|
11384
|
-
const path =
|
|
12077
|
+
const path = join8(dir, ".prefix-counter");
|
|
11385
12078
|
if (!existsSync8(path))
|
|
11386
12079
|
return 0;
|
|
11387
12080
|
const val = parseInt(readFileSync6(path, "utf-8").trim(), 10);
|
|
11388
12081
|
return isNaN(val) ? 0 : val;
|
|
11389
12082
|
}
|
|
11390
12083
|
function writePrefixCounter(dir, value) {
|
|
11391
|
-
|
|
12084
|
+
writeFileSync5(join8(dir, ".prefix-counter"), String(value));
|
|
11392
12085
|
}
|
|
11393
12086
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
11394
12087
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -11424,7 +12117,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
11424
12117
|
const existingByTodosId = new Map;
|
|
11425
12118
|
const files = listJsonFiles(dir);
|
|
11426
12119
|
for (const f of files) {
|
|
11427
|
-
const path =
|
|
12120
|
+
const path = join8(dir, f);
|
|
11428
12121
|
const ct = readClaudeTask(dir, f);
|
|
11429
12122
|
if (ct?.metadata?.["todos_id"]) {
|
|
11430
12123
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -11531,7 +12224,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
11531
12224
|
}
|
|
11532
12225
|
for (const f of files) {
|
|
11533
12226
|
try {
|
|
11534
|
-
const filePath =
|
|
12227
|
+
const filePath = join8(dir, f);
|
|
11535
12228
|
const ct = readClaudeTask(dir, f);
|
|
11536
12229
|
if (!ct)
|
|
11537
12230
|
continue;
|
|
@@ -11600,26 +12293,26 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
11600
12293
|
|
|
11601
12294
|
// src/lib/agent-tasks.ts
|
|
11602
12295
|
import { existsSync as existsSync9 } from "fs";
|
|
11603
|
-
import { join as
|
|
12296
|
+
import { join as join9 } from "path";
|
|
11604
12297
|
function getTodosGlobalDir3() {
|
|
11605
|
-
const newDir =
|
|
11606
|
-
const legacyDir =
|
|
12298
|
+
const newDir = join9(HOME, ".hasna", "todos");
|
|
12299
|
+
const legacyDir = join9(HOME, ".todos");
|
|
11607
12300
|
if (!existsSync9(newDir) && existsSync9(legacyDir))
|
|
11608
12301
|
return legacyDir;
|
|
11609
12302
|
return newDir;
|
|
11610
12303
|
}
|
|
11611
12304
|
function agentBaseDir(agent) {
|
|
11612
12305
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
11613
|
-
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] ||
|
|
12306
|
+
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join9(getTodosGlobalDir3(), "agents");
|
|
11614
12307
|
}
|
|
11615
12308
|
function getTaskListDir2(agent, taskListId) {
|
|
11616
|
-
return
|
|
12309
|
+
return join9(agentBaseDir(agent), agent, taskListId);
|
|
11617
12310
|
}
|
|
11618
12311
|
function readAgentTask(dir, filename) {
|
|
11619
|
-
return readJsonFile(
|
|
12312
|
+
return readJsonFile(join9(dir, filename));
|
|
11620
12313
|
}
|
|
11621
12314
|
function writeAgentTask(dir, task) {
|
|
11622
|
-
writeJsonFile(
|
|
12315
|
+
writeJsonFile(join9(dir, `${task.id}.json`), task);
|
|
11623
12316
|
}
|
|
11624
12317
|
function taskToAgentTask(task, externalId, existingMeta) {
|
|
11625
12318
|
return {
|
|
@@ -11653,7 +12346,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
11653
12346
|
const existingByTodosId = new Map;
|
|
11654
12347
|
const files = listJsonFiles(dir);
|
|
11655
12348
|
for (const f of files) {
|
|
11656
|
-
const path =
|
|
12349
|
+
const path = join9(dir, f);
|
|
11657
12350
|
const at = readAgentTask(dir, f);
|
|
11658
12351
|
if (at?.metadata?.["todos_id"]) {
|
|
11659
12352
|
existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -11746,7 +12439,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
11746
12439
|
}
|
|
11747
12440
|
for (const f of files) {
|
|
11748
12441
|
try {
|
|
11749
|
-
const filePath =
|
|
12442
|
+
const filePath = join9(dir, f);
|
|
11750
12443
|
const at = readAgentTask(dir, f);
|
|
11751
12444
|
if (!at)
|
|
11752
12445
|
continue;
|
|
@@ -11884,7 +12577,7 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
11884
12577
|
}
|
|
11885
12578
|
// src/lib/extract.ts
|
|
11886
12579
|
import { readFileSync as readFileSync7, statSync as statSync4 } from "fs";
|
|
11887
|
-
import { relative as relative3, resolve as resolve7, join as
|
|
12580
|
+
import { relative as relative3, resolve as resolve7, join as join10 } from "path";
|
|
11888
12581
|
var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
|
|
11889
12582
|
var DEFAULT_EXTENSIONS = new Set([
|
|
11890
12583
|
".ts",
|
|
@@ -12018,7 +12711,7 @@ function extractTodos(options, db) {
|
|
|
12018
12711
|
const files = collectFiles(basePath, extensions);
|
|
12019
12712
|
const allComments = [];
|
|
12020
12713
|
for (const file of files) {
|
|
12021
|
-
const fullPath = statSync4(basePath).isFile() ? basePath :
|
|
12714
|
+
const fullPath = statSync4(basePath).isFile() ? basePath : join10(basePath, file);
|
|
12022
12715
|
try {
|
|
12023
12716
|
const source7 = readFileSync7(fullPath, "utf-8");
|
|
12024
12717
|
const relPath = statSync4(basePath).isFile() ? relative3(resolve7(basePath, ".."), fullPath) : file;
|
|
@@ -13449,97 +14142,6 @@ function assertApprovalGate(taskId, gateName, db) {
|
|
|
13449
14142
|
throw new Error(result.reasons.join("; "));
|
|
13450
14143
|
return result.gate;
|
|
13451
14144
|
}
|
|
13452
|
-
// src/lib/local-fields.ts
|
|
13453
|
-
init_database();
|
|
13454
|
-
init_types();
|
|
13455
|
-
var LOCAL_FIELDS_KEY = "local_fields";
|
|
13456
|
-
function normalizeList(values) {
|
|
13457
|
-
return [...new Set((values || []).map((value) => value.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
13458
|
-
}
|
|
13459
|
-
function metadataFields(task) {
|
|
13460
|
-
const value = task.metadata[LOCAL_FIELDS_KEY];
|
|
13461
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
13462
|
-
}
|
|
13463
|
-
function sameCustomValue(actual, expected) {
|
|
13464
|
-
return JSON.stringify(actual) === JSON.stringify(expected);
|
|
13465
|
-
}
|
|
13466
|
-
function hasOwnField(fields, key) {
|
|
13467
|
-
return Object.prototype.hasOwnProperty.call(fields, key);
|
|
13468
|
-
}
|
|
13469
|
-
function getTaskLocalFields(taskId, db) {
|
|
13470
|
-
const d = db || getDatabase();
|
|
13471
|
-
const task = getTask(taskId, d);
|
|
13472
|
-
if (!task)
|
|
13473
|
-
throw new TaskNotFoundError(taskId);
|
|
13474
|
-
const fields = metadataFields(task);
|
|
13475
|
-
return {
|
|
13476
|
-
labels: normalizeList(fields.labels),
|
|
13477
|
-
priority: task.priority,
|
|
13478
|
-
severity: typeof fields.severity === "string" ? fields.severity : null,
|
|
13479
|
-
owner: hasOwnField(fields, "owner") ? typeof fields.owner === "string" ? fields.owner : null : task.assigned_to,
|
|
13480
|
-
area: typeof fields.area === "string" ? fields.area : null,
|
|
13481
|
-
custom: fields.custom && typeof fields.custom === "object" && !Array.isArray(fields.custom) ? fields.custom : {}
|
|
13482
|
-
};
|
|
13483
|
-
}
|
|
13484
|
-
function setTaskLocalFields(taskId, input, db) {
|
|
13485
|
-
const d = db || getDatabase();
|
|
13486
|
-
const task = getTask(taskId, d);
|
|
13487
|
-
if (!task)
|
|
13488
|
-
throw new TaskNotFoundError(taskId);
|
|
13489
|
-
const currentFields = getTaskLocalFields(taskId, d);
|
|
13490
|
-
const labels = input.labels !== undefined ? normalizeList(input.labels) : currentFields.labels;
|
|
13491
|
-
const custom = input.custom !== undefined ? redactValue(input.merge_custom === false ? input.custom : { ...currentFields.custom, ...input.custom }) : currentFields.custom;
|
|
13492
|
-
const nextFields = {
|
|
13493
|
-
labels,
|
|
13494
|
-
priority: input.priority || task.priority,
|
|
13495
|
-
severity: input.severity !== undefined ? input.severity : currentFields.severity,
|
|
13496
|
-
owner: input.owner !== undefined ? input.owner : currentFields.owner,
|
|
13497
|
-
area: input.area !== undefined ? input.area : currentFields.area,
|
|
13498
|
-
custom
|
|
13499
|
-
};
|
|
13500
|
-
const nextMetadata = {
|
|
13501
|
-
...task.metadata,
|
|
13502
|
-
[LOCAL_FIELDS_KEY]: nextFields
|
|
13503
|
-
};
|
|
13504
|
-
const previousLabels = new Set(currentFields.labels);
|
|
13505
|
-
const nextTags = normalizeList([...task.tags.filter((tag) => !previousLabels.has(tag)), ...labels]);
|
|
13506
|
-
const updates = {
|
|
13507
|
-
version: task.version,
|
|
13508
|
-
priority: input.priority,
|
|
13509
|
-
tags: nextTags,
|
|
13510
|
-
metadata: nextMetadata
|
|
13511
|
-
};
|
|
13512
|
-
if (input.owner !== undefined)
|
|
13513
|
-
updates.assigned_to = nextFields.owner;
|
|
13514
|
-
return updateTask(taskId, updates, d);
|
|
13515
|
-
}
|
|
13516
|
-
function queryTasksByLocalFields(query, db) {
|
|
13517
|
-
const d = db || getDatabase();
|
|
13518
|
-
const tasks = listTasks({
|
|
13519
|
-
priority: query.priority,
|
|
13520
|
-
tags: query.labels,
|
|
13521
|
-
limit: 1e4
|
|
13522
|
-
}, d);
|
|
13523
|
-
const matches = tasks.filter((task) => {
|
|
13524
|
-
const fields = getTaskLocalFields(task.id, d);
|
|
13525
|
-
if (query.labels && !query.labels.every((label) => fields.labels.includes(label)))
|
|
13526
|
-
return false;
|
|
13527
|
-
if (query.severity && fields.severity !== query.severity)
|
|
13528
|
-
return false;
|
|
13529
|
-
if (query.owner && fields.owner !== query.owner)
|
|
13530
|
-
return false;
|
|
13531
|
-
if (query.area && fields.area !== query.area)
|
|
13532
|
-
return false;
|
|
13533
|
-
if (query.custom) {
|
|
13534
|
-
for (const [key, expected] of Object.entries(query.custom)) {
|
|
13535
|
-
if (!sameCustomValue(fields.custom[key], expected))
|
|
13536
|
-
return false;
|
|
13537
|
-
}
|
|
13538
|
-
}
|
|
13539
|
-
return true;
|
|
13540
|
-
});
|
|
13541
|
-
return matches.slice(0, query.limit || 100);
|
|
13542
|
-
}
|
|
13543
14145
|
// src/lib/task-dedupe.ts
|
|
13544
14146
|
init_database();
|
|
13545
14147
|
var DEFAULT_THRESHOLD = 0.74;
|
|
@@ -13978,7 +14580,7 @@ var DEFAULT_CAPABILITIES = {
|
|
|
13978
14580
|
browser: ["browser", "screenshot", "artifact", "evidence"],
|
|
13979
14581
|
script: ["script", "command", "retry", "evidence"]
|
|
13980
14582
|
};
|
|
13981
|
-
function
|
|
14583
|
+
function normalizeName2(name) {
|
|
13982
14584
|
const normalized = name.trim().toLowerCase();
|
|
13983
14585
|
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
|
|
13984
14586
|
throw new Error("verification provider name must use lowercase letters, numbers, dashes, or underscores");
|
|
@@ -13997,10 +14599,10 @@ function timeoutMs(value) {
|
|
|
13997
14599
|
return Math.max(1, Math.min(24 * 60 * 60000, Math.floor(value)));
|
|
13998
14600
|
}
|
|
13999
14601
|
function getProvider(name) {
|
|
14000
|
-
return loadConfig().verification_providers?.[
|
|
14602
|
+
return loadConfig().verification_providers?.[normalizeName2(name)] || null;
|
|
14001
14603
|
}
|
|
14002
14604
|
function upsertVerificationProvider(input) {
|
|
14003
|
-
const name =
|
|
14605
|
+
const name = normalizeName2(input.name);
|
|
14004
14606
|
const config = loadConfig();
|
|
14005
14607
|
const existing = config.verification_providers?.[name];
|
|
14006
14608
|
const timestamp = new Date().toISOString();
|
|
@@ -14030,7 +14632,7 @@ function listVerificationProviders() {
|
|
|
14030
14632
|
return Object.values(loadConfig().verification_providers || {}).sort((a, b) => a.name.localeCompare(b.name));
|
|
14031
14633
|
}
|
|
14032
14634
|
function removeVerificationProvider(name) {
|
|
14033
|
-
const normalized =
|
|
14635
|
+
const normalized = normalizeName2(name);
|
|
14034
14636
|
const config = loadConfig();
|
|
14035
14637
|
if (!config.verification_providers?.[normalized])
|
|
14036
14638
|
return false;
|
|
@@ -14879,7 +15481,8 @@ function emptyCounts2() {
|
|
|
14879
15481
|
task_files: 0,
|
|
14880
15482
|
task_commits: 0,
|
|
14881
15483
|
task_git_refs: 0,
|
|
14882
|
-
task_verifications: 0
|
|
15484
|
+
task_verifications: 0,
|
|
15485
|
+
saved_views: 0
|
|
14883
15486
|
};
|
|
14884
15487
|
}
|
|
14885
15488
|
function bridgeToMarkdownPayload(bundle) {
|
|
@@ -15311,8 +15914,8 @@ function getLocalActivityTimeline(options = {}, db) {
|
|
|
15311
15914
|
init_database();
|
|
15312
15915
|
init_migrations();
|
|
15313
15916
|
init_schema();
|
|
15314
|
-
import { chmodSync, copyFileSync, existsSync as existsSync11, mkdirSync as
|
|
15315
|
-
import { basename as basename2, dirname as dirname7, join as
|
|
15917
|
+
import { chmodSync, copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync7, statSync as statSync5 } from "fs";
|
|
15918
|
+
import { basename as basename2, dirname as dirname7, join as join11 } from "path";
|
|
15316
15919
|
var REQUIRED_TABLES = [
|
|
15317
15920
|
"_migrations",
|
|
15318
15921
|
"projects",
|
|
@@ -15482,13 +16085,13 @@ function createBackup(dbPath) {
|
|
|
15482
16085
|
if (!existsSync11(dbPath))
|
|
15483
16086
|
return;
|
|
15484
16087
|
const stamp = now().replace(/[:.]/g, "-");
|
|
15485
|
-
const backupDir =
|
|
16088
|
+
const backupDir = join11(dirname7(dbPath), `${basename2(dbPath)}.backup-${stamp}`);
|
|
15486
16089
|
const files = [];
|
|
15487
|
-
|
|
16090
|
+
mkdirSync7(backupDir, { recursive: true });
|
|
15488
16091
|
for (const source7 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
15489
16092
|
if (!existsSync11(source7))
|
|
15490
16093
|
continue;
|
|
15491
|
-
const target =
|
|
16094
|
+
const target = join11(backupDir, basename2(source7));
|
|
15492
16095
|
copyFileSync(source7, target);
|
|
15493
16096
|
files.push(target);
|
|
15494
16097
|
}
|
|
@@ -15906,6 +16509,7 @@ function checkTaskDoneContract(taskId, db) {
|
|
|
15906
16509
|
};
|
|
15907
16510
|
}
|
|
15908
16511
|
export {
|
|
16512
|
+
writeBuiltinTemplateFiles,
|
|
15909
16513
|
verifyTaskRunArtifacts,
|
|
15910
16514
|
verifyStoredArtifact,
|
|
15911
16515
|
verifyApiKey,
|
|
@@ -15963,8 +16567,11 @@ export {
|
|
|
15963
16567
|
searchTasks,
|
|
15964
16568
|
scoreTask,
|
|
15965
16569
|
saveSnapshot,
|
|
16570
|
+
saveSearchView,
|
|
15966
16571
|
runVerificationProvider,
|
|
15967
16572
|
runTodosDoctor,
|
|
16573
|
+
runSearchView,
|
|
16574
|
+
runSavedSearch,
|
|
15968
16575
|
runNextAgentDispatch,
|
|
15969
16576
|
runDueDispatches,
|
|
15970
16577
|
revokeApiKey,
|
|
@@ -16009,6 +16616,7 @@ export {
|
|
|
16009
16616
|
parseRecurrenceRule,
|
|
16010
16617
|
parseGitHubUrl,
|
|
16011
16618
|
now,
|
|
16619
|
+
normalizeScope,
|
|
16012
16620
|
normalizeGeneratedAgentNames,
|
|
16013
16621
|
normalizeApiUrl,
|
|
16014
16622
|
nextTaskShortId,
|
|
@@ -16033,6 +16641,7 @@ export {
|
|
|
16033
16641
|
listTaskFiles,
|
|
16034
16642
|
listSnapshots,
|
|
16035
16643
|
listSessions,
|
|
16644
|
+
listSearchViews,
|
|
16036
16645
|
listRunnerSandboxProfiles,
|
|
16037
16646
|
listProjects,
|
|
16038
16647
|
listProjectSources,
|
|
@@ -16051,6 +16660,7 @@ export {
|
|
|
16051
16660
|
listCyclesWithStats,
|
|
16052
16661
|
listCycles,
|
|
16053
16662
|
listComments,
|
|
16663
|
+
listBuiltinTemplates,
|
|
16054
16664
|
listApprovalGates,
|
|
16055
16665
|
listApiKeys,
|
|
16056
16666
|
listAgents,
|
|
@@ -16097,6 +16707,7 @@ export {
|
|
|
16097
16707
|
getStatus,
|
|
16098
16708
|
getStaleTasks,
|
|
16099
16709
|
getSession,
|
|
16710
|
+
getSearchView,
|
|
16100
16711
|
getRunnerSandboxProfile,
|
|
16101
16712
|
getReviewQueue,
|
|
16102
16713
|
getRelated,
|
|
@@ -16144,6 +16755,7 @@ export {
|
|
|
16144
16755
|
getChecklist,
|
|
16145
16756
|
getCapableAgents,
|
|
16146
16757
|
getBurndown,
|
|
16758
|
+
getBuiltinTemplate,
|
|
16147
16759
|
getBudget,
|
|
16148
16760
|
getBlockingDeps,
|
|
16149
16761
|
getAgentMetrics,
|
|
@@ -16169,6 +16781,8 @@ export {
|
|
|
16169
16781
|
exportTodosMarkdown,
|
|
16170
16782
|
exportTemplate,
|
|
16171
16783
|
exportStoredArtifactContent,
|
|
16784
|
+
exportBuiltinTemplateFiles,
|
|
16785
|
+
exportBuiltinTemplate,
|
|
16172
16786
|
explainRunnerSandbox,
|
|
16173
16787
|
explainPolicyPack,
|
|
16174
16788
|
expireApprovalGate,
|
|
@@ -16193,6 +16807,7 @@ export {
|
|
|
16193
16807
|
deleteTaskList,
|
|
16194
16808
|
deleteTask,
|
|
16195
16809
|
deleteSession,
|
|
16810
|
+
deleteSearchView,
|
|
16196
16811
|
deleteProject,
|
|
16197
16812
|
deletePlan,
|
|
16198
16813
|
deleteOrg,
|
|
@@ -16320,6 +16935,8 @@ export {
|
|
|
16320
16935
|
DEFAULT_ENCRYPTION_KEY_ENV,
|
|
16321
16936
|
CompletionGuardError,
|
|
16322
16937
|
CORE_MCP_TOOLS,
|
|
16938
|
+
BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
16939
|
+
BUILTIN_TEMPLATE_LIBRARY_SOURCE,
|
|
16323
16940
|
BUILTIN_TEMPLATES,
|
|
16324
16941
|
AgentNotFoundError
|
|
16325
16942
|
};
|