@hasna/todos 0.11.51 → 0.11.53
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 +14 -1
- package/dashboard/dist/assets/{index-C3fBxEWP.js → index-aJefI7kh.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
- package/dist/cli/commands/machines.d.ts.map +1 -1
- package/dist/cli/commands/storage-commands.d.ts +3 -0
- package/dist/cli/commands/storage-commands.d.ts.map +1 -0
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts +6 -0
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +3342 -450
- package/dist/contracts.js +289 -8
- package/dist/db/artifacts.d.ts.map +1 -1
- package/dist/db/builtin-templates.d.ts +0 -1
- package/dist/db/builtin-templates.d.ts.map +1 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/tags.d.ts +26 -0
- package/dist/db/tags.d.ts.map +1 -0
- package/dist/db/task-commits.d.ts +14 -6
- package/dist/db/task-commits.d.ts.map +1 -1
- package/dist/db/tasks.d.ts +1 -1
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/index.d.ts +32 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2473 -200
- package/dist/lib/agent-adapter-docs.d.ts +1 -1
- package/dist/lib/agent-adapter-docs.d.ts.map +1 -1
- package/dist/lib/agent-coordination.d.ts.map +1 -1
- package/dist/lib/agent-run-dispatcher.d.ts +5 -2
- package/dist/lib/agent-run-dispatcher.d.ts.map +1 -1
- package/dist/lib/agent-workflow-demo.d.ts +1 -1
- package/dist/lib/agent-workflow-demo.d.ts.map +1 -1
- package/dist/lib/db-backup.d.ts.map +1 -1
- package/dist/lib/headless-boundaries.d.ts +2 -2
- package/dist/lib/headless-boundaries.d.ts.map +1 -1
- package/dist/lib/import-export-bridge.d.ts.map +1 -1
- package/dist/lib/inbox-intake.d.ts.map +1 -1
- package/dist/lib/issue-importers.d.ts.map +1 -1
- package/dist/lib/machine-topology.d.ts +1 -1
- package/dist/lib/machine-topology.d.ts.map +1 -1
- package/dist/lib/native-storage-status.d.ts +65 -0
- package/dist/lib/native-storage-status.d.ts.map +1 -0
- package/dist/lib/nl-intake.d.ts.map +1 -1
- package/dist/lib/notification-reminders.d.ts.map +1 -1
- package/dist/lib/plan-execution.d.ts +1 -0
- package/dist/lib/plan-execution.d.ts.map +1 -1
- package/dist/lib/release-checks.d.ts.map +1 -1
- package/dist/lib/resource-snapshots.d.ts.map +1 -1
- package/dist/lib/saved-views.d.ts.map +1 -1
- package/dist/lib/secret-redaction.d.ts +1 -1
- package/dist/lib/secret-redaction.d.ts.map +1 -1
- package/dist/lib/task-scheduling.d.ts +1 -1
- package/dist/lib/task-scheduling.d.ts.map +1 -1
- package/dist/lib/template-library.d.ts +1 -1
- package/dist/lib/template-library.d.ts.map +1 -1
- package/dist/lib/user-scaffolds.d.ts.map +1 -1
- package/dist/lib/verification-evidence.d.ts +4 -4
- package/dist/lib/verification-evidence.d.ts.map +1 -1
- package/dist/lib/verification-providers.d.ts +3 -3
- package/dist/lib/verification-providers.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +562 -55
- package/dist/mcp/tools/task-crud.d.ts.map +1 -1
- package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
- package/dist/registry.js +289 -8
- package/dist/release-provenance.json +7 -0
- package/dist/server/index.js +588 -57
- package/dist/server/serve.d.ts.map +1 -1
- package/dist/storage/config.d.ts +86 -0
- package/dist/storage/config.d.ts.map +1 -0
- package/dist/storage/factory.d.ts +17 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/hybrid.d.ts +26 -0
- package/dist/storage/hybrid.d.ts.map +1 -0
- package/dist/storage/index.d.ts +15 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/local-sqlite.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts +11 -0
- package/dist/storage/postgres-adapter.d.ts.map +1 -0
- package/dist/storage/postgres-sync.d.ts +44 -0
- package/dist/storage/postgres-sync.d.ts.map +1 -0
- package/dist/storage/s3-artifact-sync.d.ts +75 -0
- package/dist/storage/s3-artifact-sync.d.ts.map +1 -0
- package/dist/storage/s3-artifacts.d.ts +51 -0
- package/dist/storage/s3-artifacts.d.ts.map +1 -0
- package/dist/storage/sqlite-snapshot.d.ts +5 -0
- package/dist/storage/sqlite-snapshot.d.ts.map +1 -0
- package/dist/storage.d.ts +2 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +2053 -10
- package/package.json +1 -1
- package/dist/cli/brains.d.ts +0 -3
- package/dist/cli/brains.d.ts.map +0 -1
- package/dist/db/file-locks.d.ts +0 -43
- package/dist/db/file-locks.d.ts.map +0 -1
- package/dist/db/project-agent-roles.d.ts +0 -34
- package/dist/db/project-agent-roles.d.ts.map +0 -1
- package/dist/db/task-claim.d.ts +0 -7
- package/dist/db/task-claim.d.ts.map +0 -1
- package/dist/db/task-workflow.d.ts +0 -7
- package/dist/db/task-workflow.d.ts.map +0 -1
- package/dist/lib/north-star.d.ts +0 -33
- package/dist/lib/north-star.d.ts.map +0 -1
- package/dist/lib/public-release-gate.d.ts +0 -57
- package/dist/lib/public-release-gate.d.ts.map +0 -1
- package/dist/lib/task-runner.d.ts +0 -101
- package/dist/lib/task-runner.d.ts.map +0 -1
- package/dist/mcp/tools/access-profiles.d.ts +0 -8
- package/dist/mcp/tools/access-profiles.d.ts.map +0 -1
- package/dist/mcp/tools/activity-audit.d.ts +0 -9
- package/dist/mcp/tools/activity-audit.d.ts.map +0 -1
- package/dist/mcp/tools/agent-adapter-docs.d.ts +0 -8
- package/dist/mcp/tools/agent-adapter-docs.d.ts.map +0 -1
- package/dist/mcp/tools/agent-coordination.d.ts +0 -9
- package/dist/mcp/tools/agent-coordination.d.ts.map +0 -1
- package/dist/mcp/tools/agent-runs.d.ts +0 -9
- package/dist/mcp/tools/agent-runs.d.ts.map +0 -1
- package/dist/mcp/tools/agent-workflow-demo.d.ts +0 -8
- package/dist/mcp/tools/agent-workflow-demo.d.ts.map +0 -1
- package/dist/mcp/tools/approval-gates.d.ts +0 -9
- package/dist/mcp/tools/approval-gates.d.ts.map +0 -1
- package/dist/mcp/tools/artifacts.d.ts +0 -9
- package/dist/mcp/tools/artifacts.d.ts.map +0 -1
- package/dist/mcp/tools/branch-work-plans.d.ts +0 -8
- package/dist/mcp/tools/branch-work-plans.d.ts.map +0 -1
- package/dist/mcp/tools/cli-docs.d.ts +0 -8
- package/dist/mcp/tools/cli-docs.d.ts.map +0 -1
- package/dist/mcp/tools/command-aliases.d.ts +0 -8
- package/dist/mcp/tools/command-aliases.d.ts.map +0 -1
- package/dist/mcp/tools/context-packs.d.ts +0 -9
- package/dist/mcp/tools/context-packs.d.ts.map +0 -1
- package/dist/mcp/tools/crypto.d.ts +0 -8
- package/dist/mcp/tools/crypto.d.ts.map +0 -1
- package/dist/mcp/tools/db-backup.d.ts +0 -8
- package/dist/mcp/tools/db-backup.d.ts.map +0 -1
- package/dist/mcp/tools/decision-records.d.ts +0 -9
- package/dist/mcp/tools/decision-records.d.ts.map +0 -1
- package/dist/mcp/tools/dependency-graph.d.ts +0 -9
- package/dist/mcp/tools/dependency-graph.d.ts.map +0 -1
- package/dist/mcp/tools/failure-triage.d.ts +0 -9
- package/dist/mcp/tools/failure-triage.d.ts.map +0 -1
- package/dist/mcp/tools/feature-manifest.d.ts +0 -8
- package/dist/mcp/tools/feature-manifest.d.ts.map +0 -1
- package/dist/mcp/tools/git-traceability.d.ts +0 -9
- package/dist/mcp/tools/git-traceability.d.ts.map +0 -1
- package/dist/mcp/tools/goal.d.ts +0 -9
- package/dist/mcp/tools/goal.d.ts.map +0 -1
- package/dist/mcp/tools/handoff-packets.d.ts +0 -9
- package/dist/mcp/tools/handoff-packets.d.ts.map +0 -1
- package/dist/mcp/tools/import-export-bridge.d.ts +0 -9
- package/dist/mcp/tools/import-export-bridge.d.ts.map +0 -1
- package/dist/mcp/tools/inbox-intake.d.ts +0 -9
- package/dist/mcp/tools/inbox-intake.d.ts.map +0 -1
- package/dist/mcp/tools/issue-importers.d.ts +0 -9
- package/dist/mcp/tools/issue-importers.d.ts.map +0 -1
- package/dist/mcp/tools/json-schemas.d.ts +0 -8
- package/dist/mcp/tools/json-schemas.d.ts.map +0 -1
- package/dist/mcp/tools/machine-topology.d.ts +0 -8
- package/dist/mcp/tools/machine-topology.d.ts.map +0 -1
- package/dist/mcp/tools/mention-resolver.d.ts +0 -9
- package/dist/mcp/tools/mention-resolver.d.ts.map +0 -1
- package/dist/mcp/tools/nl-intake.d.ts +0 -9
- package/dist/mcp/tools/nl-intake.d.ts.map +0 -1
- package/dist/mcp/tools/notification-reminders.d.ts +0 -9
- package/dist/mcp/tools/notification-reminders.d.ts.map +0 -1
- package/dist/mcp/tools/parity.d.ts +0 -8
- package/dist/mcp/tools/parity.d.ts.map +0 -1
- package/dist/mcp/tools/plan-execution.d.ts +0 -9
- package/dist/mcp/tools/plan-execution.d.ts.map +0 -1
- package/dist/mcp/tools/policy-packs.d.ts +0 -9
- package/dist/mcp/tools/policy-packs.d.ts.map +0 -1
- package/dist/mcp/tools/project-bootstrap.d.ts +0 -8
- package/dist/mcp/tools/project-bootstrap.d.ts.map +0 -1
- package/dist/mcp/tools/release-checks.d.ts +0 -8
- package/dist/mcp/tools/release-checks.d.ts.map +0 -1
- package/dist/mcp/tools/release-notes.d.ts +0 -9
- package/dist/mcp/tools/release-notes.d.ts.map +0 -1
- package/dist/mcp/tools/report-exports.d.ts +0 -9
- package/dist/mcp/tools/report-exports.d.ts.map +0 -1
- package/dist/mcp/tools/resource-subscriptions.d.ts +0 -8
- package/dist/mcp/tools/resource-subscriptions.d.ts.map +0 -1
- package/dist/mcp/tools/run-records.d.ts +0 -9
- package/dist/mcp/tools/run-records.d.ts.map +0 -1
- package/dist/mcp/tools/sandbox.d.ts +0 -8
- package/dist/mcp/tools/sandbox.d.ts.map +0 -1
- package/dist/mcp/tools/saved-views.d.ts +0 -9
- package/dist/mcp/tools/saved-views.d.ts.map +0 -1
- package/dist/mcp/tools/secret-redaction.d.ts +0 -8
- package/dist/mcp/tools/secret-redaction.d.ts.map +0 -1
- package/dist/mcp/tools/task-dedupe.d.ts +0 -9
- package/dist/mcp/tools/task-dedupe.d.ts.map +0 -1
- package/dist/mcp/tools/task-scheduling.d.ts +0 -9
- package/dist/mcp/tools/task-scheduling.d.ts.map +0 -1
- package/dist/mcp/tools/template-library.d.ts +0 -8
- package/dist/mcp/tools/template-library.d.ts.map +0 -1
- package/dist/mcp/tools/terminal-notifications.d.ts +0 -9
- package/dist/mcp/tools/terminal-notifications.d.ts.map +0 -1
- package/dist/mcp/tools/todos-md.d.ts +0 -8
- package/dist/mcp/tools/todos-md.d.ts.map +0 -1
- package/dist/mcp/tools/user-scaffolds.d.ts +0 -8
- package/dist/mcp/tools/user-scaffolds.d.ts.map +0 -1
- package/dist/mcp/tools/verification.d.ts +0 -9
- package/dist/mcp/tools/verification.d.ts.map +0 -1
- package/dist/mcp/tools/webhooks.d.ts +0 -8
- package/dist/mcp/tools/webhooks.d.ts.map +0 -1
- package/dist/mcp/tools/workspace-trust.d.ts +0 -8
- package/dist/mcp/tools/workspace-trust.d.ts.map +0 -1
- package/dist/test/no-network.d.ts +0 -7
- package/dist/test/no-network.d.ts.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -1273,6 +1273,15 @@ function ensureSchema(db) {
|
|
|
1273
1273
|
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
1274
1274
|
tag TEXT NOT NULL, PRIMARY KEY (task_id, tag)
|
|
1275
1275
|
)`);
|
|
1276
|
+
ensureTable("tags", `
|
|
1277
|
+
CREATE TABLE tags (
|
|
1278
|
+
id TEXT PRIMARY KEY,
|
|
1279
|
+
name TEXT NOT NULL UNIQUE,
|
|
1280
|
+
color TEXT,
|
|
1281
|
+
description TEXT,
|
|
1282
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1283
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1284
|
+
)`);
|
|
1276
1285
|
ensureTable("task_dependencies", `
|
|
1277
1286
|
CREATE TABLE task_dependencies (
|
|
1278
1287
|
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
@@ -1486,6 +1495,36 @@ function ensureSchema(db) {
|
|
|
1486
1495
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_git_refs_task ON task_git_refs(task_id)");
|
|
1487
1496
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_git_refs_lookup ON task_git_refs(ref_type, name)");
|
|
1488
1497
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_git_refs_url ON task_git_refs(url)");
|
|
1498
|
+
ensureTable("task_commits", `
|
|
1499
|
+
CREATE TABLE task_commits (
|
|
1500
|
+
id TEXT PRIMARY KEY,
|
|
1501
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
1502
|
+
sha TEXT NOT NULL,
|
|
1503
|
+
message TEXT,
|
|
1504
|
+
author TEXT,
|
|
1505
|
+
files_changed TEXT,
|
|
1506
|
+
committed_at TEXT,
|
|
1507
|
+
branch TEXT,
|
|
1508
|
+
pr_url TEXT,
|
|
1509
|
+
pr_number INTEGER,
|
|
1510
|
+
pr_state TEXT,
|
|
1511
|
+
ci_snapshot TEXT,
|
|
1512
|
+
release_tag TEXT,
|
|
1513
|
+
repo_path TEXT,
|
|
1514
|
+
traceability TEXT DEFAULT '{}',
|
|
1515
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1516
|
+
UNIQUE(task_id, sha)
|
|
1517
|
+
)`);
|
|
1518
|
+
ensureColumn("task_commits", "branch", "TEXT");
|
|
1519
|
+
ensureColumn("task_commits", "pr_url", "TEXT");
|
|
1520
|
+
ensureColumn("task_commits", "pr_number", "INTEGER");
|
|
1521
|
+
ensureColumn("task_commits", "pr_state", "TEXT");
|
|
1522
|
+
ensureColumn("task_commits", "ci_snapshot", "TEXT");
|
|
1523
|
+
ensureColumn("task_commits", "release_tag", "TEXT");
|
|
1524
|
+
ensureColumn("task_commits", "repo_path", "TEXT");
|
|
1525
|
+
ensureColumn("task_commits", "traceability", "TEXT DEFAULT '{}'");
|
|
1526
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_commits_task ON task_commits(task_id)");
|
|
1527
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_commits_sha ON task_commits(sha)");
|
|
1489
1528
|
ensureTable("task_verifications", `
|
|
1490
1529
|
CREATE TABLE task_verifications (
|
|
1491
1530
|
id TEXT PRIMARY KEY,
|
|
@@ -1670,6 +1709,9 @@ function ensureSchema(db) {
|
|
|
1670
1709
|
ensureColumn("tasks", "max_retries", "INTEGER DEFAULT 3");
|
|
1671
1710
|
ensureColumn("tasks", "retry_after", "TEXT");
|
|
1672
1711
|
ensureColumn("tasks", "sla_minutes", "INTEGER");
|
|
1712
|
+
ensureColumn("tasks", "scheduled_start_at", "TEXT");
|
|
1713
|
+
ensureColumn("tasks", "priority_score", "INTEGER");
|
|
1714
|
+
ensureColumn("tasks", "priority_reason", "TEXT");
|
|
1673
1715
|
ensureColumn("tasks", "archived_at", "TEXT");
|
|
1674
1716
|
ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
|
|
1675
1717
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
@@ -1792,6 +1834,7 @@ function ensureSchema(db) {
|
|
|
1792
1834
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug)");
|
|
1793
1835
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_tag ON task_tags(tag)");
|
|
1794
1836
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_task ON task_tags(task_id)");
|
|
1837
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name)");
|
|
1795
1838
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_project ON plans(project_id)");
|
|
1796
1839
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
|
|
1797
1840
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_task_list ON plans(task_list_id)");
|
|
@@ -1929,6 +1972,217 @@ function ensureSchema(db) {
|
|
|
1929
1972
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date)");
|
|
1930
1973
|
ensureColumn("tasks", "cycle_id", "TEXT REFERENCES cycles(id) ON DELETE SET NULL");
|
|
1931
1974
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL");
|
|
1975
|
+
ensureTable("labels", `
|
|
1976
|
+
CREATE TABLE labels (
|
|
1977
|
+
id TEXT PRIMARY KEY,
|
|
1978
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
1979
|
+
name TEXT NOT NULL,
|
|
1980
|
+
color TEXT,
|
|
1981
|
+
description TEXT,
|
|
1982
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1983
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1984
|
+
UNIQUE(project_id, name)
|
|
1985
|
+
)`);
|
|
1986
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_labels_project ON labels(project_id)");
|
|
1987
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_labels_name ON labels(name)");
|
|
1988
|
+
ensureTable("task_labels", `
|
|
1989
|
+
CREATE TABLE task_labels (
|
|
1990
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
1991
|
+
label_id TEXT NOT NULL REFERENCES labels(id) ON DELETE CASCADE,
|
|
1992
|
+
PRIMARY KEY (task_id, label_id)
|
|
1993
|
+
)`);
|
|
1994
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_labels_task ON task_labels(task_id)");
|
|
1995
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_labels_label ON task_labels(label_id)");
|
|
1996
|
+
ensureTable("custom_field_definitions", `
|
|
1997
|
+
CREATE TABLE custom_field_definitions (
|
|
1998
|
+
id TEXT PRIMARY KEY,
|
|
1999
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
2000
|
+
name TEXT NOT NULL,
|
|
2001
|
+
slug TEXT NOT NULL,
|
|
2002
|
+
field_type TEXT NOT NULL CHECK(field_type IN ('text', 'number', 'boolean', 'date', 'enum')),
|
|
2003
|
+
options TEXT DEFAULT '[]',
|
|
2004
|
+
required INTEGER NOT NULL DEFAULT 0,
|
|
2005
|
+
default_value TEXT,
|
|
2006
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
2007
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2008
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2009
|
+
UNIQUE(project_id, slug)
|
|
2010
|
+
)`);
|
|
2011
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_custom_fields_project ON custom_field_definitions(project_id)");
|
|
2012
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_custom_fields_slug ON custom_field_definitions(slug)");
|
|
2013
|
+
ensureTable("task_custom_field_values", `
|
|
2014
|
+
CREATE TABLE task_custom_field_values (
|
|
2015
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2016
|
+
field_id TEXT NOT NULL REFERENCES custom_field_definitions(id) ON DELETE CASCADE,
|
|
2017
|
+
value TEXT,
|
|
2018
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2019
|
+
PRIMARY KEY (task_id, field_id)
|
|
2020
|
+
)`);
|
|
2021
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_custom_values_task ON task_custom_field_values(task_id)");
|
|
2022
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_custom_values_field ON task_custom_field_values(field_id)");
|
|
2023
|
+
ensureTable("saved_views", `
|
|
2024
|
+
CREATE TABLE saved_views (
|
|
2025
|
+
id TEXT PRIMARY KEY,
|
|
2026
|
+
name TEXT NOT NULL UNIQUE,
|
|
2027
|
+
slug TEXT NOT NULL UNIQUE,
|
|
2028
|
+
entity_type TEXT NOT NULL DEFAULT 'task',
|
|
2029
|
+
filters TEXT NOT NULL DEFAULT '{}',
|
|
2030
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2031
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2032
|
+
)`);
|
|
2033
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_saved_views_slug ON saved_views(slug)");
|
|
2034
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_saved_views_entity ON saved_views(entity_type)");
|
|
2035
|
+
ensureTable("decision_records", `
|
|
2036
|
+
CREATE TABLE decision_records (
|
|
2037
|
+
id TEXT PRIMARY KEY,
|
|
2038
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2039
|
+
task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
|
2040
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
2041
|
+
agent_id TEXT,
|
|
2042
|
+
sequence_num INTEGER NOT NULL,
|
|
2043
|
+
short_ref TEXT NOT NULL UNIQUE,
|
|
2044
|
+
title TEXT NOT NULL,
|
|
2045
|
+
status TEXT NOT NULL DEFAULT 'proposed' CHECK(status IN ('proposed', 'accepted', 'deprecated', 'superseded', 'rejected')),
|
|
2046
|
+
context TEXT,
|
|
2047
|
+
decision TEXT NOT NULL,
|
|
2048
|
+
consequences TEXT,
|
|
2049
|
+
alternatives TEXT DEFAULT '[]',
|
|
2050
|
+
tags TEXT DEFAULT '[]',
|
|
2051
|
+
supersedes_id TEXT REFERENCES decision_records(id) ON DELETE SET NULL,
|
|
2052
|
+
superseded_by_id TEXT REFERENCES decision_records(id) ON DELETE SET NULL,
|
|
2053
|
+
metadata TEXT DEFAULT '{}',
|
|
2054
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2055
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2056
|
+
)`);
|
|
2057
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_project ON decision_records(project_id)");
|
|
2058
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_task ON decision_records(task_id)");
|
|
2059
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_plan ON decision_records(plan_id)");
|
|
2060
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_status ON decision_records(status)");
|
|
2061
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_short_ref ON decision_records(short_ref)");
|
|
2062
|
+
ensureTable("knowledge_snapshots", `
|
|
2063
|
+
CREATE TABLE knowledge_snapshots (
|
|
2064
|
+
id TEXT PRIMARY KEY,
|
|
2065
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2066
|
+
title TEXT NOT NULL,
|
|
2067
|
+
summary TEXT,
|
|
2068
|
+
content_hash TEXT NOT NULL,
|
|
2069
|
+
snapshot TEXT NOT NULL,
|
|
2070
|
+
decision_ids TEXT DEFAULT '[]',
|
|
2071
|
+
topics TEXT DEFAULT '[]',
|
|
2072
|
+
source TEXT NOT NULL DEFAULT 'auto' CHECK(source IN ('manual', 'auto', 'import')),
|
|
2073
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2074
|
+
)`);
|
|
2075
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_knowledge_snapshots_project ON knowledge_snapshots(project_id)");
|
|
2076
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_knowledge_snapshots_hash ON knowledge_snapshots(content_hash)");
|
|
2077
|
+
ensureTable("verification_records", `
|
|
2078
|
+
CREATE TABLE verification_records (
|
|
2079
|
+
id TEXT PRIMARY KEY,
|
|
2080
|
+
task_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2081
|
+
provider_name TEXT NOT NULL,
|
|
2082
|
+
provider_type TEXT NOT NULL,
|
|
2083
|
+
status TEXT NOT NULL DEFAULT 'unknown' CHECK(status IN ('passed', 'failed', 'unknown')),
|
|
2084
|
+
summary TEXT,
|
|
2085
|
+
evidence TEXT DEFAULT '{}',
|
|
2086
|
+
artifact_id TEXT,
|
|
2087
|
+
started_at TEXT,
|
|
2088
|
+
completed_at TEXT,
|
|
2089
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2090
|
+
)`);
|
|
2091
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_verification_records_task ON verification_records(task_id)");
|
|
2092
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_verification_records_status ON verification_records(status)");
|
|
2093
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_verification_records_created ON verification_records(created_at)");
|
|
2094
|
+
ensureTable("task_leases", `
|
|
2095
|
+
CREATE TABLE task_leases (
|
|
2096
|
+
task_id TEXT PRIMARY KEY REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2097
|
+
agent_id TEXT NOT NULL,
|
|
2098
|
+
acquired_at TEXT NOT NULL,
|
|
2099
|
+
expires_at TEXT NOT NULL,
|
|
2100
|
+
heartbeat_at TEXT,
|
|
2101
|
+
steal_count INTEGER NOT NULL DEFAULT 0,
|
|
2102
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2103
|
+
)`);
|
|
2104
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_leases_agent ON task_leases(agent_id)");
|
|
2105
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_leases_expires ON task_leases(expires_at)");
|
|
2106
|
+
ensureTable("reminder_preferences", `
|
|
2107
|
+
CREATE TABLE reminder_preferences (
|
|
2108
|
+
id TEXT PRIMARY KEY,
|
|
2109
|
+
due_soon_hours INTEGER NOT NULL DEFAULT 24,
|
|
2110
|
+
sla_warning_minutes INTEGER NOT NULL DEFAULT 30,
|
|
2111
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
2112
|
+
desktop_notify INTEGER NOT NULL DEFAULT 0,
|
|
2113
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2114
|
+
)`);
|
|
2115
|
+
ensureTable("notification_reminders", `
|
|
2116
|
+
CREATE TABLE notification_reminders (
|
|
2117
|
+
id TEXT PRIMARY KEY,
|
|
2118
|
+
task_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2119
|
+
reminder_type TEXT NOT NULL CHECK(reminder_type IN ('due_soon', 'due_overdue', 'sla_warning', 'sla_breach', 'custom')),
|
|
2120
|
+
title TEXT NOT NULL,
|
|
2121
|
+
message TEXT,
|
|
2122
|
+
trigger_at TEXT NOT NULL,
|
|
2123
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'fired', 'dismissed', 'snoozed')),
|
|
2124
|
+
snoozed_until TEXT,
|
|
2125
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2126
|
+
agent_id TEXT,
|
|
2127
|
+
priority TEXT NOT NULL DEFAULT 'medium',
|
|
2128
|
+
metadata TEXT DEFAULT '{}',
|
|
2129
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2130
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2131
|
+
fired_at TEXT,
|
|
2132
|
+
dismissed_at TEXT
|
|
2133
|
+
)`);
|
|
2134
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_task ON notification_reminders(task_id)");
|
|
2135
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_status ON notification_reminders(status)");
|
|
2136
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_trigger ON notification_reminders(trigger_at)");
|
|
2137
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_project ON notification_reminders(project_id)");
|
|
2138
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_agent ON notification_reminders(agent_id)");
|
|
2139
|
+
ensureTable("run_records", `
|
|
2140
|
+
CREATE TABLE run_records (
|
|
2141
|
+
id TEXT PRIMARY KEY,
|
|
2142
|
+
agent_run_id TEXT,
|
|
2143
|
+
agent_id TEXT,
|
|
2144
|
+
objective TEXT,
|
|
2145
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
2146
|
+
claimed_task_ids TEXT DEFAULT '[]',
|
|
2147
|
+
commands TEXT DEFAULT '[]',
|
|
2148
|
+
stdout_summary TEXT,
|
|
2149
|
+
stderr_summary TEXT,
|
|
2150
|
+
files_touched TEXT DEFAULT '[]',
|
|
2151
|
+
verification_results TEXT DEFAULT '[]',
|
|
2152
|
+
artifact_ids TEXT DEFAULT '[]',
|
|
2153
|
+
status_transitions TEXT DEFAULT '[]',
|
|
2154
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'failed', 'archived')),
|
|
2155
|
+
replay_bundle TEXT,
|
|
2156
|
+
metadata TEXT DEFAULT '{}',
|
|
2157
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2158
|
+
completed_at TEXT,
|
|
2159
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2160
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2161
|
+
)`);
|
|
2162
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_agent_run ON run_records(agent_run_id)");
|
|
2163
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_agent ON run_records(agent_id)");
|
|
2164
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_plan ON run_records(plan_id)");
|
|
2165
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_status ON run_records(status)");
|
|
2166
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_started ON run_records(started_at)");
|
|
2167
|
+
ensureTable("activity_log", `
|
|
2168
|
+
CREATE TABLE activity_log (
|
|
2169
|
+
id TEXT PRIMARY KEY,
|
|
2170
|
+
entity_type TEXT NOT NULL,
|
|
2171
|
+
entity_id TEXT NOT NULL,
|
|
2172
|
+
action TEXT NOT NULL,
|
|
2173
|
+
field TEXT,
|
|
2174
|
+
old_value TEXT,
|
|
2175
|
+
new_value TEXT,
|
|
2176
|
+
actor_id TEXT,
|
|
2177
|
+
session_id TEXT,
|
|
2178
|
+
machine_id TEXT,
|
|
2179
|
+
metadata TEXT DEFAULT '{}',
|
|
2180
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2181
|
+
)`);
|
|
2182
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_activity_log_entity ON activity_log(entity_type, entity_id)");
|
|
2183
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_activity_log_actor ON activity_log(actor_id)");
|
|
2184
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_activity_log_action ON activity_log(action)");
|
|
2185
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_activity_log_created ON activity_log(created_at)");
|
|
1932
2186
|
ensureTable("api_keys", `
|
|
1933
2187
|
CREATE TABLE api_keys (
|
|
1934
2188
|
id TEXT PRIMARY KEY,
|
|
@@ -2380,11 +2634,17 @@ function ensureDir(filePath) {
|
|
|
2380
2634
|
}
|
|
2381
2635
|
}
|
|
2382
2636
|
function getDatabase(dbPath) {
|
|
2383
|
-
if (_db)
|
|
2384
|
-
return _db;
|
|
2385
2637
|
const path = dbPath || getDbPath();
|
|
2638
|
+
if (_db && _dbPath === path)
|
|
2639
|
+
return _db;
|
|
2640
|
+
if (_db && _dbPath !== path) {
|
|
2641
|
+
_db.close();
|
|
2642
|
+
_db = null;
|
|
2643
|
+
_dbPath = null;
|
|
2644
|
+
}
|
|
2386
2645
|
ensureDir(path);
|
|
2387
2646
|
_db = new Database(path);
|
|
2647
|
+
_dbPath = path;
|
|
2388
2648
|
_db.run("PRAGMA journal_mode = WAL");
|
|
2389
2649
|
_db.run("PRAGMA busy_timeout = 5000");
|
|
2390
2650
|
_db.run("PRAGMA foreign_keys = ON");
|
|
@@ -2397,10 +2657,12 @@ function closeDatabase() {
|
|
|
2397
2657
|
if (_db) {
|
|
2398
2658
|
_db.close();
|
|
2399
2659
|
_db = null;
|
|
2660
|
+
_dbPath = null;
|
|
2400
2661
|
}
|
|
2401
2662
|
}
|
|
2402
2663
|
function resetDatabase() {
|
|
2403
2664
|
_db = null;
|
|
2665
|
+
_dbPath = null;
|
|
2404
2666
|
}
|
|
2405
2667
|
function now() {
|
|
2406
2668
|
return new Date().toISOString();
|
|
@@ -2461,7 +2723,7 @@ function resolvePartialId(db, table, partialId) {
|
|
|
2461
2723
|
}
|
|
2462
2724
|
return null;
|
|
2463
2725
|
}
|
|
2464
|
-
var LOCK_EXPIRY_MINUTES = 30, _db = null, ALLOWED_TABLES;
|
|
2726
|
+
var LOCK_EXPIRY_MINUTES = 30, _db = null, _dbPath = null, ALLOWED_TABLES;
|
|
2465
2727
|
var init_database = __esm(() => {
|
|
2466
2728
|
init_schema();
|
|
2467
2729
|
init_machines();
|
|
@@ -3784,9 +4046,10 @@ function redactExportRecord(record) {
|
|
|
3784
4046
|
}
|
|
3785
4047
|
return base;
|
|
3786
4048
|
}
|
|
3787
|
-
var REDACTION_PLACEHOLDER = "[REDACTED]", DEFAULT_PATTERNS, DEFAULT_ALLOWLIST, customRedactors;
|
|
4049
|
+
var SECRET_REDACTION_SCHEMA, REDACTION_PLACEHOLDER = "[REDACTED]", DEFAULT_PATTERNS, DEFAULT_ALLOWLIST, customRedactors;
|
|
3788
4050
|
var init_secret_redaction = __esm(() => {
|
|
3789
4051
|
init_redaction();
|
|
4052
|
+
SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
|
|
3790
4053
|
DEFAULT_PATTERNS = [
|
|
3791
4054
|
{ name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
|
|
3792
4055
|
{ name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
|
|
@@ -7654,11 +7917,19 @@ function getTaskVerifications(taskId, db) {
|
|
|
7654
7917
|
}
|
|
7655
7918
|
function getTaskTraceability(taskId, db) {
|
|
7656
7919
|
const d = db || getDatabase();
|
|
7920
|
+
const commits = getTaskCommits(taskId, d);
|
|
7921
|
+
const gitRefs = getTaskGitRefs(taskId, d);
|
|
7657
7922
|
return {
|
|
7658
7923
|
task_id: taskId,
|
|
7659
|
-
commits
|
|
7660
|
-
git_refs:
|
|
7661
|
-
verifications: getTaskVerifications(taskId, d)
|
|
7924
|
+
commits,
|
|
7925
|
+
git_refs: gitRefs,
|
|
7926
|
+
verifications: getTaskVerifications(taskId, d),
|
|
7927
|
+
branches: Array.from(new Set([
|
|
7928
|
+
...commits.map((commit) => commit.branch).filter((branch) => Boolean(branch)),
|
|
7929
|
+
...gitRefs.filter((ref) => ref.ref_type === "branch").map((ref) => ref.name)
|
|
7930
|
+
])).sort(),
|
|
7931
|
+
release_tags: Array.from(new Set(commits.map((commit) => commit.release_tag).filter((tag) => Boolean(tag)))).sort(),
|
|
7932
|
+
pull_requests: commits.filter((commit) => commit.pr_url).map((commit) => ({ url: commit.pr_url, state: commit.pr_state, number: commit.pr_number }))
|
|
7662
7933
|
};
|
|
7663
7934
|
}
|
|
7664
7935
|
var init_task_commits = __esm(() => {
|
|
@@ -9184,22 +9455,27 @@ function getHeadlessBoundaryManifest() {
|
|
|
9184
9455
|
notes: [
|
|
9185
9456
|
"Use todos CLI or todos-mcp for agent workflows.",
|
|
9186
9457
|
"todos serve exposes a local-only REST API on 127.0.0.1 \u2014 not a hosted SaaS.",
|
|
9187
|
-
"
|
|
9458
|
+
"Remote sync is explicit opt-in from CLI/MCP, never from the dashboard."
|
|
9188
9459
|
]
|
|
9189
9460
|
};
|
|
9190
9461
|
}
|
|
9191
|
-
var HEADLESS_BOUNDARY_VERSION = "todos.headless-boundary.v1", FORBIDDEN_HOSTED_HOSTS, FORBIDDEN_WEB_PATTERNS, LOCAL_HOSTS;
|
|
9462
|
+
var HEADLESS_BOUNDARY_VERSION = "todos.headless-boundary.v1", FORBIDDEN_HOSTED_HOSTS, PRIVATE_PLATFORM_ORG, HOSTED_TODOS_PACKAGE, FORBIDDEN_WEB_PATTERNS, LOCAL_HOSTS;
|
|
9192
9463
|
var init_headless_boundaries = __esm(() => {
|
|
9193
9464
|
FORBIDDEN_HOSTED_HOSTS = [
|
|
9194
9465
|
"todos.md",
|
|
9195
9466
|
"www.todos.md",
|
|
9196
9467
|
"preview.todos.md",
|
|
9197
9468
|
"pay.hasna.tools",
|
|
9198
|
-
"platform
|
|
9469
|
+
["platform", "todos"].join("-")
|
|
9199
9470
|
];
|
|
9471
|
+
PRIVATE_PLATFORM_ORG = ["hasna", "studio"].join("");
|
|
9472
|
+
HOSTED_TODOS_PACKAGE = ["platform", "todos"].join("-");
|
|
9200
9473
|
FORBIDDEN_WEB_PATTERNS = [
|
|
9201
9474
|
{ name: "hosted todos.md API", pattern: /https?:\/\/(?:www\.)?todos\.md/i },
|
|
9202
|
-
{
|
|
9475
|
+
{
|
|
9476
|
+
name: "hosted platform package",
|
|
9477
|
+
pattern: new RegExp(`@${PRIVATE_PLATFORM_ORG}/${HOSTED_TODOS_PACKAGE}|${PRIVATE_PLATFORM_ORG}/${HOSTED_TODOS_PACKAGE}`, "i")
|
|
9478
|
+
},
|
|
9203
9479
|
{ name: "browser sign-in flow", pattern: /\/sign-?in\b|\/login\b.*(?:oauth|session|auth)/i },
|
|
9204
9480
|
{ name: "Stripe billing UI", pattern: /\bstripe\.(?:com|js)\b|\bcheckout\.sessions?\b/i },
|
|
9205
9481
|
{ name: "hosted OAuth redirect", pattern: /oauth.*redirect.*todos\.md/i }
|
|
@@ -31716,6 +31992,13 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
31716
31992
|
}
|
|
31717
31993
|
return current.version;
|
|
31718
31994
|
}
|
|
31995
|
+
function resolveAssignee(value) {
|
|
31996
|
+
try {
|
|
31997
|
+
return resolveId(value, "agents");
|
|
31998
|
+
} catch {
|
|
31999
|
+
return value;
|
|
32000
|
+
}
|
|
32001
|
+
}
|
|
31719
32002
|
if (shouldRegisterTool("create_task")) {
|
|
31720
32003
|
server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
|
|
31721
32004
|
title: exports_external.string().describe("Task title"),
|
|
@@ -31738,7 +32021,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
31738
32021
|
const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, ...rest } = params;
|
|
31739
32022
|
const resolved = { ...rest };
|
|
31740
32023
|
if (assigned_to)
|
|
31741
|
-
resolved.assigned_to =
|
|
32024
|
+
resolved.assigned_to = resolveAssignee(assigned_to);
|
|
31742
32025
|
if (project_id)
|
|
31743
32026
|
resolved.project_id = resolveId(project_id, "projects");
|
|
31744
32027
|
if (task_list_id)
|
|
@@ -31782,7 +32065,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
31782
32065
|
if (params.task_list_id)
|
|
31783
32066
|
resolved.task_list_id = resolveId(params.task_list_id, "task_lists");
|
|
31784
32067
|
if (params.assigned_to)
|
|
31785
|
-
resolved.assigned_to =
|
|
32068
|
+
resolved.assigned_to = resolveAssignee(params.assigned_to);
|
|
31786
32069
|
const tasks = listTasks(resolved, undefined);
|
|
31787
32070
|
if (tasks.length === 0)
|
|
31788
32071
|
return { content: [{ type: "text", text: "No tasks found." }] };
|
|
@@ -31879,7 +32162,7 @@ ${task.description}` : null
|
|
|
31879
32162
|
if (resolved.assigned_to === "")
|
|
31880
32163
|
resolved.assigned_to = null;
|
|
31881
32164
|
if (resolved.assigned_to && typeof resolved.assigned_to === "string")
|
|
31882
|
-
resolved.assigned_to =
|
|
32165
|
+
resolved.assigned_to = resolveAssignee(resolved.assigned_to);
|
|
31883
32166
|
if (resolved.project_id && typeof resolved.project_id === "string")
|
|
31884
32167
|
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
31885
32168
|
if (resolved.task_list_id && typeof resolved.task_list_id === "string")
|
|
@@ -32089,6 +32372,194 @@ var init_project_bootstrap = __esm(() => {
|
|
|
32089
32372
|
init_task_lists();
|
|
32090
32373
|
});
|
|
32091
32374
|
|
|
32375
|
+
// src/db/labels.ts
|
|
32376
|
+
function rowToLabel(row) {
|
|
32377
|
+
return { ...row };
|
|
32378
|
+
}
|
|
32379
|
+
function normalizeName(name) {
|
|
32380
|
+
return name.trim().toLowerCase();
|
|
32381
|
+
}
|
|
32382
|
+
function createLabel(input, db) {
|
|
32383
|
+
const d = db || getDatabase();
|
|
32384
|
+
const id = uuid();
|
|
32385
|
+
const ts = now();
|
|
32386
|
+
d.run(`INSERT INTO labels (id, project_id, name, color, description, created_at, updated_at)
|
|
32387
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id ?? null, input.name.trim(), input.color ?? null, input.description ?? null, ts, ts]);
|
|
32388
|
+
return rowToLabel(d.query("SELECT * FROM labels WHERE id = ?").get(id));
|
|
32389
|
+
}
|
|
32390
|
+
function getLabel(idOrName, db) {
|
|
32391
|
+
const d = db || getDatabase();
|
|
32392
|
+
let row = d.query("SELECT * FROM labels WHERE id = ?").get(idOrName);
|
|
32393
|
+
if (!row) {
|
|
32394
|
+
row = d.query("SELECT * FROM labels WHERE lower(name) = ?").get(normalizeName(idOrName));
|
|
32395
|
+
}
|
|
32396
|
+
return row ? rowToLabel(row) : null;
|
|
32397
|
+
}
|
|
32398
|
+
function listLabels(projectId, db) {
|
|
32399
|
+
const d = db || getDatabase();
|
|
32400
|
+
if (projectId) {
|
|
32401
|
+
return d.query("SELECT * FROM labels WHERE project_id IS NULL OR project_id = ? ORDER BY name").all(projectId).map(rowToLabel);
|
|
32402
|
+
}
|
|
32403
|
+
return d.query("SELECT * FROM labels ORDER BY name").all().map(rowToLabel);
|
|
32404
|
+
}
|
|
32405
|
+
function updateLabel(idOrName, input, db) {
|
|
32406
|
+
const d = db || getDatabase();
|
|
32407
|
+
const existing = getLabel(idOrName, d);
|
|
32408
|
+
if (!existing)
|
|
32409
|
+
throw new Error(`Label not found: ${idOrName}`);
|
|
32410
|
+
const ts = now();
|
|
32411
|
+
d.run(`UPDATE labels SET
|
|
32412
|
+
name = COALESCE(?, name),
|
|
32413
|
+
color = COALESCE(?, color),
|
|
32414
|
+
description = COALESCE(?, description),
|
|
32415
|
+
updated_at = ?
|
|
32416
|
+
WHERE id = ?`, [input.name?.trim() ?? null, input.color ?? null, input.description ?? null, ts, existing.id]);
|
|
32417
|
+
return getLabel(existing.id, d);
|
|
32418
|
+
}
|
|
32419
|
+
function deleteLabel(idOrName, db) {
|
|
32420
|
+
const d = db || getDatabase();
|
|
32421
|
+
const existing = getLabel(idOrName, d);
|
|
32422
|
+
if (!existing)
|
|
32423
|
+
return false;
|
|
32424
|
+
d.run("DELETE FROM task_labels WHERE label_id = ?", [existing.id]);
|
|
32425
|
+
return d.run("DELETE FROM labels WHERE id = ?", [existing.id]).changes > 0;
|
|
32426
|
+
}
|
|
32427
|
+
function assignLabelToTask(taskId, labelIdOrName, db) {
|
|
32428
|
+
const d = db || getDatabase();
|
|
32429
|
+
const label = getLabel(labelIdOrName, d);
|
|
32430
|
+
if (!label)
|
|
32431
|
+
throw new Error(`Label not found: ${labelIdOrName}`);
|
|
32432
|
+
d.run("INSERT OR IGNORE INTO task_labels (task_id, label_id) VALUES (?, ?)", [taskId, label.id]);
|
|
32433
|
+
d.run("INSERT OR IGNORE INTO task_tags (task_id, tag) VALUES (?, ?)", [taskId, label.name]);
|
|
32434
|
+
return label;
|
|
32435
|
+
}
|
|
32436
|
+
var init_labels = __esm(() => {
|
|
32437
|
+
init_database();
|
|
32438
|
+
});
|
|
32439
|
+
|
|
32440
|
+
// src/db/tags.ts
|
|
32441
|
+
function normalizeName2(name) {
|
|
32442
|
+
return name.trim();
|
|
32443
|
+
}
|
|
32444
|
+
function taskCount(name, db) {
|
|
32445
|
+
const row = db.query("SELECT COUNT(*) AS count FROM task_tags WHERE tag = ?").get(name);
|
|
32446
|
+
return row?.count ?? 0;
|
|
32447
|
+
}
|
|
32448
|
+
function rowToTag(row, db) {
|
|
32449
|
+
return {
|
|
32450
|
+
...row,
|
|
32451
|
+
task_count: taskCount(row.name, db)
|
|
32452
|
+
};
|
|
32453
|
+
}
|
|
32454
|
+
function virtualTag(name, db) {
|
|
32455
|
+
return {
|
|
32456
|
+
id: name,
|
|
32457
|
+
name,
|
|
32458
|
+
color: null,
|
|
32459
|
+
description: null,
|
|
32460
|
+
task_count: taskCount(name, db),
|
|
32461
|
+
created_at: null,
|
|
32462
|
+
updated_at: null
|
|
32463
|
+
};
|
|
32464
|
+
}
|
|
32465
|
+
function getTagRow(idOrName, db) {
|
|
32466
|
+
return db.query("SELECT * FROM tags WHERE id = ?").get(idOrName) ?? db.query("SELECT * FROM tags WHERE lower(name) = lower(?)").get(idOrName);
|
|
32467
|
+
}
|
|
32468
|
+
function updateTaskTagJson(oldName, newName, db) {
|
|
32469
|
+
const rows = db.query("SELECT id, tags FROM tasks WHERE tags IS NOT NULL AND tags != '[]'").all();
|
|
32470
|
+
const update = db.prepare("UPDATE tasks SET tags = ?, updated_at = ? WHERE id = ?");
|
|
32471
|
+
const ts = now();
|
|
32472
|
+
for (const row of rows) {
|
|
32473
|
+
if (!row.tags)
|
|
32474
|
+
continue;
|
|
32475
|
+
let tags;
|
|
32476
|
+
try {
|
|
32477
|
+
tags = JSON.parse(row.tags);
|
|
32478
|
+
} catch {
|
|
32479
|
+
continue;
|
|
32480
|
+
}
|
|
32481
|
+
if (!tags.includes(oldName))
|
|
32482
|
+
continue;
|
|
32483
|
+
const next = newName ? Array.from(new Set(tags.map((tag) => tag === oldName ? newName : tag))) : tags.filter((tag) => tag !== oldName);
|
|
32484
|
+
update.run(JSON.stringify(next), ts, row.id);
|
|
32485
|
+
}
|
|
32486
|
+
}
|
|
32487
|
+
function renameTaskTagRows(oldName, newName, db) {
|
|
32488
|
+
const rows = db.query("SELECT task_id FROM task_tags WHERE tag = ?").all(oldName);
|
|
32489
|
+
const insert = db.prepare("INSERT OR IGNORE INTO task_tags (task_id, tag) VALUES (?, ?)");
|
|
32490
|
+
for (const row of rows)
|
|
32491
|
+
insert.run(row.task_id, newName);
|
|
32492
|
+
db.run("DELETE FROM task_tags WHERE tag = ?", [oldName]);
|
|
32493
|
+
}
|
|
32494
|
+
function createTag(input, db) {
|
|
32495
|
+
const d = db || getDatabase();
|
|
32496
|
+
const name = normalizeName2(input.name);
|
|
32497
|
+
if (!name)
|
|
32498
|
+
throw new Error("Tag name is required");
|
|
32499
|
+
const id = uuid();
|
|
32500
|
+
const ts = now();
|
|
32501
|
+
d.run(`INSERT INTO tags (id, name, color, description, created_at, updated_at)
|
|
32502
|
+
VALUES (?, ?, ?, ?, ?, ?)`, [id, name, input.color ?? null, input.description ?? null, ts, ts]);
|
|
32503
|
+
return rowToTag(d.query("SELECT * FROM tags WHERE id = ?").get(id), d);
|
|
32504
|
+
}
|
|
32505
|
+
function listTags(db) {
|
|
32506
|
+
const d = db || getDatabase();
|
|
32507
|
+
const rows = d.query("SELECT * FROM tags ORDER BY name").all();
|
|
32508
|
+
const tagsByName = new Map(rows.map((row) => [row.name.toLowerCase(), rowToTag(row, d)]));
|
|
32509
|
+
const taskTags = d.query("SELECT tag, COUNT(*) AS count FROM task_tags GROUP BY tag ORDER BY tag").all();
|
|
32510
|
+
for (const row of taskTags) {
|
|
32511
|
+
const key = row.tag.toLowerCase();
|
|
32512
|
+
if (!tagsByName.has(key))
|
|
32513
|
+
tagsByName.set(key, { ...virtualTag(row.tag, d), task_count: row.count });
|
|
32514
|
+
}
|
|
32515
|
+
return [...tagsByName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
32516
|
+
}
|
|
32517
|
+
function getTag(idOrName, db) {
|
|
32518
|
+
const d = db || getDatabase();
|
|
32519
|
+
const row = getTagRow(idOrName, d);
|
|
32520
|
+
if (row)
|
|
32521
|
+
return rowToTag(row, d);
|
|
32522
|
+
const existing = d.query("SELECT tag FROM task_tags WHERE lower(tag) = lower(?) LIMIT 1").get(idOrName);
|
|
32523
|
+
return existing ? virtualTag(existing.tag, d) : null;
|
|
32524
|
+
}
|
|
32525
|
+
function updateTag(idOrName, input, db) {
|
|
32526
|
+
const d = db || getDatabase();
|
|
32527
|
+
const existing = getTag(idOrName, d);
|
|
32528
|
+
if (!existing)
|
|
32529
|
+
throw new Error(`Tag not found: ${idOrName}`);
|
|
32530
|
+
const row = getTagRow(existing.id, d);
|
|
32531
|
+
if (!row)
|
|
32532
|
+
createTag({ name: existing.name, color: existing.color ?? undefined, description: existing.description ?? undefined }, d);
|
|
32533
|
+
const stored = getTagRow(existing.id, d) ?? getTagRow(existing.name, d);
|
|
32534
|
+
if (!stored)
|
|
32535
|
+
throw new Error(`Tag not found: ${idOrName}`);
|
|
32536
|
+
const nextName = input.name !== undefined ? normalizeName2(input.name) : stored.name;
|
|
32537
|
+
if (!nextName)
|
|
32538
|
+
throw new Error("Tag name is required");
|
|
32539
|
+
const duplicate = getTagRow(nextName, d);
|
|
32540
|
+
if (duplicate && duplicate.id !== stored.id)
|
|
32541
|
+
throw new Error(`Tag already exists: ${nextName}`);
|
|
32542
|
+
if (nextName !== stored.name) {
|
|
32543
|
+
renameTaskTagRows(stored.name, nextName, d);
|
|
32544
|
+
updateTaskTagJson(stored.name, nextName, d);
|
|
32545
|
+
}
|
|
32546
|
+
d.run(`UPDATE tags SET name = ?, color = COALESCE(?, color), description = COALESCE(?, description), updated_at = ? WHERE id = ?`, [nextName, input.color ?? null, input.description ?? null, now(), stored.id]);
|
|
32547
|
+
return getTag(stored.id, d);
|
|
32548
|
+
}
|
|
32549
|
+
function deleteTag(idOrName, db) {
|
|
32550
|
+
const d = db || getDatabase();
|
|
32551
|
+
const existing = getTag(idOrName, d);
|
|
32552
|
+
if (!existing)
|
|
32553
|
+
return false;
|
|
32554
|
+
d.run("DELETE FROM tags WHERE id = ? OR lower(name) = lower(?)", [existing.id, existing.name]);
|
|
32555
|
+
d.run("DELETE FROM task_tags WHERE tag = ?", [existing.name]);
|
|
32556
|
+
updateTaskTagJson(existing.name, null, d);
|
|
32557
|
+
return true;
|
|
32558
|
+
}
|
|
32559
|
+
var init_tags = __esm(() => {
|
|
32560
|
+
init_database();
|
|
32561
|
+
});
|
|
32562
|
+
|
|
32092
32563
|
// src/lib/retention-cleanup.ts
|
|
32093
32564
|
import { existsSync as existsSync9, unlinkSync } from "fs";
|
|
32094
32565
|
function normalizeScopes(scopes) {
|
|
@@ -34323,18 +34794,18 @@ var init_local_fields = __esm(() => {
|
|
|
34323
34794
|
});
|
|
34324
34795
|
|
|
34325
34796
|
// src/lib/workflow-states.ts
|
|
34326
|
-
function
|
|
34797
|
+
function normalizeName3(value) {
|
|
34327
34798
|
return value.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
34328
34799
|
}
|
|
34329
34800
|
function isTaskStatus(value) {
|
|
34330
34801
|
return TASK_STATUSES.includes(value);
|
|
34331
34802
|
}
|
|
34332
34803
|
function normalizeState(input) {
|
|
34333
|
-
const name =
|
|
34804
|
+
const name = normalizeName3(input.name || "");
|
|
34334
34805
|
if (!name || !isTaskStatus(input.canonical_status))
|
|
34335
34806
|
return null;
|
|
34336
|
-
const aliases = [...new Set((input.aliases || []).map(
|
|
34337
|
-
const transitions = input.transitions ? [...new Set(input.transitions.map(
|
|
34807
|
+
const aliases = [...new Set((input.aliases || []).map(normalizeName3).filter((alias) => alias && alias !== name))].sort();
|
|
34808
|
+
const transitions = input.transitions ? [...new Set(input.transitions.map(normalizeName3).filter(Boolean))].sort() : null;
|
|
34338
34809
|
return {
|
|
34339
34810
|
name,
|
|
34340
34811
|
canonical_status: input.canonical_status,
|
|
@@ -34363,7 +34834,7 @@ function listWorkflowStates(projectPath) {
|
|
|
34363
34834
|
return mergeWorkflowConfig(workflowConfig);
|
|
34364
34835
|
}
|
|
34365
34836
|
function resolveWorkflowState(input, projectPath) {
|
|
34366
|
-
const normalized =
|
|
34837
|
+
const normalized = normalizeName3(input);
|
|
34367
34838
|
const states = listWorkflowStates(projectPath);
|
|
34368
34839
|
const byName = states.find((state) => state.name === normalized);
|
|
34369
34840
|
if (byName)
|
|
@@ -35353,10 +35824,10 @@ function summarizeTasks(taskIds, planIds, runIds, db) {
|
|
|
35353
35824
|
const completed = tasks.filter((task) => task.status === "completed");
|
|
35354
35825
|
const inProgress = tasks.filter((task) => task.status === "in_progress");
|
|
35355
35826
|
const pending = tasks.filter((task) => task.status === "pending");
|
|
35356
|
-
const
|
|
35357
|
-
const percent =
|
|
35827
|
+
const taskCount2 = tasks.length;
|
|
35828
|
+
const percent = taskCount2 === 0 ? 0 : Math.round(completed.length / taskCount2 * 100);
|
|
35358
35829
|
return {
|
|
35359
|
-
task_count:
|
|
35830
|
+
task_count: taskCount2,
|
|
35360
35831
|
completed_count: completed.length,
|
|
35361
35832
|
in_progress_count: inProgress.length,
|
|
35362
35833
|
pending_count: pending.length,
|
|
@@ -35364,7 +35835,7 @@ function summarizeTasks(taskIds, planIds, runIds, db) {
|
|
|
35364
35835
|
plan_count: planIds.length,
|
|
35365
35836
|
run_count: runIds.length,
|
|
35366
35837
|
percent_complete: percent,
|
|
35367
|
-
readiness:
|
|
35838
|
+
readiness: taskCount2 === 0 ? "empty" : blocked.length > 0 ? "blocked" : completed.length === taskCount2 ? "complete" : inProgress.length > 0 ? "in_progress" : "ready"
|
|
35368
35839
|
};
|
|
35369
35840
|
}
|
|
35370
35841
|
function createRoadmap(input) {
|
|
@@ -36543,7 +37014,7 @@ function normalizeScope(scope) {
|
|
|
36543
37014
|
}
|
|
36544
37015
|
return "tasks";
|
|
36545
37016
|
}
|
|
36546
|
-
function
|
|
37017
|
+
function normalizeName4(name) {
|
|
36547
37018
|
const normalized = name.trim();
|
|
36548
37019
|
if (!normalized)
|
|
36549
37020
|
throw new Error("Saved view name is required");
|
|
@@ -36793,7 +37264,7 @@ function runSavedSearch(filters = {}, scope = "tasks", db) {
|
|
|
36793
37264
|
}
|
|
36794
37265
|
function saveSearchView(input, db) {
|
|
36795
37266
|
const d = db || getDatabase();
|
|
36796
|
-
const name =
|
|
37267
|
+
const name = normalizeName4(input.name);
|
|
36797
37268
|
const timestamp3 = now();
|
|
36798
37269
|
const existing = getSearchView(name, d);
|
|
36799
37270
|
if (existing) {
|
|
@@ -37794,6 +38265,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
37794
38265
|
if (shouldRegisterTool("create_project")) {
|
|
37795
38266
|
server.tool("create_project", "Create a new project.", {
|
|
37796
38267
|
name: exports_external.string().describe("Project name"),
|
|
38268
|
+
path: exports_external.string().describe("Unique filesystem path for the project"),
|
|
37797
38269
|
description: exports_external.string().optional(),
|
|
37798
38270
|
status: exports_external.enum(["active", "completed", "on_hold", "archived"]).optional(),
|
|
37799
38271
|
short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if omitted)"),
|
|
@@ -37813,7 +38285,11 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
37813
38285
|
limit: exports_external.number().optional()
|
|
37814
38286
|
}, async ({ status, limit }) => {
|
|
37815
38287
|
try {
|
|
37816
|
-
|
|
38288
|
+
let projects = listProjects();
|
|
38289
|
+
if (status)
|
|
38290
|
+
projects = projects.filter((p) => p.status === status);
|
|
38291
|
+
if (limit)
|
|
38292
|
+
projects = projects.slice(0, limit);
|
|
37817
38293
|
if (projects.length === 0)
|
|
37818
38294
|
return { content: [{ type: "text", text: "No projects found." }] };
|
|
37819
38295
|
const lines = projects.map((p) => `[${p.status}] ${p.short_id || p.id.slice(0, 8)} ${p.name}`);
|
|
@@ -38443,12 +38919,12 @@ Tasks:` : null,
|
|
|
38443
38919
|
});
|
|
38444
38920
|
}
|
|
38445
38921
|
if (shouldRegisterTool("list_tags")) {
|
|
38446
|
-
server.tool("list_tags", "List all tags.", async () => {
|
|
38922
|
+
server.tool("list_tags", "List all distinct task tags in use, with task counts.", async () => {
|
|
38447
38923
|
try {
|
|
38448
|
-
const
|
|
38449
|
-
if (
|
|
38924
|
+
const rows = listTags();
|
|
38925
|
+
if (rows.length === 0)
|
|
38450
38926
|
return { content: [{ type: "text", text: "No tags found." }] };
|
|
38451
|
-
const lines =
|
|
38927
|
+
const lines = rows.map((r) => `${r.color ? "[" + r.color + "] " : ""}${r.name} (${r.task_count})`);
|
|
38452
38928
|
return { content: [{ type: "text", text: lines.join(`
|
|
38453
38929
|
`) }] };
|
|
38454
38930
|
} catch (e) {
|
|
@@ -39017,6 +39493,8 @@ var init_task_project_tools = __esm(() => {
|
|
|
39017
39493
|
init_comments();
|
|
39018
39494
|
init_task_runs();
|
|
39019
39495
|
init_project_bootstrap();
|
|
39496
|
+
init_labels();
|
|
39497
|
+
init_tags();
|
|
39020
39498
|
init_redaction();
|
|
39021
39499
|
init_retention_cleanup();
|
|
39022
39500
|
init_mention_resolver();
|
|
@@ -65542,7 +66020,7 @@ var init_agent_run_dispatcher = __esm(() => {
|
|
|
65542
66020
|
|
|
65543
66021
|
// src/lib/verification-providers.ts
|
|
65544
66022
|
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
65545
|
-
function
|
|
66023
|
+
function normalizeName5(name) {
|
|
65546
66024
|
const normalized = name.trim().toLowerCase();
|
|
65547
66025
|
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
|
|
65548
66026
|
throw new Error("verification provider name must use lowercase letters, numbers, dashes, or underscores");
|
|
@@ -65561,10 +66039,10 @@ function timeoutMs(value) {
|
|
|
65561
66039
|
return Math.max(1, Math.min(24 * 60 * 60000, Math.floor(value)));
|
|
65562
66040
|
}
|
|
65563
66041
|
function getProvider(name) {
|
|
65564
|
-
return loadConfig().verification_providers?.[
|
|
66042
|
+
return loadConfig().verification_providers?.[normalizeName5(name)] || null;
|
|
65565
66043
|
}
|
|
65566
66044
|
function upsertVerificationProvider(input) {
|
|
65567
|
-
const name =
|
|
66045
|
+
const name = normalizeName5(input.name);
|
|
65568
66046
|
const config2 = loadConfig();
|
|
65569
66047
|
const existing = config2.verification_providers?.[name];
|
|
65570
66048
|
const timestamp3 = new Date().toISOString();
|
|
@@ -65594,7 +66072,7 @@ function listVerificationProviders() {
|
|
|
65594
66072
|
return Object.values(loadConfig().verification_providers || {}).sort((a, b) => a.name.localeCompare(b.name));
|
|
65595
66073
|
}
|
|
65596
66074
|
function removeVerificationProvider(name) {
|
|
65597
|
-
const normalized =
|
|
66075
|
+
const normalized = normalizeName5(name);
|
|
65598
66076
|
const config2 = loadConfig();
|
|
65599
66077
|
if (!config2.verification_providers?.[normalized])
|
|
65600
66078
|
return false;
|
|
@@ -69930,7 +70408,7 @@ import { basename as basename5, join as join10, resolve as resolve13 } from "pat
|
|
|
69930
70408
|
function isObject3(value) {
|
|
69931
70409
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
69932
70410
|
}
|
|
69933
|
-
function
|
|
70411
|
+
function normalizeName6(name) {
|
|
69934
70412
|
const normalized = name.trim().toLowerCase();
|
|
69935
70413
|
if (!/^[a-z0-9][a-z0-9_.-]{0,63}$/.test(normalized)) {
|
|
69936
70414
|
throw new Error("extension name must use lowercase letters, numbers, dots, dashes, or underscores");
|
|
@@ -69948,7 +70426,7 @@ function permissionList(value) {
|
|
|
69948
70426
|
function normalizeManifest(input) {
|
|
69949
70427
|
if (!isObject3(input))
|
|
69950
70428
|
throw new Error("extension manifest must be a JSON object");
|
|
69951
|
-
const name =
|
|
70429
|
+
const name = normalizeName6(String(input["name"] || ""));
|
|
69952
70430
|
const version2 = String(input["version"] || "").trim();
|
|
69953
70431
|
if (!version2)
|
|
69954
70432
|
throw new Error("extension manifest requires version");
|
|
@@ -69956,7 +70434,7 @@ function normalizeManifest(input) {
|
|
|
69956
70434
|
todos: typeof input["compatibility"]["todos"] === "string" ? input["compatibility"]["todos"] : undefined
|
|
69957
70435
|
} : undefined;
|
|
69958
70436
|
const commands = Array.isArray(input["commands"]) ? input["commands"].filter(isObject3).map((command) => ({
|
|
69959
|
-
name:
|
|
70437
|
+
name: normalizeName6(String(command["name"] || "")),
|
|
69960
70438
|
command: typeof command["command"] === "string" ? command["command"] : undefined,
|
|
69961
70439
|
description: typeof command["description"] === "string" ? command["description"] : undefined,
|
|
69962
70440
|
permissions: permissionList(command["permissions"]),
|
|
@@ -69965,12 +70443,12 @@ function normalizeManifest(input) {
|
|
|
69965
70443
|
network: typeof command["network"] === "boolean" ? command["network"] : undefined
|
|
69966
70444
|
})) : [];
|
|
69967
70445
|
const mcpTools = Array.isArray(input["mcp_tools"]) ? input["mcp_tools"].filter(isObject3).map((tool) => ({
|
|
69968
|
-
name:
|
|
70446
|
+
name: normalizeName6(String(tool["name"] || "")),
|
|
69969
70447
|
description: typeof tool["description"] === "string" ? tool["description"] : undefined,
|
|
69970
70448
|
permissions: permissionList(tool["permissions"])
|
|
69971
70449
|
})) : [];
|
|
69972
70450
|
const templates = Array.isArray(input["templates"]) ? input["templates"].filter(isObject3).map((template) => ({
|
|
69973
|
-
name:
|
|
70451
|
+
name: normalizeName6(String(template["name"] || "")),
|
|
69974
70452
|
kind: typeof template["kind"] === "string" ? template["kind"] : undefined,
|
|
69975
70453
|
description: typeof template["description"] === "string" ? template["description"] : undefined,
|
|
69976
70454
|
path: typeof template["path"] === "string" ? template["path"] : undefined,
|
|
@@ -69979,7 +70457,7 @@ function normalizeManifest(input) {
|
|
|
69979
70457
|
permissions: permissionList(template["permissions"])
|
|
69980
70458
|
})) : [];
|
|
69981
70459
|
const renderers = Array.isArray(input["renderers"]) ? input["renderers"].filter(isObject3).map((renderer) => ({
|
|
69982
|
-
name:
|
|
70460
|
+
name: normalizeName6(String(renderer["name"] || "")),
|
|
69983
70461
|
target: typeof renderer["target"] === "string" ? renderer["target"] : "",
|
|
69984
70462
|
description: typeof renderer["description"] === "string" ? renderer["description"] : undefined,
|
|
69985
70463
|
command: typeof renderer["command"] === "string" ? renderer["command"] : undefined,
|
|
@@ -70378,10 +70856,10 @@ function listLocalExtensions() {
|
|
|
70378
70856
|
return Object.values(loadConfig().extension_registry || {}).sort((a, b) => a.name.localeCompare(b.name));
|
|
70379
70857
|
}
|
|
70380
70858
|
function getLocalExtension(name) {
|
|
70381
|
-
return loadConfig().extension_registry?.[
|
|
70859
|
+
return loadConfig().extension_registry?.[normalizeName6(name)] || null;
|
|
70382
70860
|
}
|
|
70383
70861
|
function removeLocalExtension(name) {
|
|
70384
|
-
const normalized =
|
|
70862
|
+
const normalized = normalizeName6(name);
|
|
70385
70863
|
const config2 = loadConfig();
|
|
70386
70864
|
if (!config2.extension_registry?.[normalized])
|
|
70387
70865
|
return false;
|
|
@@ -75892,8 +76370,6 @@ var init_builtin_templates = __esm(() => {
|
|
|
75892
76370
|
},
|
|
75893
76371
|
{
|
|
75894
76372
|
name: "open-source-project",
|
|
75895
|
-
version: 1,
|
|
75896
|
-
category: "project",
|
|
75897
76373
|
description: "Full open-source project bootstrap \u2014 scaffold to publish",
|
|
75898
76374
|
category: "open-source",
|
|
75899
76375
|
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
@@ -75919,7 +76395,7 @@ var init_builtin_templates = __esm(() => {
|
|
|
75919
76395
|
},
|
|
75920
76396
|
{
|
|
75921
76397
|
name: "release",
|
|
75922
|
-
version:
|
|
76398
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
75923
76399
|
category: "ops",
|
|
75924
76400
|
description: "Version release workflow \u2014 test, changelog, publish, verify",
|
|
75925
76401
|
variables: [
|
|
@@ -75936,7 +76412,7 @@ var init_builtin_templates = __esm(() => {
|
|
|
75936
76412
|
},
|
|
75937
76413
|
{
|
|
75938
76414
|
name: "docs-refresh",
|
|
75939
|
-
version:
|
|
76415
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
75940
76416
|
category: "workflow",
|
|
75941
76417
|
description: "Documentation refresh \u2014 audit, update, verify links",
|
|
75942
76418
|
variables: [{ name: "scope", required: true, description: "Docs scope (README, API, AGENTS.md)" }],
|
|
@@ -75949,7 +76425,7 @@ var init_builtin_templates = __esm(() => {
|
|
|
75949
76425
|
},
|
|
75950
76426
|
{
|
|
75951
76427
|
name: "migration",
|
|
75952
|
-
version:
|
|
76428
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
75953
76429
|
category: "ops",
|
|
75954
76430
|
description: "Schema/data migration workflow",
|
|
75955
76431
|
variables: [
|
|
@@ -75966,7 +76442,7 @@ var init_builtin_templates = __esm(() => {
|
|
|
75966
76442
|
},
|
|
75967
76443
|
{
|
|
75968
76444
|
name: "incident-response",
|
|
75969
|
-
version:
|
|
76445
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
75970
76446
|
category: "ops",
|
|
75971
76447
|
description: "Incident triage, mitigation, postmortem",
|
|
75972
76448
|
variables: [{ name: "incident", required: true, description: "Incident summary" }],
|
|
@@ -76013,8 +76489,8 @@ function registerTemplateTools(server, { shouldRegisterTool, resolveId, formatEr
|
|
|
76013
76489
|
const { createTemplate: createTemplate2, getTemplateWithTasks: getTemplateWithTasks2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
76014
76490
|
const t = createTemplate2(params);
|
|
76015
76491
|
const withTasks = getTemplateWithTasks2(t.id);
|
|
76016
|
-
const
|
|
76017
|
-
const taskInfo =
|
|
76492
|
+
const taskCount2 = withTasks?.tasks.length ?? 0;
|
|
76493
|
+
const taskInfo = taskCount2 > 0 ? ` | ${taskCount2} task(s)` : "";
|
|
76018
76494
|
return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"${taskInfo}` }] };
|
|
76019
76495
|
} catch (e) {
|
|
76020
76496
|
return { content: [{ type: "text", text: formatError2(e) }], isError: true };
|
|
@@ -76718,6 +77194,26 @@ function getMcpVersion() {
|
|
|
76718
77194
|
function hasVersionFlag() {
|
|
76719
77195
|
return process.argv.includes("--version") || process.argv.includes("-V");
|
|
76720
77196
|
}
|
|
77197
|
+
function hasHelpFlag() {
|
|
77198
|
+
return process.argv.includes("--help") || process.argv.includes("-h");
|
|
77199
|
+
}
|
|
77200
|
+
function printHelp() {
|
|
77201
|
+
console.log(`Usage: todos-mcp [options]
|
|
77202
|
+
|
|
77203
|
+
Start the @hasna/todos MCP server.
|
|
77204
|
+
|
|
77205
|
+
Options:
|
|
77206
|
+
--stdio Use stdio transport
|
|
77207
|
+
--port <port> Use Streamable HTTP on the given port
|
|
77208
|
+
-V, --version output the version number
|
|
77209
|
+
-h, --help display help for command
|
|
77210
|
+
|
|
77211
|
+
Environment:
|
|
77212
|
+
TODOS_MCP_STDIO=true Force stdio transport
|
|
77213
|
+
TODOS_MCP_PORT=<port> HTTP port when not using stdio
|
|
77214
|
+
TODOS_PROFILE=<profile> Tool profile filter
|
|
77215
|
+
TODOS_TOOL_GROUPS=<list> Comma-separated tool group filter`);
|
|
77216
|
+
}
|
|
76721
77217
|
function shouldRegisterTool(name) {
|
|
76722
77218
|
return shouldRegisterToolForProfile(name);
|
|
76723
77219
|
}
|
|
@@ -76858,8 +77354,17 @@ function buildServer() {
|
|
|
76858
77354
|
version: getMcpVersion()
|
|
76859
77355
|
});
|
|
76860
77356
|
installMcpTokenDiagnostics(server);
|
|
77357
|
+
const registeredToolNames = new Set;
|
|
77358
|
+
const shouldRegisterToolOnce = (name) => {
|
|
77359
|
+
if (!shouldRegisterTool(name))
|
|
77360
|
+
return false;
|
|
77361
|
+
if (registeredToolNames.has(name))
|
|
77362
|
+
return false;
|
|
77363
|
+
registeredToolNames.add(name);
|
|
77364
|
+
return true;
|
|
77365
|
+
};
|
|
76861
77366
|
const toolContext = {
|
|
76862
|
-
shouldRegisterTool,
|
|
77367
|
+
shouldRegisterTool: shouldRegisterToolOnce,
|
|
76863
77368
|
resolveId,
|
|
76864
77369
|
formatError: formatError2,
|
|
76865
77370
|
formatTask,
|
|
@@ -76879,8 +77384,8 @@ function buildServer() {
|
|
|
76879
77384
|
registerTemplateTools(server, toolContext);
|
|
76880
77385
|
registerEnvironmentSnapshotTools(server, toolContext);
|
|
76881
77386
|
registerWorkflowPrompts(server);
|
|
76882
|
-
registerMachineTools(server, { shouldRegisterTool, formatError: formatError2 });
|
|
76883
|
-
registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError: formatError2 });
|
|
77387
|
+
registerMachineTools(server, { shouldRegisterTool: shouldRegisterToolOnce, formatError: formatError2 });
|
|
77388
|
+
registerDispatchTools(server, { shouldRegisterTool: shouldRegisterToolOnce, resolveId, formatError: formatError2 });
|
|
76884
77389
|
return server;
|
|
76885
77390
|
}
|
|
76886
77391
|
async function main() {
|
|
@@ -76924,6 +77429,10 @@ var init_mcp3 = __esm(() => {
|
|
|
76924
77429
|
console.log(getMcpVersion());
|
|
76925
77430
|
process.exit(0);
|
|
76926
77431
|
}
|
|
77432
|
+
if (hasHelpFlag()) {
|
|
77433
|
+
printHelp();
|
|
77434
|
+
process.exit(0);
|
|
77435
|
+
}
|
|
76927
77436
|
agentFocusMap = new Map;
|
|
76928
77437
|
isDirectRun = process.argv[1]?.endsWith("/mcp/index.ts") || process.argv[1]?.endsWith("/mcp/index.js");
|
|
76929
77438
|
if (isDirectRun) {
|
|
@@ -77216,8 +77725,6 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
77216
77725
|
return handleMcpHttpRequest2(req, buildServer2);
|
|
77217
77726
|
}
|
|
77218
77727
|
if (method === "OPTIONS") {
|
|
77219
|
-
const reqOrigin2 = req.headers.get("origin") || undefined;
|
|
77220
|
-
const allowed = reqOrigin2 && (reqOrigin2 === `http://localhost:${port}` || reqOrigin2 === "http://localhost:0");
|
|
77221
77728
|
return new Response(null, {
|
|
77222
77729
|
headers: corsHeaders || {
|
|
77223
77730
|
Vary: "Origin"
|
|
@@ -77486,11 +77993,30 @@ var init_serve = __esm(() => {
|
|
|
77486
77993
|
|
|
77487
77994
|
// src/server/index.ts
|
|
77488
77995
|
init_package_version();
|
|
77489
|
-
init_serve();
|
|
77490
77996
|
var DEFAULT_PORT = 19427;
|
|
77491
77997
|
function hasVersionFlag2() {
|
|
77492
77998
|
return process.argv.includes("--version") || process.argv.includes("-V");
|
|
77493
77999
|
}
|
|
78000
|
+
function hasHelpFlag2() {
|
|
78001
|
+
return process.argv.includes("--help") || process.argv.includes("-h");
|
|
78002
|
+
}
|
|
78003
|
+
function printHelp2() {
|
|
78004
|
+
console.log(`Usage: todos-serve [options]
|
|
78005
|
+
|
|
78006
|
+
Start the @hasna/todos dashboard server.
|
|
78007
|
+
|
|
78008
|
+
Options:
|
|
78009
|
+
--port <port> HTTP port to bind. Defaults to ${DEFAULT_PORT}
|
|
78010
|
+
--host <host> Hostname to bind. Defaults to 127.0.0.1
|
|
78011
|
+
--api-key <key> Require this API key for dashboard/API requests
|
|
78012
|
+
--no-open Do not open the dashboard in a browser
|
|
78013
|
+
-V, --version output the version number
|
|
78014
|
+
-h, --help display help for command
|
|
78015
|
+
|
|
78016
|
+
Environment:
|
|
78017
|
+
TODOS_NO_OPEN=true Do not open the dashboard in a browser
|
|
78018
|
+
TODOS_API_KEY=<key> Require this API key for dashboard/API requests`);
|
|
78019
|
+
}
|
|
77494
78020
|
function parsePort() {
|
|
77495
78021
|
const portArg = process.argv.find((a) => a === "--port" || a.startsWith("--port="));
|
|
77496
78022
|
if (portArg) {
|
|
@@ -77526,13 +78052,18 @@ async function main2() {
|
|
|
77526
78052
|
console.log(getPackageVersion());
|
|
77527
78053
|
return;
|
|
77528
78054
|
}
|
|
78055
|
+
if (hasHelpFlag2()) {
|
|
78056
|
+
printHelp2();
|
|
78057
|
+
return;
|
|
78058
|
+
}
|
|
77529
78059
|
const requestedPort = parsePort();
|
|
77530
78060
|
const port = await findFreePort(requestedPort);
|
|
77531
78061
|
if (port !== requestedPort) {
|
|
77532
78062
|
console.log(`Port ${requestedPort} in use, using ${port}`);
|
|
77533
78063
|
}
|
|
77534
78064
|
const noOpen = process.argv.includes("--no-open") || process.env["TODOS_NO_OPEN"] === "true";
|
|
77535
|
-
startServer(
|
|
78065
|
+
const { startServer: startServer2 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
|
|
78066
|
+
startServer2(port, {
|
|
77536
78067
|
open: !noOpen,
|
|
77537
78068
|
host: parseStringArg("--host"),
|
|
77538
78069
|
apiKey: parseStringArg("--api-key")
|