@hasna/todos 0.11.41 → 0.11.42
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 +20 -0
- package/dist/cli/commands/project-commands.d.ts.map +1 -1
- package/dist/cli/index.js +741 -141
- package/dist/cli-mcp-parity.d.ts.map +1 -1
- package/dist/contracts.js +73 -6
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +519 -103
- 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.js +544 -96
- 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.js +4 -0
- package/dist/registry.js +86 -7
- 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/cli/index.js
CHANGED
|
@@ -3083,6 +3083,19 @@ var init_migrations = __esm(() => {
|
|
|
3083
3083
|
);
|
|
3084
3084
|
CREATE INDEX IF NOT EXISTS idx_handoff_acks_agent ON handoff_acknowledgements(agent_id, acknowledged_at);
|
|
3085
3085
|
INSERT OR IGNORE INTO _migrations (id) VALUES (54);
|
|
3086
|
+
`,
|
|
3087
|
+
`
|
|
3088
|
+
CREATE TABLE IF NOT EXISTS saved_search_views (
|
|
3089
|
+
id TEXT PRIMARY KEY,
|
|
3090
|
+
name TEXT NOT NULL UNIQUE,
|
|
3091
|
+
description TEXT,
|
|
3092
|
+
scope TEXT NOT NULL DEFAULT 'tasks' CHECK(scope IN ('all', 'tasks', 'projects', 'plans', 'runs', 'comments')),
|
|
3093
|
+
filters TEXT NOT NULL DEFAULT '{}',
|
|
3094
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
3095
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
3096
|
+
);
|
|
3097
|
+
CREATE INDEX IF NOT EXISTS idx_saved_search_views_scope ON saved_search_views(scope);
|
|
3098
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (55);
|
|
3086
3099
|
`
|
|
3087
3100
|
];
|
|
3088
3101
|
});
|
|
@@ -3259,6 +3272,17 @@ function ensureSchema(db) {
|
|
|
3259
3272
|
PRIMARY KEY (handoff_id, agent_id)
|
|
3260
3273
|
)`);
|
|
3261
3274
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_handoff_acks_agent ON handoff_acknowledgements(agent_id, acknowledged_at)");
|
|
3275
|
+
ensureTable("saved_search_views", `
|
|
3276
|
+
CREATE TABLE saved_search_views (
|
|
3277
|
+
id TEXT PRIMARY KEY,
|
|
3278
|
+
name TEXT NOT NULL UNIQUE,
|
|
3279
|
+
description TEXT,
|
|
3280
|
+
scope TEXT NOT NULL DEFAULT 'tasks' CHECK(scope IN ('all', 'tasks', 'projects', 'plans', 'runs', 'comments')),
|
|
3281
|
+
filters TEXT NOT NULL DEFAULT '{}',
|
|
3282
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
3283
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
3284
|
+
)`);
|
|
3285
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_saved_search_views_scope ON saved_search_views(scope)");
|
|
3262
3286
|
ensureTable("task_relationships", `
|
|
3263
3287
|
CREATE TABLE task_relationships (
|
|
3264
3288
|
id TEXT PRIMARY KEY,
|
|
@@ -9432,6 +9456,101 @@ var init_comments = __esm(() => {
|
|
|
9432
9456
|
init_tasks();
|
|
9433
9457
|
});
|
|
9434
9458
|
|
|
9459
|
+
// src/lib/local-fields.ts
|
|
9460
|
+
function normalizeList(values) {
|
|
9461
|
+
return [...new Set((values || []).map((value) => value.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
9462
|
+
}
|
|
9463
|
+
function metadataFields(task) {
|
|
9464
|
+
const value = task.metadata[LOCAL_FIELDS_KEY];
|
|
9465
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
9466
|
+
}
|
|
9467
|
+
function sameCustomValue(actual, expected) {
|
|
9468
|
+
return JSON.stringify(actual) === JSON.stringify(expected);
|
|
9469
|
+
}
|
|
9470
|
+
function hasOwnField(fields, key) {
|
|
9471
|
+
return Object.prototype.hasOwnProperty.call(fields, key);
|
|
9472
|
+
}
|
|
9473
|
+
function getTaskLocalFields(taskId, db) {
|
|
9474
|
+
const d = db || getDatabase();
|
|
9475
|
+
const task = getTask(taskId, d);
|
|
9476
|
+
if (!task)
|
|
9477
|
+
throw new TaskNotFoundError(taskId);
|
|
9478
|
+
const fields = metadataFields(task);
|
|
9479
|
+
return {
|
|
9480
|
+
labels: normalizeList(fields.labels),
|
|
9481
|
+
priority: task.priority,
|
|
9482
|
+
severity: typeof fields.severity === "string" ? fields.severity : null,
|
|
9483
|
+
owner: hasOwnField(fields, "owner") ? typeof fields.owner === "string" ? fields.owner : null : task.assigned_to,
|
|
9484
|
+
area: typeof fields.area === "string" ? fields.area : null,
|
|
9485
|
+
custom: fields.custom && typeof fields.custom === "object" && !Array.isArray(fields.custom) ? fields.custom : {}
|
|
9486
|
+
};
|
|
9487
|
+
}
|
|
9488
|
+
function setTaskLocalFields(taskId, input, db) {
|
|
9489
|
+
const d = db || getDatabase();
|
|
9490
|
+
const task = getTask(taskId, d);
|
|
9491
|
+
if (!task)
|
|
9492
|
+
throw new TaskNotFoundError(taskId);
|
|
9493
|
+
const currentFields = getTaskLocalFields(taskId, d);
|
|
9494
|
+
const labels = input.labels !== undefined ? normalizeList(input.labels) : currentFields.labels;
|
|
9495
|
+
const custom = input.custom !== undefined ? redactValue(input.merge_custom === false ? input.custom : { ...currentFields.custom, ...input.custom }) : currentFields.custom;
|
|
9496
|
+
const nextFields = {
|
|
9497
|
+
labels,
|
|
9498
|
+
priority: input.priority || task.priority,
|
|
9499
|
+
severity: input.severity !== undefined ? input.severity : currentFields.severity,
|
|
9500
|
+
owner: input.owner !== undefined ? input.owner : currentFields.owner,
|
|
9501
|
+
area: input.area !== undefined ? input.area : currentFields.area,
|
|
9502
|
+
custom
|
|
9503
|
+
};
|
|
9504
|
+
const nextMetadata = {
|
|
9505
|
+
...task.metadata,
|
|
9506
|
+
[LOCAL_FIELDS_KEY]: nextFields
|
|
9507
|
+
};
|
|
9508
|
+
const previousLabels = new Set(currentFields.labels);
|
|
9509
|
+
const nextTags = normalizeList([...task.tags.filter((tag) => !previousLabels.has(tag)), ...labels]);
|
|
9510
|
+
const updates = {
|
|
9511
|
+
version: task.version,
|
|
9512
|
+
priority: input.priority,
|
|
9513
|
+
tags: nextTags,
|
|
9514
|
+
metadata: nextMetadata
|
|
9515
|
+
};
|
|
9516
|
+
if (input.owner !== undefined)
|
|
9517
|
+
updates.assigned_to = nextFields.owner;
|
|
9518
|
+
return updateTask(taskId, updates, d);
|
|
9519
|
+
}
|
|
9520
|
+
function queryTasksByLocalFields(query, db) {
|
|
9521
|
+
const d = db || getDatabase();
|
|
9522
|
+
const tasks = listTasks({
|
|
9523
|
+
priority: query.priority,
|
|
9524
|
+
tags: query.labels,
|
|
9525
|
+
limit: 1e4
|
|
9526
|
+
}, d);
|
|
9527
|
+
const matches = tasks.filter((task) => {
|
|
9528
|
+
const fields = getTaskLocalFields(task.id, d);
|
|
9529
|
+
if (query.labels && !query.labels.every((label) => fields.labels.includes(label)))
|
|
9530
|
+
return false;
|
|
9531
|
+
if (query.severity && fields.severity !== query.severity)
|
|
9532
|
+
return false;
|
|
9533
|
+
if (query.owner && fields.owner !== query.owner)
|
|
9534
|
+
return false;
|
|
9535
|
+
if (query.area && fields.area !== query.area)
|
|
9536
|
+
return false;
|
|
9537
|
+
if (query.custom) {
|
|
9538
|
+
for (const [key, expected] of Object.entries(query.custom)) {
|
|
9539
|
+
if (!sameCustomValue(fields.custom[key], expected))
|
|
9540
|
+
return false;
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
return true;
|
|
9544
|
+
});
|
|
9545
|
+
return matches.slice(0, query.limit || 100);
|
|
9546
|
+
}
|
|
9547
|
+
var LOCAL_FIELDS_KEY = "local_fields";
|
|
9548
|
+
var init_local_fields = __esm(() => {
|
|
9549
|
+
init_database();
|
|
9550
|
+
init_tasks();
|
|
9551
|
+
init_types();
|
|
9552
|
+
});
|
|
9553
|
+
|
|
9435
9554
|
// src/lib/search.ts
|
|
9436
9555
|
var exports_search = {};
|
|
9437
9556
|
__export(exports_search, {
|
|
@@ -9547,6 +9666,347 @@ var init_search = __esm(() => {
|
|
|
9547
9666
|
init_database();
|
|
9548
9667
|
});
|
|
9549
9668
|
|
|
9669
|
+
// src/lib/saved-search-views.ts
|
|
9670
|
+
var exports_saved_search_views = {};
|
|
9671
|
+
__export(exports_saved_search_views, {
|
|
9672
|
+
saveSearchView: () => saveSearchView,
|
|
9673
|
+
runSearchView: () => runSearchView,
|
|
9674
|
+
runSavedSearch: () => runSavedSearch,
|
|
9675
|
+
normalizeScope: () => normalizeScope,
|
|
9676
|
+
listSearchViews: () => listSearchViews,
|
|
9677
|
+
getSearchView: () => getSearchView,
|
|
9678
|
+
deleteSearchView: () => deleteSearchView
|
|
9679
|
+
});
|
|
9680
|
+
function parseFilters(value) {
|
|
9681
|
+
if (!value)
|
|
9682
|
+
return {};
|
|
9683
|
+
try {
|
|
9684
|
+
const parsed = JSON.parse(value);
|
|
9685
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
9686
|
+
} catch {
|
|
9687
|
+
return {};
|
|
9688
|
+
}
|
|
9689
|
+
}
|
|
9690
|
+
function rowToSavedSearchView(row) {
|
|
9691
|
+
return {
|
|
9692
|
+
...row,
|
|
9693
|
+
scope: normalizeScope(row.scope),
|
|
9694
|
+
filters: parseFilters(row.filters)
|
|
9695
|
+
};
|
|
9696
|
+
}
|
|
9697
|
+
function normalizeScope(scope) {
|
|
9698
|
+
if (scope === "all" || scope === "tasks" || scope === "projects" || scope === "plans" || scope === "runs" || scope === "comments") {
|
|
9699
|
+
return scope;
|
|
9700
|
+
}
|
|
9701
|
+
return "tasks";
|
|
9702
|
+
}
|
|
9703
|
+
function normalizeName(name) {
|
|
9704
|
+
const normalized = name.trim();
|
|
9705
|
+
if (!normalized)
|
|
9706
|
+
throw new Error("Saved view name is required");
|
|
9707
|
+
return normalized;
|
|
9708
|
+
}
|
|
9709
|
+
function normalizeLimit(limit) {
|
|
9710
|
+
if (!Number.isFinite(limit) || !limit || limit <= 0)
|
|
9711
|
+
return 100;
|
|
9712
|
+
return Math.min(Math.trunc(limit), 1000);
|
|
9713
|
+
}
|
|
9714
|
+
function valuesList(value) {
|
|
9715
|
+
if (value === undefined)
|
|
9716
|
+
return [];
|
|
9717
|
+
return (Array.isArray(value) ? value : [value]).map((item) => item.trim()).filter(Boolean);
|
|
9718
|
+
}
|
|
9719
|
+
function addStatusFilter(sql, params, column, value) {
|
|
9720
|
+
const values = valuesList(value);
|
|
9721
|
+
if (values.length === 0)
|
|
9722
|
+
return sql;
|
|
9723
|
+
params.push(...values);
|
|
9724
|
+
return `${sql} AND ${column} IN (${values.map(() => "?").join(",")})`;
|
|
9725
|
+
}
|
|
9726
|
+
function addDateFilter(sql, params, column, value) {
|
|
9727
|
+
if (!value)
|
|
9728
|
+
return sql;
|
|
9729
|
+
params.push(value);
|
|
9730
|
+
return `${sql} AND ${column} > ?`;
|
|
9731
|
+
}
|
|
9732
|
+
function likePattern(query) {
|
|
9733
|
+
const trimmed = query?.trim();
|
|
9734
|
+
if (!trimmed || trimmed === "*")
|
|
9735
|
+
return null;
|
|
9736
|
+
return `%${trimmed}%`;
|
|
9737
|
+
}
|
|
9738
|
+
function parseJsonObject(value) {
|
|
9739
|
+
if (!value)
|
|
9740
|
+
return {};
|
|
9741
|
+
if (typeof value === "object" && !Array.isArray(value))
|
|
9742
|
+
return value;
|
|
9743
|
+
if (typeof value !== "string")
|
|
9744
|
+
return {};
|
|
9745
|
+
try {
|
|
9746
|
+
const parsed = JSON.parse(value);
|
|
9747
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
9748
|
+
} catch {
|
|
9749
|
+
return {};
|
|
9750
|
+
}
|
|
9751
|
+
}
|
|
9752
|
+
function rowToTaskRun(row) {
|
|
9753
|
+
return { ...row, metadata: parseJsonObject(row.metadata) };
|
|
9754
|
+
}
|
|
9755
|
+
function taskMatchesSavedFilters(task, filters, db) {
|
|
9756
|
+
if (filters.plan_id && task.plan_id !== filters.plan_id)
|
|
9757
|
+
return false;
|
|
9758
|
+
if (filters.tags && !filters.tags.every((tag) => task.tags.includes(tag)))
|
|
9759
|
+
return false;
|
|
9760
|
+
if (filters.depends_on) {
|
|
9761
|
+
const row = db.query("SELECT 1 FROM task_dependencies WHERE task_id = ? AND depends_on = ?").get(task.id, filters.depends_on);
|
|
9762
|
+
if (!row)
|
|
9763
|
+
return false;
|
|
9764
|
+
}
|
|
9765
|
+
if (filters.blocks) {
|
|
9766
|
+
const row = db.query("SELECT 1 FROM task_dependencies WHERE task_id = ? AND depends_on = ?").get(filters.blocks, task.id);
|
|
9767
|
+
if (!row)
|
|
9768
|
+
return false;
|
|
9769
|
+
}
|
|
9770
|
+
return true;
|
|
9771
|
+
}
|
|
9772
|
+
function searchTaskEntities(filters, db) {
|
|
9773
|
+
let tasks;
|
|
9774
|
+
if (filters.local_fields) {
|
|
9775
|
+
const localMatches = queryTasksByLocalFields({ ...filters.local_fields, limit: 1e4 }, db);
|
|
9776
|
+
const allowedIds = new Set(localMatches.map((task) => task.id));
|
|
9777
|
+
tasks = searchTasks({
|
|
9778
|
+
query: filters.query,
|
|
9779
|
+
project_id: filters.project_id,
|
|
9780
|
+
task_list_id: filters.task_list_id,
|
|
9781
|
+
status: filters.status,
|
|
9782
|
+
priority: filters.priority,
|
|
9783
|
+
assigned_to: filters.assigned_to,
|
|
9784
|
+
agent_id: filters.agent_id,
|
|
9785
|
+
created_after: filters.created_after,
|
|
9786
|
+
updated_after: filters.updated_after,
|
|
9787
|
+
has_dependencies: filters.has_dependencies,
|
|
9788
|
+
is_blocked: filters.is_blocked
|
|
9789
|
+
}, undefined, undefined, db).filter((task) => allowedIds.has(task.id));
|
|
9790
|
+
} else {
|
|
9791
|
+
tasks = searchTasks({
|
|
9792
|
+
query: filters.query,
|
|
9793
|
+
project_id: filters.project_id,
|
|
9794
|
+
task_list_id: filters.task_list_id,
|
|
9795
|
+
status: filters.status,
|
|
9796
|
+
priority: filters.priority,
|
|
9797
|
+
assigned_to: filters.assigned_to,
|
|
9798
|
+
agent_id: filters.agent_id,
|
|
9799
|
+
created_after: filters.created_after,
|
|
9800
|
+
updated_after: filters.updated_after,
|
|
9801
|
+
has_dependencies: filters.has_dependencies,
|
|
9802
|
+
is_blocked: filters.is_blocked
|
|
9803
|
+
}, undefined, undefined, db);
|
|
9804
|
+
}
|
|
9805
|
+
return tasks.filter((task) => taskMatchesSavedFilters(task, filters, db)).slice(0, normalizeLimit(filters.limit));
|
|
9806
|
+
}
|
|
9807
|
+
function searchProjects(filters, db) {
|
|
9808
|
+
const params = [];
|
|
9809
|
+
let sql = "SELECT * FROM projects WHERE 1=1";
|
|
9810
|
+
if (filters.project_id) {
|
|
9811
|
+
sql += " AND id = ?";
|
|
9812
|
+
params.push(filters.project_id);
|
|
9813
|
+
}
|
|
9814
|
+
const pattern = likePattern(filters.query);
|
|
9815
|
+
if (pattern) {
|
|
9816
|
+
sql += " AND (name LIKE ? OR description LIKE ? OR path LIKE ?)";
|
|
9817
|
+
params.push(pattern, pattern, pattern);
|
|
9818
|
+
}
|
|
9819
|
+
sql = addDateFilter(sql, params, "created_at", filters.created_after);
|
|
9820
|
+
sql = addDateFilter(sql, params, "updated_at", filters.updated_after);
|
|
9821
|
+
sql += " ORDER BY name LIMIT ?";
|
|
9822
|
+
params.push(normalizeLimit(filters.limit));
|
|
9823
|
+
return db.query(sql).all(...params);
|
|
9824
|
+
}
|
|
9825
|
+
function searchPlans(filters, db) {
|
|
9826
|
+
const params = [];
|
|
9827
|
+
let sql = "SELECT * FROM plans WHERE 1=1";
|
|
9828
|
+
if (filters.project_id) {
|
|
9829
|
+
sql += " AND project_id = ?";
|
|
9830
|
+
params.push(filters.project_id);
|
|
9831
|
+
}
|
|
9832
|
+
if (filters.task_list_id) {
|
|
9833
|
+
sql += " AND task_list_id = ?";
|
|
9834
|
+
params.push(filters.task_list_id);
|
|
9835
|
+
}
|
|
9836
|
+
if (filters.agent_id) {
|
|
9837
|
+
sql += " AND agent_id = ?";
|
|
9838
|
+
params.push(filters.agent_id);
|
|
9839
|
+
}
|
|
9840
|
+
sql = addStatusFilter(sql, params, "status", filters.status);
|
|
9841
|
+
const pattern = likePattern(filters.query);
|
|
9842
|
+
if (pattern) {
|
|
9843
|
+
sql += " AND (name LIKE ? OR description LIKE ?)";
|
|
9844
|
+
params.push(pattern, pattern);
|
|
9845
|
+
}
|
|
9846
|
+
sql = addDateFilter(sql, params, "created_at", filters.created_after);
|
|
9847
|
+
sql = addDateFilter(sql, params, "updated_at", filters.updated_after);
|
|
9848
|
+
sql += " ORDER BY updated_at DESC, created_at DESC LIMIT ?";
|
|
9849
|
+
params.push(normalizeLimit(filters.limit));
|
|
9850
|
+
return db.query(sql).all(...params);
|
|
9851
|
+
}
|
|
9852
|
+
function searchRuns(filters, db) {
|
|
9853
|
+
const params = [];
|
|
9854
|
+
let sql = `SELECT r.* FROM task_runs r
|
|
9855
|
+
JOIN tasks t ON t.id = r.task_id
|
|
9856
|
+
WHERE 1=1`;
|
|
9857
|
+
if (filters.project_id) {
|
|
9858
|
+
sql += " AND t.project_id = ?";
|
|
9859
|
+
params.push(filters.project_id);
|
|
9860
|
+
}
|
|
9861
|
+
if (filters.task_list_id) {
|
|
9862
|
+
sql += " AND t.task_list_id = ?";
|
|
9863
|
+
params.push(filters.task_list_id);
|
|
9864
|
+
}
|
|
9865
|
+
if (filters.plan_id) {
|
|
9866
|
+
sql += " AND t.plan_id = ?";
|
|
9867
|
+
params.push(filters.plan_id);
|
|
9868
|
+
}
|
|
9869
|
+
if (filters.task_id) {
|
|
9870
|
+
sql += " AND r.task_id = ?";
|
|
9871
|
+
params.push(filters.task_id);
|
|
9872
|
+
}
|
|
9873
|
+
if (filters.agent_id) {
|
|
9874
|
+
sql += " AND r.agent_id = ?";
|
|
9875
|
+
params.push(filters.agent_id);
|
|
9876
|
+
}
|
|
9877
|
+
sql = addStatusFilter(sql, params, "r.status", filters.status);
|
|
9878
|
+
const pattern = likePattern(filters.query);
|
|
9879
|
+
if (pattern) {
|
|
9880
|
+
sql += " AND (r.title LIKE ? OR r.summary LIKE ? OR t.title LIKE ?)";
|
|
9881
|
+
params.push(pattern, pattern, pattern);
|
|
9882
|
+
}
|
|
9883
|
+
sql = addDateFilter(sql, params, "r.created_at", filters.created_after);
|
|
9884
|
+
sql = addDateFilter(sql, params, "r.updated_at", filters.updated_after);
|
|
9885
|
+
sql += " ORDER BY r.started_at DESC, r.created_at DESC LIMIT ?";
|
|
9886
|
+
params.push(normalizeLimit(filters.limit));
|
|
9887
|
+
return db.query(sql).all(...params).map(rowToTaskRun);
|
|
9888
|
+
}
|
|
9889
|
+
function searchComments(filters, db) {
|
|
9890
|
+
const params = [];
|
|
9891
|
+
let sql = `SELECT c.* FROM task_comments c
|
|
9892
|
+
JOIN tasks t ON t.id = c.task_id
|
|
9893
|
+
WHERE 1=1`;
|
|
9894
|
+
if (filters.project_id) {
|
|
9895
|
+
sql += " AND t.project_id = ?";
|
|
9896
|
+
params.push(filters.project_id);
|
|
9897
|
+
}
|
|
9898
|
+
if (filters.task_list_id) {
|
|
9899
|
+
sql += " AND t.task_list_id = ?";
|
|
9900
|
+
params.push(filters.task_list_id);
|
|
9901
|
+
}
|
|
9902
|
+
if (filters.plan_id) {
|
|
9903
|
+
sql += " AND t.plan_id = ?";
|
|
9904
|
+
params.push(filters.plan_id);
|
|
9905
|
+
}
|
|
9906
|
+
if (filters.task_id) {
|
|
9907
|
+
sql += " AND c.task_id = ?";
|
|
9908
|
+
params.push(filters.task_id);
|
|
9909
|
+
}
|
|
9910
|
+
if (filters.agent_id) {
|
|
9911
|
+
sql += " AND c.agent_id = ?";
|
|
9912
|
+
params.push(filters.agent_id);
|
|
9913
|
+
}
|
|
9914
|
+
const pattern = likePattern(filters.query);
|
|
9915
|
+
if (pattern) {
|
|
9916
|
+
sql += " AND (c.content LIKE ? OR t.title LIKE ?)";
|
|
9917
|
+
params.push(pattern, pattern);
|
|
9918
|
+
}
|
|
9919
|
+
sql = addDateFilter(sql, params, "c.created_at", filters.created_after);
|
|
9920
|
+
sql += " ORDER BY c.created_at DESC, c.id LIMIT ?";
|
|
9921
|
+
params.push(normalizeLimit(filters.limit));
|
|
9922
|
+
return db.query(sql).all(...params);
|
|
9923
|
+
}
|
|
9924
|
+
function toResults(entityType, rows) {
|
|
9925
|
+
return rows.map((entity) => ({ entity_type: entityType, entity }));
|
|
9926
|
+
}
|
|
9927
|
+
function runSavedSearch(filters = {}, scope = "tasks", db) {
|
|
9928
|
+
const d = db || getDatabase();
|
|
9929
|
+
const normalizedScope = normalizeScope(scope);
|
|
9930
|
+
const scopes = normalizedScope === "all" ? ["tasks", "projects", "plans", "runs", "comments"] : [normalizedScope];
|
|
9931
|
+
const results = [];
|
|
9932
|
+
for (const item of scopes) {
|
|
9933
|
+
if (item === "tasks")
|
|
9934
|
+
results.push(...toResults("tasks", searchTaskEntities(filters, d)));
|
|
9935
|
+
if (item === "projects")
|
|
9936
|
+
results.push(...toResults("projects", searchProjects(filters, d)));
|
|
9937
|
+
if (item === "plans")
|
|
9938
|
+
results.push(...toResults("plans", searchPlans(filters, d)));
|
|
9939
|
+
if (item === "runs")
|
|
9940
|
+
results.push(...toResults("runs", searchRuns(filters, d)));
|
|
9941
|
+
if (item === "comments")
|
|
9942
|
+
results.push(...toResults("comments", searchComments(filters, d)));
|
|
9943
|
+
}
|
|
9944
|
+
return {
|
|
9945
|
+
scope: normalizedScope,
|
|
9946
|
+
filters,
|
|
9947
|
+
count: results.length,
|
|
9948
|
+
results: results.slice(0, normalizeLimit(filters.limit))
|
|
9949
|
+
};
|
|
9950
|
+
}
|
|
9951
|
+
function saveSearchView(input, db) {
|
|
9952
|
+
const d = db || getDatabase();
|
|
9953
|
+
const name = normalizeName(input.name);
|
|
9954
|
+
const timestamp = now();
|
|
9955
|
+
const existing = getSearchView(name, d);
|
|
9956
|
+
if (existing) {
|
|
9957
|
+
d.run(`UPDATE saved_search_views
|
|
9958
|
+
SET description = ?, scope = ?, filters = ?, updated_at = ?
|
|
9959
|
+
WHERE id = ?`, [
|
|
9960
|
+
input.description ?? existing.description,
|
|
9961
|
+
normalizeScope(input.scope ?? existing.scope),
|
|
9962
|
+
JSON.stringify(input.filters ?? existing.filters),
|
|
9963
|
+
timestamp,
|
|
9964
|
+
existing.id
|
|
9965
|
+
]);
|
|
9966
|
+
return getSearchView(existing.id, d);
|
|
9967
|
+
}
|
|
9968
|
+
const id = uuid();
|
|
9969
|
+
d.run(`INSERT INTO saved_search_views (id, name, description, scope, filters, created_at, updated_at)
|
|
9970
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [
|
|
9971
|
+
id,
|
|
9972
|
+
name,
|
|
9973
|
+
input.description ?? null,
|
|
9974
|
+
normalizeScope(input.scope),
|
|
9975
|
+
JSON.stringify(input.filters ?? {}),
|
|
9976
|
+
timestamp,
|
|
9977
|
+
timestamp
|
|
9978
|
+
]);
|
|
9979
|
+
return getSearchView(id, d);
|
|
9980
|
+
}
|
|
9981
|
+
function getSearchView(idOrName, db) {
|
|
9982
|
+
const d = db || getDatabase();
|
|
9983
|
+
const row = d.query("SELECT * FROM saved_search_views WHERE id = ? OR name = ?").get(idOrName, idOrName);
|
|
9984
|
+
return row ? rowToSavedSearchView(row) : null;
|
|
9985
|
+
}
|
|
9986
|
+
function listSearchViews(scope, db) {
|
|
9987
|
+
const d = db || getDatabase();
|
|
9988
|
+
const normalizedScope = scope ? normalizeScope(scope) : null;
|
|
9989
|
+
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();
|
|
9990
|
+
return rows.map(rowToSavedSearchView);
|
|
9991
|
+
}
|
|
9992
|
+
function deleteSearchView(idOrName, db) {
|
|
9993
|
+
const d = db || getDatabase();
|
|
9994
|
+
const result = d.run("DELETE FROM saved_search_views WHERE id = ? OR name = ?", [idOrName, idOrName]);
|
|
9995
|
+
return result.changes > 0;
|
|
9996
|
+
}
|
|
9997
|
+
function runSearchView(idOrName, db) {
|
|
9998
|
+
const d = db || getDatabase();
|
|
9999
|
+
const view = getSearchView(idOrName, d);
|
|
10000
|
+
if (!view)
|
|
10001
|
+
throw new Error(`Saved search view not found: ${idOrName}`);
|
|
10002
|
+
return { ...runSavedSearch(view.filters, view.scope, d), view };
|
|
10003
|
+
}
|
|
10004
|
+
var init_saved_search_views = __esm(() => {
|
|
10005
|
+
init_database();
|
|
10006
|
+
init_local_fields();
|
|
10007
|
+
init_search();
|
|
10008
|
+
});
|
|
10009
|
+
|
|
9550
10010
|
// src/lib/claude-tasks.ts
|
|
9551
10011
|
import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
9552
10012
|
import { join as join5 } from "path";
|
|
@@ -10786,7 +11246,7 @@ function packageSource(version) {
|
|
|
10786
11246
|
function emptyCounts() {
|
|
10787
11247
|
return Object.fromEntries(dataKeys.map((key) => [key, 0]));
|
|
10788
11248
|
}
|
|
10789
|
-
function
|
|
11249
|
+
function parseJsonObject2(value) {
|
|
10790
11250
|
if (!value)
|
|
10791
11251
|
return {};
|
|
10792
11252
|
if (typeof value === "object" && !Array.isArray(value))
|
|
@@ -10832,22 +11292,25 @@ function rowToTask3(row) {
|
|
|
10832
11292
|
return {
|
|
10833
11293
|
...row,
|
|
10834
11294
|
tags: parseJsonArray(row.tags),
|
|
10835
|
-
metadata:
|
|
11295
|
+
metadata: parseJsonObject2(row.metadata),
|
|
10836
11296
|
requires_approval: Boolean(row.requires_approval)
|
|
10837
11297
|
};
|
|
10838
11298
|
}
|
|
10839
11299
|
function rowToTaskList2(row) {
|
|
10840
|
-
return { ...row, metadata:
|
|
11300
|
+
return { ...row, metadata: parseJsonObject2(row.metadata) };
|
|
10841
11301
|
}
|
|
10842
11302
|
function rowWithMetadata(row) {
|
|
10843
|
-
return { ...row, metadata:
|
|
11303
|
+
return { ...row, metadata: parseJsonObject2(row.metadata) };
|
|
10844
11304
|
}
|
|
10845
11305
|
function rowToRunEvent(row) {
|
|
10846
|
-
return { ...row, data:
|
|
11306
|
+
return { ...row, data: parseJsonObject2(row.data) };
|
|
10847
11307
|
}
|
|
10848
11308
|
function rowToCommit2(row) {
|
|
10849
11309
|
return { ...row, files_changed: row.files_changed ? parseJsonArray(row.files_changed) : null };
|
|
10850
11310
|
}
|
|
11311
|
+
function rowToSavedView(row) {
|
|
11312
|
+
return { ...row, filters: parseJsonObject2(row.filters) };
|
|
11313
|
+
}
|
|
10851
11314
|
function bridgeStats(data) {
|
|
10852
11315
|
return Object.fromEntries(dataKeys.map((key) => [key, data[key].length]));
|
|
10853
11316
|
}
|
|
@@ -10871,7 +11334,8 @@ function createLocalBridgeBundle(options = {}, db) {
|
|
|
10871
11334
|
task_files: queryByTaskIds(d, "SELECT * FROM task_files WHERE task_id IN (__TASK_IDS__) ORDER BY path, id", taskIds),
|
|
10872
11335
|
task_commits: queryByTaskIds(d, "SELECT * FROM task_commits WHERE task_id IN (__TASK_IDS__) ORDER BY created_at, id", taskIds).map(rowToCommit2),
|
|
10873
11336
|
task_git_refs: queryByTaskIds(d, "SELECT * FROM task_git_refs WHERE task_id IN (__TASK_IDS__) ORDER BY created_at, id", taskIds).map(rowWithMetadata),
|
|
10874
|
-
task_verifications: queryByTaskIds(d, "SELECT * FROM task_verifications WHERE task_id IN (__TASK_IDS__) ORDER BY run_at, id", taskIds)
|
|
11337
|
+
task_verifications: queryByTaskIds(d, "SELECT * FROM task_verifications WHERE task_id IN (__TASK_IDS__) ORDER BY run_at, id", taskIds),
|
|
11338
|
+
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)
|
|
10875
11339
|
};
|
|
10876
11340
|
const artifactContents = data.run_artifacts.map((artifact) => exportStoredArtifactContent({
|
|
10877
11341
|
id: artifact.id,
|
|
@@ -10910,6 +11374,8 @@ function validateLocalBridgeBundle(value) {
|
|
|
10910
11374
|
issues.push("data must be an object");
|
|
10911
11375
|
} else {
|
|
10912
11376
|
for (const key of dataKeys) {
|
|
11377
|
+
if (key === "saved_views" && data[key] === undefined)
|
|
11378
|
+
continue;
|
|
10913
11379
|
if (!Array.isArray(data[key]))
|
|
10914
11380
|
issues.push(`data.${key} must be an array`);
|
|
10915
11381
|
}
|
|
@@ -11082,7 +11548,8 @@ function importLocalBridgeBundle(bundle, options = {}, db) {
|
|
|
11082
11548
|
const conflictStrategy = options.conflictStrategy ?? "skip";
|
|
11083
11549
|
const data = {
|
|
11084
11550
|
...bundle.data,
|
|
11085
|
-
tasks: sortedTasks(bundle.data.tasks)
|
|
11551
|
+
tasks: sortedTasks(bundle.data.tasks),
|
|
11552
|
+
saved_views: bundle.data.saved_views ?? []
|
|
11086
11553
|
};
|
|
11087
11554
|
for (const key of dataKeys) {
|
|
11088
11555
|
for (const row of data[key]) {
|
|
@@ -11153,7 +11620,8 @@ var init_local_bridge = __esm(() => {
|
|
|
11153
11620
|
"task_files",
|
|
11154
11621
|
"task_commits",
|
|
11155
11622
|
"task_git_refs",
|
|
11156
|
-
"task_verifications"
|
|
11623
|
+
"task_verifications",
|
|
11624
|
+
"saved_views"
|
|
11157
11625
|
];
|
|
11158
11626
|
insertColumns = {
|
|
11159
11627
|
projects: ["id", "name", "path", "description", "task_list_id", "task_prefix", "task_counter", "created_at", "updated_at", "machine_id", "synced_at"],
|
|
@@ -11225,7 +11693,8 @@ var init_local_bridge = __esm(() => {
|
|
|
11225
11693
|
task_files: ["id", "task_id", "path", "status", "agent_id", "note", "created_at", "updated_at", "machine_id"],
|
|
11226
11694
|
task_commits: ["id", "task_id", "sha", "message", "author", "files_changed", "committed_at", "created_at"],
|
|
11227
11695
|
task_git_refs: ["id", "task_id", "ref_type", "name", "url", "provider", "metadata", "created_at", "updated_at"],
|
|
11228
|
-
task_verifications: ["id", "task_id", "command", "status", "output_summary", "artifact_path", "agent_id", "run_at", "created_at"]
|
|
11696
|
+
task_verifications: ["id", "task_id", "command", "status", "output_summary", "artifact_path", "agent_id", "run_at", "created_at"],
|
|
11697
|
+
saved_views: ["id", "name", "description", "scope", "filters", "created_at", "updated_at"]
|
|
11229
11698
|
};
|
|
11230
11699
|
tableByKey = {
|
|
11231
11700
|
projects: "projects",
|
|
@@ -11241,9 +11710,10 @@ var init_local_bridge = __esm(() => {
|
|
|
11241
11710
|
task_files: "task_files",
|
|
11242
11711
|
task_commits: "task_commits",
|
|
11243
11712
|
task_git_refs: "task_git_refs",
|
|
11244
|
-
task_verifications: "task_verifications"
|
|
11713
|
+
task_verifications: "task_verifications",
|
|
11714
|
+
saved_views: "saved_search_views"
|
|
11245
11715
|
};
|
|
11246
|
-
jsonColumns = new Set(["metadata", "tags", "data", "files_changed"]);
|
|
11716
|
+
jsonColumns = new Set(["metadata", "tags", "data", "files_changed", "filters"]);
|
|
11247
11717
|
});
|
|
11248
11718
|
|
|
11249
11719
|
// src/lib/local-encryption.ts
|
|
@@ -11818,7 +12288,8 @@ function emptyCounts2() {
|
|
|
11818
12288
|
task_files: 0,
|
|
11819
12289
|
task_commits: 0,
|
|
11820
12290
|
task_git_refs: 0,
|
|
11821
|
-
task_verifications: 0
|
|
12291
|
+
task_verifications: 0,
|
|
12292
|
+
saved_views: 0
|
|
11822
12293
|
};
|
|
11823
12294
|
}
|
|
11824
12295
|
function bridgeToMarkdownPayload(bundle) {
|
|
@@ -12069,6 +12540,61 @@ __export(exports_project_commands, {
|
|
|
12069
12540
|
});
|
|
12070
12541
|
import chalk4 from "chalk";
|
|
12071
12542
|
import { basename as basename3, resolve as resolve9 } from "path";
|
|
12543
|
+
function collectOption(value, previous = []) {
|
|
12544
|
+
return [...previous, value];
|
|
12545
|
+
}
|
|
12546
|
+
function splitList(value) {
|
|
12547
|
+
if (!value)
|
|
12548
|
+
return;
|
|
12549
|
+
const values = Array.isArray(value) ? value : [value];
|
|
12550
|
+
const items = values.flatMap((item) => item.split(",").map((part) => part.trim()).filter(Boolean));
|
|
12551
|
+
return items.length > 0 ? items : undefined;
|
|
12552
|
+
}
|
|
12553
|
+
function parseJsonObjectOption(value, label) {
|
|
12554
|
+
if (!value)
|
|
12555
|
+
return;
|
|
12556
|
+
try {
|
|
12557
|
+
const parsed = JSON.parse(value);
|
|
12558
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
12559
|
+
return parsed;
|
|
12560
|
+
} catch {}
|
|
12561
|
+
throw new Error(`${label} must be a JSON object`);
|
|
12562
|
+
}
|
|
12563
|
+
function buildSearchFilters(query, opts, projectId) {
|
|
12564
|
+
const filterPatch = parseJsonObjectOption(opts.filter, "--filter");
|
|
12565
|
+
const customFields = parseJsonObjectOption(opts.fieldCustom, "--field-custom");
|
|
12566
|
+
const labels = splitList(opts.fieldLabel);
|
|
12567
|
+
const tags = splitList(opts.tag);
|
|
12568
|
+
const statuses = splitList(opts.status)?.map(normalizeStatus);
|
|
12569
|
+
const filters = {
|
|
12570
|
+
query: query || opts.query,
|
|
12571
|
+
project_id: opts.allProjects ? undefined : projectId,
|
|
12572
|
+
status: statuses,
|
|
12573
|
+
priority: splitList(opts.priority),
|
|
12574
|
+
assigned_to: opts.assigned,
|
|
12575
|
+
agent_id: opts.agentId,
|
|
12576
|
+
task_list_id: opts.taskList,
|
|
12577
|
+
plan_id: opts.plan,
|
|
12578
|
+
task_id: opts.task,
|
|
12579
|
+
tags,
|
|
12580
|
+
created_after: opts.createdAfter,
|
|
12581
|
+
updated_after: opts.since || opts.updatedAfter,
|
|
12582
|
+
has_dependencies: opts.hasDeps ? true : undefined,
|
|
12583
|
+
is_blocked: opts.blocked ? true : undefined,
|
|
12584
|
+
depends_on: opts.dependsOn,
|
|
12585
|
+
blocks: opts.blocks,
|
|
12586
|
+
limit: opts.limit ? Number(opts.limit) : undefined,
|
|
12587
|
+
local_fields: labels || opts.fieldOwner || opts.fieldArea || opts.fieldSeverity || customFields ? {
|
|
12588
|
+
labels,
|
|
12589
|
+
owner: opts.fieldOwner,
|
|
12590
|
+
area: opts.fieldArea,
|
|
12591
|
+
severity: opts.fieldSeverity,
|
|
12592
|
+
custom: customFields
|
|
12593
|
+
} : undefined,
|
|
12594
|
+
...filterPatch
|
|
12595
|
+
};
|
|
12596
|
+
return Object.fromEntries(Object.entries(filters).filter(([, value]) => value !== undefined));
|
|
12597
|
+
}
|
|
12072
12598
|
function registerProjectCommands(program2) {
|
|
12073
12599
|
program2.command("project-bootstrap [path]").description("Discover a local workspace and initialize project task state").option("--name <name>", "Project display name").option("--task-list <slug>", "Default task list slug").option("--dry-run", "Show discovery without writing local state").action(async (inputPath, opts) => {
|
|
12074
12600
|
const globalOpts = program2.opts();
|
|
@@ -12121,35 +12647,126 @@ function registerProjectCommands(program2) {
|
|
|
12121
12647
|
console.log(chalk4.green("Comment added."));
|
|
12122
12648
|
}
|
|
12123
12649
|
});
|
|
12124
|
-
program2.command("search <query>").description("Search tasks").option("--status <status>", "Filter by status").option("--priority <p>", "Filter by priority").option("--assigned <agent>", "Filter by assigned agent").option("--since <date>", "Only tasks updated after this date (ISO)").option("--blocked", "Only blocked tasks (incomplete dependencies)").option("--has-deps", "Only tasks with dependencies").action((query, opts) => {
|
|
12650
|
+
program2.command("search <query>").description("Search local tasks, or run/save a cross-entity search view").option("--status <status>", "Filter by status").option("--priority <p>", "Filter by priority").option("--assigned <agent>", "Filter by assigned agent").option("--agent-id <agent>", "Filter by creator/run/comment agent").option("--task-list <id>", "Filter by task list").option("--plan <id>", "Filter by plan").option("--task <id>", "Filter runs/comments by task").option("--tag <tag>", "Filter by task tag (repeatable or comma-separated)", collectOption, []).option("--field-label <label>", "Filter by local field label (repeatable or comma-separated)", collectOption, []).option("--field-owner <owner>", "Filter by local field owner").option("--field-area <area>", "Filter by local field area").option("--field-severity <severity>", "Filter by local field severity").option("--field-custom <json>", "Filter by local custom fields as JSON").option("--since <date>", "Only tasks updated after this date (ISO)").option("--created-after <date>", "Only records created after this date (ISO)").option("--blocked", "Only blocked tasks (incomplete dependencies)").option("--has-deps", "Only tasks with dependencies").option("--depends-on <id>", "Only tasks that depend on a task").option("--blocks <id>", "Only tasks that block a task").option("--scope <scope>", "Search scope: tasks, projects, plans, runs, comments, all", "tasks").option("--limit <n>", "Maximum results", "100").option("--filter <json>", "Merge an advanced saved-search filter JSON object").option("--save-as <name>", "Save this search as a named view").option("--description <text>", "Saved view description").option("--all-projects", "Do not auto-scope the search to the current project").action((query, opts) => {
|
|
12125
12651
|
const globalOpts = program2.opts();
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
searchOpts
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
|
|
12652
|
+
try {
|
|
12653
|
+
const projectId = opts.allProjects ? undefined : autoProject(globalOpts);
|
|
12654
|
+
const scope = normalizeScope(opts.scope);
|
|
12655
|
+
const searchOpts = buildSearchFilters(query, opts, projectId);
|
|
12656
|
+
if (opts.saveAs) {
|
|
12657
|
+
const view = saveSearchView({
|
|
12658
|
+
name: opts.saveAs,
|
|
12659
|
+
description: opts.description,
|
|
12660
|
+
scope,
|
|
12661
|
+
filters: searchOpts
|
|
12662
|
+
});
|
|
12663
|
+
output(view, Boolean(globalOpts.json));
|
|
12664
|
+
if (!globalOpts.json)
|
|
12665
|
+
console.log(chalk4.green(`Saved view ${view.name}.`));
|
|
12666
|
+
return;
|
|
12667
|
+
}
|
|
12668
|
+
if (scope !== "tasks") {
|
|
12669
|
+
const result = runSavedSearch(searchOpts, scope);
|
|
12670
|
+
if (globalOpts.json) {
|
|
12671
|
+
output(result, true);
|
|
12672
|
+
return;
|
|
12673
|
+
}
|
|
12674
|
+
if (result.count === 0) {
|
|
12675
|
+
console.log(chalk4.dim(`No ${scope} results matching "${query}".`));
|
|
12676
|
+
return;
|
|
12677
|
+
}
|
|
12678
|
+
console.log(chalk4.bold(`${result.count} ${scope} result(s) for "${query}":
|
|
12679
|
+
`));
|
|
12680
|
+
for (const item of result.results) {
|
|
12681
|
+
const entity = item.entity;
|
|
12682
|
+
console.log(`${chalk4.cyan(item.entity_type)} ${entity.id?.slice?.(0, 8) || ""} ${entity.name || entity.title || entity.content || entity.summary || ""}`);
|
|
12683
|
+
}
|
|
12684
|
+
return;
|
|
12685
|
+
}
|
|
12686
|
+
const tasks = runSavedSearch(searchOpts, "tasks").results.map((item) => item.entity);
|
|
12687
|
+
if (globalOpts.json) {
|
|
12688
|
+
output(tasks, true);
|
|
12689
|
+
return;
|
|
12690
|
+
}
|
|
12691
|
+
if (tasks.length === 0) {
|
|
12692
|
+
console.log(chalk4.dim(`No tasks matching "${query}".`));
|
|
12693
|
+
return;
|
|
12694
|
+
}
|
|
12695
|
+
console.log(chalk4.bold(`${tasks.length} result(s) for "${query}":
|
|
12696
|
+
`));
|
|
12697
|
+
for (const t of tasks) {
|
|
12698
|
+
console.log(formatTaskLine(t));
|
|
12699
|
+
}
|
|
12700
|
+
} catch (e) {
|
|
12701
|
+
handleError(e);
|
|
12144
12702
|
}
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12703
|
+
});
|
|
12704
|
+
const views = program2.command("views").description("Manage local saved search views");
|
|
12705
|
+
views.command("save <name>").description("Save a local search view").option("--query <query>", "Search query").option("--scope <scope>", "Search scope: tasks, projects, plans, runs, comments, all", "tasks").option("--description <text>", "Description").option("--status <status>", "Filter by status").option("--priority <p>", "Filter by priority").option("--assigned <agent>", "Filter by assigned agent").option("--agent-id <agent>", "Filter by creator/run/comment agent").option("--task-list <id>", "Filter by task list").option("--plan <id>", "Filter by plan").option("--task <id>", "Filter runs/comments by task").option("--tag <tag>", "Filter by task tag (repeatable or comma-separated)", collectOption, []).option("--field-label <label>", "Filter by local field label (repeatable or comma-separated)", collectOption, []).option("--field-owner <owner>", "Filter by local field owner").option("--field-area <area>", "Filter by local field area").option("--field-severity <severity>", "Filter by local field severity").option("--field-custom <json>", "Filter by local custom fields as JSON").option("--since <date>", "Only records updated after this date (ISO)").option("--created-after <date>", "Only records created after this date (ISO)").option("--blocked", "Only blocked tasks").option("--has-deps", "Only tasks with dependencies").option("--depends-on <id>", "Only tasks that depend on a task").option("--blocks <id>", "Only tasks that block a task").option("--limit <n>", "Maximum results", "100").option("--filter <json>", "Merge an advanced saved-search filter JSON object").option("--all-projects", "Do not auto-scope the view to the current project").action((name, opts) => {
|
|
12706
|
+
const globalOpts = program2.opts();
|
|
12707
|
+
try {
|
|
12708
|
+
const projectId = opts.allProjects ? undefined : autoProject(globalOpts);
|
|
12709
|
+
const view = saveSearchView({
|
|
12710
|
+
name,
|
|
12711
|
+
description: opts.description,
|
|
12712
|
+
scope: normalizeScope(opts.scope),
|
|
12713
|
+
filters: buildSearchFilters(opts.query, opts, projectId)
|
|
12714
|
+
});
|
|
12715
|
+
output(view, Boolean(globalOpts.json));
|
|
12716
|
+
if (!globalOpts.json)
|
|
12717
|
+
console.log(chalk4.green(`Saved view ${view.name}.`));
|
|
12718
|
+
} catch (e) {
|
|
12719
|
+
handleError(e);
|
|
12148
12720
|
}
|
|
12149
|
-
|
|
12721
|
+
});
|
|
12722
|
+
views.command("list").description("List local saved search views").option("--scope <scope>", "Filter by scope").action((opts) => {
|
|
12723
|
+
const globalOpts = program2.opts();
|
|
12724
|
+
try {
|
|
12725
|
+
const rows = listSearchViews(opts.scope ? normalizeScope(opts.scope) : undefined);
|
|
12726
|
+
output(rows, Boolean(globalOpts.json));
|
|
12727
|
+
if (!globalOpts.json) {
|
|
12728
|
+
if (rows.length === 0) {
|
|
12729
|
+
console.log(chalk4.dim("No saved search views."));
|
|
12730
|
+
return;
|
|
12731
|
+
}
|
|
12732
|
+
for (const row of rows)
|
|
12733
|
+
console.log(`${chalk4.cyan(row.name)} ${chalk4.dim(`[${row.scope}]`)} ${JSON.stringify(row.filters)}`);
|
|
12734
|
+
}
|
|
12735
|
+
} catch (e) {
|
|
12736
|
+
handleError(e);
|
|
12737
|
+
}
|
|
12738
|
+
});
|
|
12739
|
+
views.command("run <name>").description("Run a local saved search view").action((name) => {
|
|
12740
|
+
const globalOpts = program2.opts();
|
|
12741
|
+
try {
|
|
12742
|
+
const result = runSearchView(name);
|
|
12743
|
+
if (globalOpts.json) {
|
|
12744
|
+
output(result, true);
|
|
12745
|
+
return;
|
|
12746
|
+
}
|
|
12747
|
+
console.log(chalk4.bold(`${result.count} result(s) for view "${result.view?.name || name}":
|
|
12150
12748
|
`));
|
|
12151
|
-
|
|
12152
|
-
|
|
12749
|
+
for (const item of result.results) {
|
|
12750
|
+
if (item.entity_type === "tasks") {
|
|
12751
|
+
console.log(formatTaskLine(item.entity));
|
|
12752
|
+
continue;
|
|
12753
|
+
}
|
|
12754
|
+
const entity = item.entity;
|
|
12755
|
+
console.log(`${chalk4.cyan(item.entity_type)} ${entity.id?.slice?.(0, 8) || ""} ${entity.name || entity.title || entity.content || entity.summary || ""}`);
|
|
12756
|
+
}
|
|
12757
|
+
} catch (e) {
|
|
12758
|
+
handleError(e);
|
|
12759
|
+
}
|
|
12760
|
+
});
|
|
12761
|
+
views.command("delete <name>").description("Delete a local saved search view").action((name) => {
|
|
12762
|
+
const globalOpts = program2.opts();
|
|
12763
|
+
try {
|
|
12764
|
+
const deleted = deleteSearchView(name);
|
|
12765
|
+
output({ deleted }, Boolean(globalOpts.json));
|
|
12766
|
+
if (!globalOpts.json)
|
|
12767
|
+
console.log(deleted ? chalk4.green(`Deleted view ${name}.`) : chalk4.dim(`View not found: ${name}`));
|
|
12768
|
+
} catch (e) {
|
|
12769
|
+
handleError(e);
|
|
12153
12770
|
}
|
|
12154
12771
|
});
|
|
12155
12772
|
program2.command("deps <id>").description("Manage task dependencies").option("--needs <dep-id>", "Add dependency (this task needs dep-id)").option("--remove <dep-id>", "Remove dependency").option("--graph", "Show the dependency graph instead of direct edges").option("--direction <direction>", "Graph direction: up, down, or both", "both").action(async (id, opts) => {
|
|
@@ -12576,7 +13193,7 @@ var init_project_commands = __esm(() => {
|
|
|
12576
13193
|
init_database();
|
|
12577
13194
|
init_projects();
|
|
12578
13195
|
init_comments();
|
|
12579
|
-
|
|
13196
|
+
init_saved_search_views();
|
|
12580
13197
|
init_sync();
|
|
12581
13198
|
init_config();
|
|
12582
13199
|
init_helpers();
|
|
@@ -18614,101 +19231,6 @@ var init_task_dedupe = __esm(() => {
|
|
|
18614
19231
|
PY_STACK_RE = /File\s+"([^"]+)",\s+line\s+\d+,\s+in\s+([^\s]+)/i;
|
|
18615
19232
|
});
|
|
18616
19233
|
|
|
18617
|
-
// src/lib/local-fields.ts
|
|
18618
|
-
function normalizeList(values) {
|
|
18619
|
-
return [...new Set((values || []).map((value) => value.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
18620
|
-
}
|
|
18621
|
-
function metadataFields(task) {
|
|
18622
|
-
const value = task.metadata[LOCAL_FIELDS_KEY];
|
|
18623
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
18624
|
-
}
|
|
18625
|
-
function sameCustomValue(actual, expected) {
|
|
18626
|
-
return JSON.stringify(actual) === JSON.stringify(expected);
|
|
18627
|
-
}
|
|
18628
|
-
function hasOwnField(fields, key) {
|
|
18629
|
-
return Object.prototype.hasOwnProperty.call(fields, key);
|
|
18630
|
-
}
|
|
18631
|
-
function getTaskLocalFields(taskId, db) {
|
|
18632
|
-
const d = db || getDatabase();
|
|
18633
|
-
const task = getTask(taskId, d);
|
|
18634
|
-
if (!task)
|
|
18635
|
-
throw new TaskNotFoundError(taskId);
|
|
18636
|
-
const fields = metadataFields(task);
|
|
18637
|
-
return {
|
|
18638
|
-
labels: normalizeList(fields.labels),
|
|
18639
|
-
priority: task.priority,
|
|
18640
|
-
severity: typeof fields.severity === "string" ? fields.severity : null,
|
|
18641
|
-
owner: hasOwnField(fields, "owner") ? typeof fields.owner === "string" ? fields.owner : null : task.assigned_to,
|
|
18642
|
-
area: typeof fields.area === "string" ? fields.area : null,
|
|
18643
|
-
custom: fields.custom && typeof fields.custom === "object" && !Array.isArray(fields.custom) ? fields.custom : {}
|
|
18644
|
-
};
|
|
18645
|
-
}
|
|
18646
|
-
function setTaskLocalFields(taskId, input, db) {
|
|
18647
|
-
const d = db || getDatabase();
|
|
18648
|
-
const task = getTask(taskId, d);
|
|
18649
|
-
if (!task)
|
|
18650
|
-
throw new TaskNotFoundError(taskId);
|
|
18651
|
-
const currentFields = getTaskLocalFields(taskId, d);
|
|
18652
|
-
const labels = input.labels !== undefined ? normalizeList(input.labels) : currentFields.labels;
|
|
18653
|
-
const custom = input.custom !== undefined ? redactValue(input.merge_custom === false ? input.custom : { ...currentFields.custom, ...input.custom }) : currentFields.custom;
|
|
18654
|
-
const nextFields = {
|
|
18655
|
-
labels,
|
|
18656
|
-
priority: input.priority || task.priority,
|
|
18657
|
-
severity: input.severity !== undefined ? input.severity : currentFields.severity,
|
|
18658
|
-
owner: input.owner !== undefined ? input.owner : currentFields.owner,
|
|
18659
|
-
area: input.area !== undefined ? input.area : currentFields.area,
|
|
18660
|
-
custom
|
|
18661
|
-
};
|
|
18662
|
-
const nextMetadata = {
|
|
18663
|
-
...task.metadata,
|
|
18664
|
-
[LOCAL_FIELDS_KEY]: nextFields
|
|
18665
|
-
};
|
|
18666
|
-
const previousLabels = new Set(currentFields.labels);
|
|
18667
|
-
const nextTags = normalizeList([...task.tags.filter((tag) => !previousLabels.has(tag)), ...labels]);
|
|
18668
|
-
const updates = {
|
|
18669
|
-
version: task.version,
|
|
18670
|
-
priority: input.priority,
|
|
18671
|
-
tags: nextTags,
|
|
18672
|
-
metadata: nextMetadata
|
|
18673
|
-
};
|
|
18674
|
-
if (input.owner !== undefined)
|
|
18675
|
-
updates.assigned_to = nextFields.owner;
|
|
18676
|
-
return updateTask(taskId, updates, d);
|
|
18677
|
-
}
|
|
18678
|
-
function queryTasksByLocalFields(query, db) {
|
|
18679
|
-
const d = db || getDatabase();
|
|
18680
|
-
const tasks = listTasks({
|
|
18681
|
-
priority: query.priority,
|
|
18682
|
-
tags: query.labels,
|
|
18683
|
-
limit: 1e4
|
|
18684
|
-
}, d);
|
|
18685
|
-
const matches = tasks.filter((task) => {
|
|
18686
|
-
const fields = getTaskLocalFields(task.id, d);
|
|
18687
|
-
if (query.labels && !query.labels.every((label) => fields.labels.includes(label)))
|
|
18688
|
-
return false;
|
|
18689
|
-
if (query.severity && fields.severity !== query.severity)
|
|
18690
|
-
return false;
|
|
18691
|
-
if (query.owner && fields.owner !== query.owner)
|
|
18692
|
-
return false;
|
|
18693
|
-
if (query.area && fields.area !== query.area)
|
|
18694
|
-
return false;
|
|
18695
|
-
if (query.custom) {
|
|
18696
|
-
for (const [key, expected] of Object.entries(query.custom)) {
|
|
18697
|
-
if (!sameCustomValue(fields.custom[key], expected))
|
|
18698
|
-
return false;
|
|
18699
|
-
}
|
|
18700
|
-
}
|
|
18701
|
-
return true;
|
|
18702
|
-
});
|
|
18703
|
-
return matches.slice(0, query.limit || 100);
|
|
18704
|
-
}
|
|
18705
|
-
var LOCAL_FIELDS_KEY = "local_fields";
|
|
18706
|
-
var init_local_fields = __esm(() => {
|
|
18707
|
-
init_database();
|
|
18708
|
-
init_tasks();
|
|
18709
|
-
init_types();
|
|
18710
|
-
});
|
|
18711
|
-
|
|
18712
19234
|
// src/lib/activity-timeline.ts
|
|
18713
19235
|
var exports_activity_timeline = {};
|
|
18714
19236
|
__export(exports_activity_timeline, {
|
|
@@ -19552,7 +20074,7 @@ __export(exports_query_commands, {
|
|
|
19552
20074
|
registerQueryCommands: () => registerQueryCommands
|
|
19553
20075
|
});
|
|
19554
20076
|
import chalk7 from "chalk";
|
|
19555
|
-
function
|
|
20077
|
+
function parseJsonObjectOption2(value, label) {
|
|
19556
20078
|
if (!value)
|
|
19557
20079
|
return;
|
|
19558
20080
|
try {
|
|
@@ -20904,7 +21426,7 @@ Repairs`));
|
|
|
20904
21426
|
fields.command("set <task-id>").description("Set local fields for a task").option("--labels <labels>", "Comma-separated labels").option("--priority <priority>", "Priority: low, medium, high, critical").option("--severity <severity>", "Local severity, for example s0, s1, s2").option("--owner <owner>", "Local owner or responsible agent").option("--area <area>", "Local area or component").option("--custom <json>", "Custom fields as a JSON object").option("--field <pairs...>", "Custom key=value pairs").option("--replace-custom", "Replace custom fields instead of merging").option("-j, --json", "Output as JSON").action((taskId, opts) => {
|
|
20905
21427
|
const globalOpts = program2.opts();
|
|
20906
21428
|
try {
|
|
20907
|
-
const custom = mergeCustomFields(
|
|
21429
|
+
const custom = mergeCustomFields(parseJsonObjectOption2(opts.custom, "--custom"), parseFieldPairs(opts.field));
|
|
20908
21430
|
const input = {
|
|
20909
21431
|
labels: parseCsvOption(opts.labels),
|
|
20910
21432
|
priority: parsePriority(opts.priority),
|
|
@@ -20934,7 +21456,7 @@ Repairs`));
|
|
|
20934
21456
|
severity: opts.severity,
|
|
20935
21457
|
owner: opts.owner,
|
|
20936
21458
|
area: opts.area,
|
|
20937
|
-
custom: mergeCustomFields(
|
|
21459
|
+
custom: mergeCustomFields(parseJsonObjectOption2(opts.custom, "--custom"), parseFieldPairs(opts.field)),
|
|
20938
21460
|
limit: Number(opts.limit)
|
|
20939
21461
|
};
|
|
20940
21462
|
const tasks = queryTasksByLocalFields(query);
|
|
@@ -21018,7 +21540,7 @@ Repairs`));
|
|
|
21018
21540
|
source_type: opts.sourceType,
|
|
21019
21541
|
source_name: opts.sourceName || opts.file,
|
|
21020
21542
|
source_url: opts.sourceUrl,
|
|
21021
|
-
metadata:
|
|
21543
|
+
metadata: parseJsonObjectOption2(opts.metadata, "--metadata"),
|
|
21022
21544
|
project_id: autoProject(globalOpts) || undefined,
|
|
21023
21545
|
priority: opts.priority,
|
|
21024
21546
|
tags: opts.tags ? String(opts.tags).split(",").map((tag) => tag.trim()).filter(Boolean) : undefined,
|
|
@@ -25810,6 +26332,10 @@ var init_token_utils = __esm(() => {
|
|
|
25810
26332
|
"request_task_review",
|
|
25811
26333
|
"reschedule_task",
|
|
25812
26334
|
"search_tasks",
|
|
26335
|
+
"save_search_view",
|
|
26336
|
+
"list_search_views",
|
|
26337
|
+
"run_search_view",
|
|
26338
|
+
"delete_search_view",
|
|
25813
26339
|
"standup",
|
|
25814
26340
|
"set_task_contract",
|
|
25815
26341
|
"task_context",
|
|
@@ -27601,6 +28127,76 @@ ${lines.join(`
|
|
|
27601
28127
|
}
|
|
27602
28128
|
});
|
|
27603
28129
|
}
|
|
28130
|
+
if (shouldRegisterTool("save_search_view")) {
|
|
28131
|
+
server.tool("save_search_view", "Save a local search view for tasks, projects, plans, runs, comments, or all records.", {
|
|
28132
|
+
name: exports_external.string().describe("Saved view name"),
|
|
28133
|
+
query: exports_external.string().optional().describe("Search query"),
|
|
28134
|
+
scope: exports_external.enum(["all", "tasks", "projects", "plans", "runs", "comments"]).optional(),
|
|
28135
|
+
description: exports_external.string().optional(),
|
|
28136
|
+
project_id: exports_external.string().optional(),
|
|
28137
|
+
status: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional(),
|
|
28138
|
+
priority: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional(),
|
|
28139
|
+
assigned_to: exports_external.string().optional(),
|
|
28140
|
+
agent_id: exports_external.string().optional(),
|
|
28141
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
28142
|
+
limit: exports_external.number().optional()
|
|
28143
|
+
}, async ({ name, scope, description, ...filters }) => {
|
|
28144
|
+
try {
|
|
28145
|
+
const { saveSearchView: saveSearchView2 } = (init_saved_search_views(), __toCommonJS(exports_saved_search_views));
|
|
28146
|
+
const view = saveSearchView2({
|
|
28147
|
+
name,
|
|
28148
|
+
description,
|
|
28149
|
+
scope,
|
|
28150
|
+
filters: {
|
|
28151
|
+
...filters,
|
|
28152
|
+
project_id: filters.project_id ? resolveId(filters.project_id, "projects") : undefined
|
|
28153
|
+
}
|
|
28154
|
+
});
|
|
28155
|
+
return { content: [{ type: "text", text: JSON.stringify(view, null, 2) }] };
|
|
28156
|
+
} catch (e) {
|
|
28157
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28158
|
+
}
|
|
28159
|
+
});
|
|
28160
|
+
}
|
|
28161
|
+
if (shouldRegisterTool("list_search_views")) {
|
|
28162
|
+
server.tool("list_search_views", "List local saved search views.", {
|
|
28163
|
+
scope: exports_external.enum(["all", "tasks", "projects", "plans", "runs", "comments"]).optional()
|
|
28164
|
+
}, async ({ scope }) => {
|
|
28165
|
+
try {
|
|
28166
|
+
const { listSearchViews: listSearchViews2 } = (init_saved_search_views(), __toCommonJS(exports_saved_search_views));
|
|
28167
|
+
const views = listSearchViews2(scope);
|
|
28168
|
+
return { content: [{ type: "text", text: JSON.stringify(views, null, 2) }] };
|
|
28169
|
+
} catch (e) {
|
|
28170
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28171
|
+
}
|
|
28172
|
+
});
|
|
28173
|
+
}
|
|
28174
|
+
if (shouldRegisterTool("run_search_view")) {
|
|
28175
|
+
server.tool("run_search_view", "Run a local saved search view and return stable JSON results.", {
|
|
28176
|
+
name: exports_external.string().describe("Saved view name or id")
|
|
28177
|
+
}, async ({ name }) => {
|
|
28178
|
+
try {
|
|
28179
|
+
const { runSearchView: runSearchView2 } = (init_saved_search_views(), __toCommonJS(exports_saved_search_views));
|
|
28180
|
+
const result = runSearchView2(name);
|
|
28181
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
28182
|
+
} catch (e) {
|
|
28183
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28184
|
+
}
|
|
28185
|
+
});
|
|
28186
|
+
}
|
|
28187
|
+
if (shouldRegisterTool("delete_search_view")) {
|
|
28188
|
+
server.tool("delete_search_view", "Delete a local saved search view.", {
|
|
28189
|
+
name: exports_external.string().describe("Saved view name or id")
|
|
28190
|
+
}, async ({ name }) => {
|
|
28191
|
+
try {
|
|
28192
|
+
const { deleteSearchView: deleteSearchView2 } = (init_saved_search_views(), __toCommonJS(exports_saved_search_views));
|
|
28193
|
+
const deleted = deleteSearchView2(name);
|
|
28194
|
+
return { content: [{ type: "text", text: JSON.stringify({ deleted }, null, 2) }] };
|
|
28195
|
+
} catch (e) {
|
|
28196
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
28197
|
+
}
|
|
28198
|
+
});
|
|
28199
|
+
}
|
|
27604
28200
|
}
|
|
27605
28201
|
var init_task_project_tools = __esm(() => {
|
|
27606
28202
|
init_zod();
|
|
@@ -28949,6 +29545,10 @@ function registerTaskMetaTools(server, ctx) {
|
|
|
28949
29545
|
reschedule_task: "reschedule_task \u2014 Update deadline. Params: task_id, deadline, version",
|
|
28950
29546
|
prioritize_task: "prioritize_task \u2014 Set priority. Params: task_id, priority, version",
|
|
28951
29547
|
search_tasks: "search_tasks \u2014 Full-text search. Params: query, project_id, status, limit",
|
|
29548
|
+
save_search_view: "save_search_view \u2014 Save a local search view across tasks, projects, plans, runs, comments, or all records. Params: name, query, scope, description, project_id, status, priority, assigned_to, agent_id, tags, limit",
|
|
29549
|
+
list_search_views: "list_search_views \u2014 List local saved search views. Params: scope",
|
|
29550
|
+
run_search_view: "run_search_view \u2014 Run a local saved search view and return stable JSON results. Params: name",
|
|
29551
|
+
delete_search_view: "delete_search_view \u2014 Delete a local saved search view. Params: name",
|
|
28952
29552
|
get_my_tasks: "get_my_tasks \u2014 Get tasks for calling agent. Params: agent_id, status, project_id, limit",
|
|
28953
29553
|
get_next_task: "get_next_task \u2014 Get the next available task without claiming it. Params: agent_id, project_id, task_list_id, plan_id, tags",
|
|
28954
29554
|
claim_next_task: "claim_next_task \u2014 Atomically claim and start the next available task. Params: agent_id, project_id, task_list_id, plan_id, tags, steal_stale, stale_minutes",
|
|
@@ -29281,7 +29881,7 @@ __export(exports_verification_providers, {
|
|
|
29281
29881
|
discoverVerificationProviderCapabilities: () => discoverVerificationProviderCapabilities
|
|
29282
29882
|
});
|
|
29283
29883
|
import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
|
|
29284
|
-
function
|
|
29884
|
+
function normalizeName2(name) {
|
|
29285
29885
|
const normalized = name.trim().toLowerCase();
|
|
29286
29886
|
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
|
|
29287
29887
|
throw new Error("verification provider name must use lowercase letters, numbers, dashes, or underscores");
|
|
@@ -29300,10 +29900,10 @@ function timeoutMs(value) {
|
|
|
29300
29900
|
return Math.max(1, Math.min(24 * 60 * 60000, Math.floor(value)));
|
|
29301
29901
|
}
|
|
29302
29902
|
function getProvider(name) {
|
|
29303
|
-
return loadConfig().verification_providers?.[
|
|
29903
|
+
return loadConfig().verification_providers?.[normalizeName2(name)] || null;
|
|
29304
29904
|
}
|
|
29305
29905
|
function upsertVerificationProvider(input) {
|
|
29306
|
-
const name =
|
|
29906
|
+
const name = normalizeName2(input.name);
|
|
29307
29907
|
const config = loadConfig();
|
|
29308
29908
|
const existing = config.verification_providers?.[name];
|
|
29309
29909
|
const timestamp = new Date().toISOString();
|
|
@@ -29333,7 +29933,7 @@ function listVerificationProviders() {
|
|
|
29333
29933
|
return Object.values(loadConfig().verification_providers || {}).sort((a, b) => a.name.localeCompare(b.name));
|
|
29334
29934
|
}
|
|
29335
29935
|
function removeVerificationProvider(name) {
|
|
29336
|
-
const normalized =
|
|
29936
|
+
const normalized = normalizeName2(name);
|
|
29337
29937
|
const config = loadConfig();
|
|
29338
29938
|
if (!config.verification_providers?.[normalized])
|
|
29339
29939
|
return false;
|