@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/mcp/index.js
CHANGED
|
@@ -1224,6 +1224,15 @@ function ensureSchema(db) {
|
|
|
1224
1224
|
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
1225
1225
|
tag TEXT NOT NULL, PRIMARY KEY (task_id, tag)
|
|
1226
1226
|
)`);
|
|
1227
|
+
ensureTable("tags", `
|
|
1228
|
+
CREATE TABLE tags (
|
|
1229
|
+
id TEXT PRIMARY KEY,
|
|
1230
|
+
name TEXT NOT NULL UNIQUE,
|
|
1231
|
+
color TEXT,
|
|
1232
|
+
description TEXT,
|
|
1233
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1234
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1235
|
+
)`);
|
|
1227
1236
|
ensureTable("task_dependencies", `
|
|
1228
1237
|
CREATE TABLE task_dependencies (
|
|
1229
1238
|
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
@@ -1437,6 +1446,36 @@ function ensureSchema(db) {
|
|
|
1437
1446
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_git_refs_task ON task_git_refs(task_id)");
|
|
1438
1447
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_git_refs_lookup ON task_git_refs(ref_type, name)");
|
|
1439
1448
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_git_refs_url ON task_git_refs(url)");
|
|
1449
|
+
ensureTable("task_commits", `
|
|
1450
|
+
CREATE TABLE task_commits (
|
|
1451
|
+
id TEXT PRIMARY KEY,
|
|
1452
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
1453
|
+
sha TEXT NOT NULL,
|
|
1454
|
+
message TEXT,
|
|
1455
|
+
author TEXT,
|
|
1456
|
+
files_changed TEXT,
|
|
1457
|
+
committed_at TEXT,
|
|
1458
|
+
branch TEXT,
|
|
1459
|
+
pr_url TEXT,
|
|
1460
|
+
pr_number INTEGER,
|
|
1461
|
+
pr_state TEXT,
|
|
1462
|
+
ci_snapshot TEXT,
|
|
1463
|
+
release_tag TEXT,
|
|
1464
|
+
repo_path TEXT,
|
|
1465
|
+
traceability TEXT DEFAULT '{}',
|
|
1466
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1467
|
+
UNIQUE(task_id, sha)
|
|
1468
|
+
)`);
|
|
1469
|
+
ensureColumn("task_commits", "branch", "TEXT");
|
|
1470
|
+
ensureColumn("task_commits", "pr_url", "TEXT");
|
|
1471
|
+
ensureColumn("task_commits", "pr_number", "INTEGER");
|
|
1472
|
+
ensureColumn("task_commits", "pr_state", "TEXT");
|
|
1473
|
+
ensureColumn("task_commits", "ci_snapshot", "TEXT");
|
|
1474
|
+
ensureColumn("task_commits", "release_tag", "TEXT");
|
|
1475
|
+
ensureColumn("task_commits", "repo_path", "TEXT");
|
|
1476
|
+
ensureColumn("task_commits", "traceability", "TEXT DEFAULT '{}'");
|
|
1477
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_commits_task ON task_commits(task_id)");
|
|
1478
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_commits_sha ON task_commits(sha)");
|
|
1440
1479
|
ensureTable("task_verifications", `
|
|
1441
1480
|
CREATE TABLE task_verifications (
|
|
1442
1481
|
id TEXT PRIMARY KEY,
|
|
@@ -1621,6 +1660,9 @@ function ensureSchema(db) {
|
|
|
1621
1660
|
ensureColumn("tasks", "max_retries", "INTEGER DEFAULT 3");
|
|
1622
1661
|
ensureColumn("tasks", "retry_after", "TEXT");
|
|
1623
1662
|
ensureColumn("tasks", "sla_minutes", "INTEGER");
|
|
1663
|
+
ensureColumn("tasks", "scheduled_start_at", "TEXT");
|
|
1664
|
+
ensureColumn("tasks", "priority_score", "INTEGER");
|
|
1665
|
+
ensureColumn("tasks", "priority_reason", "TEXT");
|
|
1624
1666
|
ensureColumn("tasks", "archived_at", "TEXT");
|
|
1625
1667
|
ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
|
|
1626
1668
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
@@ -1743,6 +1785,7 @@ function ensureSchema(db) {
|
|
|
1743
1785
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug)");
|
|
1744
1786
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_tag ON task_tags(tag)");
|
|
1745
1787
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_task ON task_tags(task_id)");
|
|
1788
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name)");
|
|
1746
1789
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_project ON plans(project_id)");
|
|
1747
1790
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
|
|
1748
1791
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_task_list ON plans(task_list_id)");
|
|
@@ -1880,6 +1923,217 @@ function ensureSchema(db) {
|
|
|
1880
1923
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_cycles_dates ON cycles(start_date, end_date)");
|
|
1881
1924
|
ensureColumn("tasks", "cycle_id", "TEXT REFERENCES cycles(id) ON DELETE SET NULL");
|
|
1882
1925
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_cycle ON tasks(cycle_id) WHERE cycle_id IS NOT NULL");
|
|
1926
|
+
ensureTable("labels", `
|
|
1927
|
+
CREATE TABLE labels (
|
|
1928
|
+
id TEXT PRIMARY KEY,
|
|
1929
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
1930
|
+
name TEXT NOT NULL,
|
|
1931
|
+
color TEXT,
|
|
1932
|
+
description TEXT,
|
|
1933
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1934
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1935
|
+
UNIQUE(project_id, name)
|
|
1936
|
+
)`);
|
|
1937
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_labels_project ON labels(project_id)");
|
|
1938
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_labels_name ON labels(name)");
|
|
1939
|
+
ensureTable("task_labels", `
|
|
1940
|
+
CREATE TABLE task_labels (
|
|
1941
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
1942
|
+
label_id TEXT NOT NULL REFERENCES labels(id) ON DELETE CASCADE,
|
|
1943
|
+
PRIMARY KEY (task_id, label_id)
|
|
1944
|
+
)`);
|
|
1945
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_labels_task ON task_labels(task_id)");
|
|
1946
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_labels_label ON task_labels(label_id)");
|
|
1947
|
+
ensureTable("custom_field_definitions", `
|
|
1948
|
+
CREATE TABLE custom_field_definitions (
|
|
1949
|
+
id TEXT PRIMARY KEY,
|
|
1950
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
1951
|
+
name TEXT NOT NULL,
|
|
1952
|
+
slug TEXT NOT NULL,
|
|
1953
|
+
field_type TEXT NOT NULL CHECK(field_type IN ('text', 'number', 'boolean', 'date', 'enum')),
|
|
1954
|
+
options TEXT DEFAULT '[]',
|
|
1955
|
+
required INTEGER NOT NULL DEFAULT 0,
|
|
1956
|
+
default_value TEXT,
|
|
1957
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
1958
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1959
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1960
|
+
UNIQUE(project_id, slug)
|
|
1961
|
+
)`);
|
|
1962
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_custom_fields_project ON custom_field_definitions(project_id)");
|
|
1963
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_custom_fields_slug ON custom_field_definitions(slug)");
|
|
1964
|
+
ensureTable("task_custom_field_values", `
|
|
1965
|
+
CREATE TABLE task_custom_field_values (
|
|
1966
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
1967
|
+
field_id TEXT NOT NULL REFERENCES custom_field_definitions(id) ON DELETE CASCADE,
|
|
1968
|
+
value TEXT,
|
|
1969
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1970
|
+
PRIMARY KEY (task_id, field_id)
|
|
1971
|
+
)`);
|
|
1972
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_custom_values_task ON task_custom_field_values(task_id)");
|
|
1973
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_custom_values_field ON task_custom_field_values(field_id)");
|
|
1974
|
+
ensureTable("saved_views", `
|
|
1975
|
+
CREATE TABLE saved_views (
|
|
1976
|
+
id TEXT PRIMARY KEY,
|
|
1977
|
+
name TEXT NOT NULL UNIQUE,
|
|
1978
|
+
slug TEXT NOT NULL UNIQUE,
|
|
1979
|
+
entity_type TEXT NOT NULL DEFAULT 'task',
|
|
1980
|
+
filters TEXT NOT NULL DEFAULT '{}',
|
|
1981
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1982
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1983
|
+
)`);
|
|
1984
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_saved_views_slug ON saved_views(slug)");
|
|
1985
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_saved_views_entity ON saved_views(entity_type)");
|
|
1986
|
+
ensureTable("decision_records", `
|
|
1987
|
+
CREATE TABLE decision_records (
|
|
1988
|
+
id TEXT PRIMARY KEY,
|
|
1989
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
1990
|
+
task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
|
1991
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
1992
|
+
agent_id TEXT,
|
|
1993
|
+
sequence_num INTEGER NOT NULL,
|
|
1994
|
+
short_ref TEXT NOT NULL UNIQUE,
|
|
1995
|
+
title TEXT NOT NULL,
|
|
1996
|
+
status TEXT NOT NULL DEFAULT 'proposed' CHECK(status IN ('proposed', 'accepted', 'deprecated', 'superseded', 'rejected')),
|
|
1997
|
+
context TEXT,
|
|
1998
|
+
decision TEXT NOT NULL,
|
|
1999
|
+
consequences TEXT,
|
|
2000
|
+
alternatives TEXT DEFAULT '[]',
|
|
2001
|
+
tags TEXT DEFAULT '[]',
|
|
2002
|
+
supersedes_id TEXT REFERENCES decision_records(id) ON DELETE SET NULL,
|
|
2003
|
+
superseded_by_id TEXT REFERENCES decision_records(id) ON DELETE SET NULL,
|
|
2004
|
+
metadata TEXT DEFAULT '{}',
|
|
2005
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2006
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2007
|
+
)`);
|
|
2008
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_project ON decision_records(project_id)");
|
|
2009
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_task ON decision_records(task_id)");
|
|
2010
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_plan ON decision_records(plan_id)");
|
|
2011
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_status ON decision_records(status)");
|
|
2012
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_decision_records_short_ref ON decision_records(short_ref)");
|
|
2013
|
+
ensureTable("knowledge_snapshots", `
|
|
2014
|
+
CREATE TABLE knowledge_snapshots (
|
|
2015
|
+
id TEXT PRIMARY KEY,
|
|
2016
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2017
|
+
title TEXT NOT NULL,
|
|
2018
|
+
summary TEXT,
|
|
2019
|
+
content_hash TEXT NOT NULL,
|
|
2020
|
+
snapshot TEXT NOT NULL,
|
|
2021
|
+
decision_ids TEXT DEFAULT '[]',
|
|
2022
|
+
topics TEXT DEFAULT '[]',
|
|
2023
|
+
source TEXT NOT NULL DEFAULT 'auto' CHECK(source IN ('manual', 'auto', 'import')),
|
|
2024
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2025
|
+
)`);
|
|
2026
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_knowledge_snapshots_project ON knowledge_snapshots(project_id)");
|
|
2027
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_knowledge_snapshots_hash ON knowledge_snapshots(content_hash)");
|
|
2028
|
+
ensureTable("verification_records", `
|
|
2029
|
+
CREATE TABLE verification_records (
|
|
2030
|
+
id TEXT PRIMARY KEY,
|
|
2031
|
+
task_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2032
|
+
provider_name TEXT NOT NULL,
|
|
2033
|
+
provider_type TEXT NOT NULL,
|
|
2034
|
+
status TEXT NOT NULL DEFAULT 'unknown' CHECK(status IN ('passed', 'failed', 'unknown')),
|
|
2035
|
+
summary TEXT,
|
|
2036
|
+
evidence TEXT DEFAULT '{}',
|
|
2037
|
+
artifact_id TEXT,
|
|
2038
|
+
started_at TEXT,
|
|
2039
|
+
completed_at TEXT,
|
|
2040
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2041
|
+
)`);
|
|
2042
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_verification_records_task ON verification_records(task_id)");
|
|
2043
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_verification_records_status ON verification_records(status)");
|
|
2044
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_verification_records_created ON verification_records(created_at)");
|
|
2045
|
+
ensureTable("task_leases", `
|
|
2046
|
+
CREATE TABLE task_leases (
|
|
2047
|
+
task_id TEXT PRIMARY KEY REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2048
|
+
agent_id TEXT NOT NULL,
|
|
2049
|
+
acquired_at TEXT NOT NULL,
|
|
2050
|
+
expires_at TEXT NOT NULL,
|
|
2051
|
+
heartbeat_at TEXT,
|
|
2052
|
+
steal_count INTEGER NOT NULL DEFAULT 0,
|
|
2053
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2054
|
+
)`);
|
|
2055
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_leases_agent ON task_leases(agent_id)");
|
|
2056
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_leases_expires ON task_leases(expires_at)");
|
|
2057
|
+
ensureTable("reminder_preferences", `
|
|
2058
|
+
CREATE TABLE reminder_preferences (
|
|
2059
|
+
id TEXT PRIMARY KEY,
|
|
2060
|
+
due_soon_hours INTEGER NOT NULL DEFAULT 24,
|
|
2061
|
+
sla_warning_minutes INTEGER NOT NULL DEFAULT 30,
|
|
2062
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
2063
|
+
desktop_notify INTEGER NOT NULL DEFAULT 0,
|
|
2064
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2065
|
+
)`);
|
|
2066
|
+
ensureTable("notification_reminders", `
|
|
2067
|
+
CREATE TABLE notification_reminders (
|
|
2068
|
+
id TEXT PRIMARY KEY,
|
|
2069
|
+
task_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2070
|
+
reminder_type TEXT NOT NULL CHECK(reminder_type IN ('due_soon', 'due_overdue', 'sla_warning', 'sla_breach', 'custom')),
|
|
2071
|
+
title TEXT NOT NULL,
|
|
2072
|
+
message TEXT,
|
|
2073
|
+
trigger_at TEXT NOT NULL,
|
|
2074
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'fired', 'dismissed', 'snoozed')),
|
|
2075
|
+
snoozed_until TEXT,
|
|
2076
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2077
|
+
agent_id TEXT,
|
|
2078
|
+
priority TEXT NOT NULL DEFAULT 'medium',
|
|
2079
|
+
metadata TEXT DEFAULT '{}',
|
|
2080
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2081
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2082
|
+
fired_at TEXT,
|
|
2083
|
+
dismissed_at TEXT
|
|
2084
|
+
)`);
|
|
2085
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_task ON notification_reminders(task_id)");
|
|
2086
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_status ON notification_reminders(status)");
|
|
2087
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_trigger ON notification_reminders(trigger_at)");
|
|
2088
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_project ON notification_reminders(project_id)");
|
|
2089
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_notification_reminders_agent ON notification_reminders(agent_id)");
|
|
2090
|
+
ensureTable("run_records", `
|
|
2091
|
+
CREATE TABLE run_records (
|
|
2092
|
+
id TEXT PRIMARY KEY,
|
|
2093
|
+
agent_run_id TEXT,
|
|
2094
|
+
agent_id TEXT,
|
|
2095
|
+
objective TEXT,
|
|
2096
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
2097
|
+
claimed_task_ids TEXT DEFAULT '[]',
|
|
2098
|
+
commands TEXT DEFAULT '[]',
|
|
2099
|
+
stdout_summary TEXT,
|
|
2100
|
+
stderr_summary TEXT,
|
|
2101
|
+
files_touched TEXT DEFAULT '[]',
|
|
2102
|
+
verification_results TEXT DEFAULT '[]',
|
|
2103
|
+
artifact_ids TEXT DEFAULT '[]',
|
|
2104
|
+
status_transitions TEXT DEFAULT '[]',
|
|
2105
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'failed', 'archived')),
|
|
2106
|
+
replay_bundle TEXT,
|
|
2107
|
+
metadata TEXT DEFAULT '{}',
|
|
2108
|
+
started_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2109
|
+
completed_at TEXT,
|
|
2110
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2111
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2112
|
+
)`);
|
|
2113
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_agent_run ON run_records(agent_run_id)");
|
|
2114
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_agent ON run_records(agent_id)");
|
|
2115
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_plan ON run_records(plan_id)");
|
|
2116
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_status ON run_records(status)");
|
|
2117
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_run_records_started ON run_records(started_at)");
|
|
2118
|
+
ensureTable("activity_log", `
|
|
2119
|
+
CREATE TABLE activity_log (
|
|
2120
|
+
id TEXT PRIMARY KEY,
|
|
2121
|
+
entity_type TEXT NOT NULL,
|
|
2122
|
+
entity_id TEXT NOT NULL,
|
|
2123
|
+
action TEXT NOT NULL,
|
|
2124
|
+
field TEXT,
|
|
2125
|
+
old_value TEXT,
|
|
2126
|
+
new_value TEXT,
|
|
2127
|
+
actor_id TEXT,
|
|
2128
|
+
session_id TEXT,
|
|
2129
|
+
machine_id TEXT,
|
|
2130
|
+
metadata TEXT DEFAULT '{}',
|
|
2131
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2132
|
+
)`);
|
|
2133
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_activity_log_entity ON activity_log(entity_type, entity_id)");
|
|
2134
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_activity_log_actor ON activity_log(actor_id)");
|
|
2135
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_activity_log_action ON activity_log(action)");
|
|
2136
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_activity_log_created ON activity_log(created_at)");
|
|
1883
2137
|
ensureTable("api_keys", `
|
|
1884
2138
|
CREATE TABLE api_keys (
|
|
1885
2139
|
id TEXT PRIMARY KEY,
|
|
@@ -2331,11 +2585,17 @@ function ensureDir(filePath) {
|
|
|
2331
2585
|
}
|
|
2332
2586
|
}
|
|
2333
2587
|
function getDatabase(dbPath) {
|
|
2334
|
-
if (_db)
|
|
2335
|
-
return _db;
|
|
2336
2588
|
const path = dbPath || getDbPath();
|
|
2589
|
+
if (_db && _dbPath === path)
|
|
2590
|
+
return _db;
|
|
2591
|
+
if (_db && _dbPath !== path) {
|
|
2592
|
+
_db.close();
|
|
2593
|
+
_db = null;
|
|
2594
|
+
_dbPath = null;
|
|
2595
|
+
}
|
|
2337
2596
|
ensureDir(path);
|
|
2338
2597
|
_db = new Database(path);
|
|
2598
|
+
_dbPath = path;
|
|
2339
2599
|
_db.run("PRAGMA journal_mode = WAL");
|
|
2340
2600
|
_db.run("PRAGMA busy_timeout = 5000");
|
|
2341
2601
|
_db.run("PRAGMA foreign_keys = ON");
|
|
@@ -2348,10 +2608,12 @@ function closeDatabase() {
|
|
|
2348
2608
|
if (_db) {
|
|
2349
2609
|
_db.close();
|
|
2350
2610
|
_db = null;
|
|
2611
|
+
_dbPath = null;
|
|
2351
2612
|
}
|
|
2352
2613
|
}
|
|
2353
2614
|
function resetDatabase() {
|
|
2354
2615
|
_db = null;
|
|
2616
|
+
_dbPath = null;
|
|
2355
2617
|
}
|
|
2356
2618
|
function now() {
|
|
2357
2619
|
return new Date().toISOString();
|
|
@@ -2412,7 +2674,7 @@ function resolvePartialId(db, table, partialId) {
|
|
|
2412
2674
|
}
|
|
2413
2675
|
return null;
|
|
2414
2676
|
}
|
|
2415
|
-
var LOCK_EXPIRY_MINUTES = 30, _db = null, ALLOWED_TABLES;
|
|
2677
|
+
var LOCK_EXPIRY_MINUTES = 30, _db = null, _dbPath = null, ALLOWED_TABLES;
|
|
2416
2678
|
var init_database = __esm(() => {
|
|
2417
2679
|
init_schema();
|
|
2418
2680
|
init_machines();
|
|
@@ -8423,9 +8685,10 @@ function redactExportRecord(record) {
|
|
|
8423
8685
|
}
|
|
8424
8686
|
return base;
|
|
8425
8687
|
}
|
|
8426
|
-
var REDACTION_PLACEHOLDER = "[REDACTED]", DEFAULT_PATTERNS, DEFAULT_ALLOWLIST, customRedactors;
|
|
8688
|
+
var SECRET_REDACTION_SCHEMA, REDACTION_PLACEHOLDER = "[REDACTED]", DEFAULT_PATTERNS, DEFAULT_ALLOWLIST, customRedactors;
|
|
8427
8689
|
var init_secret_redaction = __esm(() => {
|
|
8428
8690
|
init_redaction();
|
|
8691
|
+
SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
|
|
8429
8692
|
DEFAULT_PATTERNS = [
|
|
8430
8693
|
{ name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
|
|
8431
8694
|
{ name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
|
|
@@ -12293,11 +12556,19 @@ function getTaskVerifications(taskId, db) {
|
|
|
12293
12556
|
}
|
|
12294
12557
|
function getTaskTraceability(taskId, db) {
|
|
12295
12558
|
const d = db || getDatabase();
|
|
12559
|
+
const commits = getTaskCommits(taskId, d);
|
|
12560
|
+
const gitRefs = getTaskGitRefs(taskId, d);
|
|
12296
12561
|
return {
|
|
12297
12562
|
task_id: taskId,
|
|
12298
|
-
commits
|
|
12299
|
-
git_refs:
|
|
12300
|
-
verifications: getTaskVerifications(taskId, d)
|
|
12563
|
+
commits,
|
|
12564
|
+
git_refs: gitRefs,
|
|
12565
|
+
verifications: getTaskVerifications(taskId, d),
|
|
12566
|
+
branches: Array.from(new Set([
|
|
12567
|
+
...commits.map((commit) => commit.branch).filter((branch) => Boolean(branch)),
|
|
12568
|
+
...gitRefs.filter((ref) => ref.ref_type === "branch").map((ref) => ref.name)
|
|
12569
|
+
])).sort(),
|
|
12570
|
+
release_tags: Array.from(new Set(commits.map((commit) => commit.release_tag).filter((tag) => Boolean(tag)))).sort(),
|
|
12571
|
+
pull_requests: commits.filter((commit) => commit.pr_url).map((commit) => ({ url: commit.pr_url, state: commit.pr_state, number: commit.pr_number }))
|
|
12301
12572
|
};
|
|
12302
12573
|
}
|
|
12303
12574
|
var init_task_commits = __esm(() => {
|
|
@@ -14069,6 +14340,13 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
14069
14340
|
}
|
|
14070
14341
|
return current.version;
|
|
14071
14342
|
}
|
|
14343
|
+
function resolveAssignee(value) {
|
|
14344
|
+
try {
|
|
14345
|
+
return resolveId(value, "agents");
|
|
14346
|
+
} catch {
|
|
14347
|
+
return value;
|
|
14348
|
+
}
|
|
14349
|
+
}
|
|
14072
14350
|
if (shouldRegisterTool("create_task")) {
|
|
14073
14351
|
server.tool("create_task", "Create a new task in a project. Pass short_id=null to auto-generate.", {
|
|
14074
14352
|
title: exports_external.string().describe("Task title"),
|
|
@@ -14091,7 +14369,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
14091
14369
|
const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, ...rest } = params;
|
|
14092
14370
|
const resolved = { ...rest };
|
|
14093
14371
|
if (assigned_to)
|
|
14094
|
-
resolved.assigned_to =
|
|
14372
|
+
resolved.assigned_to = resolveAssignee(assigned_to);
|
|
14095
14373
|
if (project_id)
|
|
14096
14374
|
resolved.project_id = resolveId(project_id, "projects");
|
|
14097
14375
|
if (task_list_id)
|
|
@@ -14135,7 +14413,7 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
14135
14413
|
if (params.task_list_id)
|
|
14136
14414
|
resolved.task_list_id = resolveId(params.task_list_id, "task_lists");
|
|
14137
14415
|
if (params.assigned_to)
|
|
14138
|
-
resolved.assigned_to =
|
|
14416
|
+
resolved.assigned_to = resolveAssignee(params.assigned_to);
|
|
14139
14417
|
const tasks = listTasks(resolved, undefined);
|
|
14140
14418
|
if (tasks.length === 0)
|
|
14141
14419
|
return { content: [{ type: "text", text: "No tasks found." }] };
|
|
@@ -14232,7 +14510,7 @@ ${task.description}` : null
|
|
|
14232
14510
|
if (resolved.assigned_to === "")
|
|
14233
14511
|
resolved.assigned_to = null;
|
|
14234
14512
|
if (resolved.assigned_to && typeof resolved.assigned_to === "string")
|
|
14235
|
-
resolved.assigned_to =
|
|
14513
|
+
resolved.assigned_to = resolveAssignee(resolved.assigned_to);
|
|
14236
14514
|
if (resolved.project_id && typeof resolved.project_id === "string")
|
|
14237
14515
|
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
14238
14516
|
if (resolved.task_list_id && typeof resolved.task_list_id === "string")
|
|
@@ -14442,6 +14720,194 @@ var init_project_bootstrap = __esm(() => {
|
|
|
14442
14720
|
init_task_lists();
|
|
14443
14721
|
});
|
|
14444
14722
|
|
|
14723
|
+
// src/db/labels.ts
|
|
14724
|
+
function rowToLabel(row) {
|
|
14725
|
+
return { ...row };
|
|
14726
|
+
}
|
|
14727
|
+
function normalizeName(name) {
|
|
14728
|
+
return name.trim().toLowerCase();
|
|
14729
|
+
}
|
|
14730
|
+
function createLabel(input, db) {
|
|
14731
|
+
const d = db || getDatabase();
|
|
14732
|
+
const id = uuid();
|
|
14733
|
+
const ts = now();
|
|
14734
|
+
d.run(`INSERT INTO labels (id, project_id, name, color, description, created_at, updated_at)
|
|
14735
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id ?? null, input.name.trim(), input.color ?? null, input.description ?? null, ts, ts]);
|
|
14736
|
+
return rowToLabel(d.query("SELECT * FROM labels WHERE id = ?").get(id));
|
|
14737
|
+
}
|
|
14738
|
+
function getLabel(idOrName, db) {
|
|
14739
|
+
const d = db || getDatabase();
|
|
14740
|
+
let row = d.query("SELECT * FROM labels WHERE id = ?").get(idOrName);
|
|
14741
|
+
if (!row) {
|
|
14742
|
+
row = d.query("SELECT * FROM labels WHERE lower(name) = ?").get(normalizeName(idOrName));
|
|
14743
|
+
}
|
|
14744
|
+
return row ? rowToLabel(row) : null;
|
|
14745
|
+
}
|
|
14746
|
+
function listLabels(projectId, db) {
|
|
14747
|
+
const d = db || getDatabase();
|
|
14748
|
+
if (projectId) {
|
|
14749
|
+
return d.query("SELECT * FROM labels WHERE project_id IS NULL OR project_id = ? ORDER BY name").all(projectId).map(rowToLabel);
|
|
14750
|
+
}
|
|
14751
|
+
return d.query("SELECT * FROM labels ORDER BY name").all().map(rowToLabel);
|
|
14752
|
+
}
|
|
14753
|
+
function updateLabel(idOrName, input, db) {
|
|
14754
|
+
const d = db || getDatabase();
|
|
14755
|
+
const existing = getLabel(idOrName, d);
|
|
14756
|
+
if (!existing)
|
|
14757
|
+
throw new Error(`Label not found: ${idOrName}`);
|
|
14758
|
+
const ts = now();
|
|
14759
|
+
d.run(`UPDATE labels SET
|
|
14760
|
+
name = COALESCE(?, name),
|
|
14761
|
+
color = COALESCE(?, color),
|
|
14762
|
+
description = COALESCE(?, description),
|
|
14763
|
+
updated_at = ?
|
|
14764
|
+
WHERE id = ?`, [input.name?.trim() ?? null, input.color ?? null, input.description ?? null, ts, existing.id]);
|
|
14765
|
+
return getLabel(existing.id, d);
|
|
14766
|
+
}
|
|
14767
|
+
function deleteLabel(idOrName, db) {
|
|
14768
|
+
const d = db || getDatabase();
|
|
14769
|
+
const existing = getLabel(idOrName, d);
|
|
14770
|
+
if (!existing)
|
|
14771
|
+
return false;
|
|
14772
|
+
d.run("DELETE FROM task_labels WHERE label_id = ?", [existing.id]);
|
|
14773
|
+
return d.run("DELETE FROM labels WHERE id = ?", [existing.id]).changes > 0;
|
|
14774
|
+
}
|
|
14775
|
+
function assignLabelToTask(taskId, labelIdOrName, db) {
|
|
14776
|
+
const d = db || getDatabase();
|
|
14777
|
+
const label = getLabel(labelIdOrName, d);
|
|
14778
|
+
if (!label)
|
|
14779
|
+
throw new Error(`Label not found: ${labelIdOrName}`);
|
|
14780
|
+
d.run("INSERT OR IGNORE INTO task_labels (task_id, label_id) VALUES (?, ?)", [taskId, label.id]);
|
|
14781
|
+
d.run("INSERT OR IGNORE INTO task_tags (task_id, tag) VALUES (?, ?)", [taskId, label.name]);
|
|
14782
|
+
return label;
|
|
14783
|
+
}
|
|
14784
|
+
var init_labels = __esm(() => {
|
|
14785
|
+
init_database();
|
|
14786
|
+
});
|
|
14787
|
+
|
|
14788
|
+
// src/db/tags.ts
|
|
14789
|
+
function normalizeName2(name) {
|
|
14790
|
+
return name.trim();
|
|
14791
|
+
}
|
|
14792
|
+
function taskCount(name, db) {
|
|
14793
|
+
const row = db.query("SELECT COUNT(*) AS count FROM task_tags WHERE tag = ?").get(name);
|
|
14794
|
+
return row?.count ?? 0;
|
|
14795
|
+
}
|
|
14796
|
+
function rowToTag(row, db) {
|
|
14797
|
+
return {
|
|
14798
|
+
...row,
|
|
14799
|
+
task_count: taskCount(row.name, db)
|
|
14800
|
+
};
|
|
14801
|
+
}
|
|
14802
|
+
function virtualTag(name, db) {
|
|
14803
|
+
return {
|
|
14804
|
+
id: name,
|
|
14805
|
+
name,
|
|
14806
|
+
color: null,
|
|
14807
|
+
description: null,
|
|
14808
|
+
task_count: taskCount(name, db),
|
|
14809
|
+
created_at: null,
|
|
14810
|
+
updated_at: null
|
|
14811
|
+
};
|
|
14812
|
+
}
|
|
14813
|
+
function getTagRow(idOrName, db) {
|
|
14814
|
+
return db.query("SELECT * FROM tags WHERE id = ?").get(idOrName) ?? db.query("SELECT * FROM tags WHERE lower(name) = lower(?)").get(idOrName);
|
|
14815
|
+
}
|
|
14816
|
+
function updateTaskTagJson(oldName, newName, db) {
|
|
14817
|
+
const rows = db.query("SELECT id, tags FROM tasks WHERE tags IS NOT NULL AND tags != '[]'").all();
|
|
14818
|
+
const update = db.prepare("UPDATE tasks SET tags = ?, updated_at = ? WHERE id = ?");
|
|
14819
|
+
const ts = now();
|
|
14820
|
+
for (const row of rows) {
|
|
14821
|
+
if (!row.tags)
|
|
14822
|
+
continue;
|
|
14823
|
+
let tags;
|
|
14824
|
+
try {
|
|
14825
|
+
tags = JSON.parse(row.tags);
|
|
14826
|
+
} catch {
|
|
14827
|
+
continue;
|
|
14828
|
+
}
|
|
14829
|
+
if (!tags.includes(oldName))
|
|
14830
|
+
continue;
|
|
14831
|
+
const next = newName ? Array.from(new Set(tags.map((tag) => tag === oldName ? newName : tag))) : tags.filter((tag) => tag !== oldName);
|
|
14832
|
+
update.run(JSON.stringify(next), ts, row.id);
|
|
14833
|
+
}
|
|
14834
|
+
}
|
|
14835
|
+
function renameTaskTagRows(oldName, newName, db) {
|
|
14836
|
+
const rows = db.query("SELECT task_id FROM task_tags WHERE tag = ?").all(oldName);
|
|
14837
|
+
const insert = db.prepare("INSERT OR IGNORE INTO task_tags (task_id, tag) VALUES (?, ?)");
|
|
14838
|
+
for (const row of rows)
|
|
14839
|
+
insert.run(row.task_id, newName);
|
|
14840
|
+
db.run("DELETE FROM task_tags WHERE tag = ?", [oldName]);
|
|
14841
|
+
}
|
|
14842
|
+
function createTag(input, db) {
|
|
14843
|
+
const d = db || getDatabase();
|
|
14844
|
+
const name = normalizeName2(input.name);
|
|
14845
|
+
if (!name)
|
|
14846
|
+
throw new Error("Tag name is required");
|
|
14847
|
+
const id = uuid();
|
|
14848
|
+
const ts = now();
|
|
14849
|
+
d.run(`INSERT INTO tags (id, name, color, description, created_at, updated_at)
|
|
14850
|
+
VALUES (?, ?, ?, ?, ?, ?)`, [id, name, input.color ?? null, input.description ?? null, ts, ts]);
|
|
14851
|
+
return rowToTag(d.query("SELECT * FROM tags WHERE id = ?").get(id), d);
|
|
14852
|
+
}
|
|
14853
|
+
function listTags(db) {
|
|
14854
|
+
const d = db || getDatabase();
|
|
14855
|
+
const rows = d.query("SELECT * FROM tags ORDER BY name").all();
|
|
14856
|
+
const tagsByName = new Map(rows.map((row) => [row.name.toLowerCase(), rowToTag(row, d)]));
|
|
14857
|
+
const taskTags = d.query("SELECT tag, COUNT(*) AS count FROM task_tags GROUP BY tag ORDER BY tag").all();
|
|
14858
|
+
for (const row of taskTags) {
|
|
14859
|
+
const key = row.tag.toLowerCase();
|
|
14860
|
+
if (!tagsByName.has(key))
|
|
14861
|
+
tagsByName.set(key, { ...virtualTag(row.tag, d), task_count: row.count });
|
|
14862
|
+
}
|
|
14863
|
+
return [...tagsByName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
14864
|
+
}
|
|
14865
|
+
function getTag(idOrName, db) {
|
|
14866
|
+
const d = db || getDatabase();
|
|
14867
|
+
const row = getTagRow(idOrName, d);
|
|
14868
|
+
if (row)
|
|
14869
|
+
return rowToTag(row, d);
|
|
14870
|
+
const existing = d.query("SELECT tag FROM task_tags WHERE lower(tag) = lower(?) LIMIT 1").get(idOrName);
|
|
14871
|
+
return existing ? virtualTag(existing.tag, d) : null;
|
|
14872
|
+
}
|
|
14873
|
+
function updateTag(idOrName, input, db) {
|
|
14874
|
+
const d = db || getDatabase();
|
|
14875
|
+
const existing = getTag(idOrName, d);
|
|
14876
|
+
if (!existing)
|
|
14877
|
+
throw new Error(`Tag not found: ${idOrName}`);
|
|
14878
|
+
const row = getTagRow(existing.id, d);
|
|
14879
|
+
if (!row)
|
|
14880
|
+
createTag({ name: existing.name, color: existing.color ?? undefined, description: existing.description ?? undefined }, d);
|
|
14881
|
+
const stored = getTagRow(existing.id, d) ?? getTagRow(existing.name, d);
|
|
14882
|
+
if (!stored)
|
|
14883
|
+
throw new Error(`Tag not found: ${idOrName}`);
|
|
14884
|
+
const nextName = input.name !== undefined ? normalizeName2(input.name) : stored.name;
|
|
14885
|
+
if (!nextName)
|
|
14886
|
+
throw new Error("Tag name is required");
|
|
14887
|
+
const duplicate = getTagRow(nextName, d);
|
|
14888
|
+
if (duplicate && duplicate.id !== stored.id)
|
|
14889
|
+
throw new Error(`Tag already exists: ${nextName}`);
|
|
14890
|
+
if (nextName !== stored.name) {
|
|
14891
|
+
renameTaskTagRows(stored.name, nextName, d);
|
|
14892
|
+
updateTaskTagJson(stored.name, nextName, d);
|
|
14893
|
+
}
|
|
14894
|
+
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]);
|
|
14895
|
+
return getTag(stored.id, d);
|
|
14896
|
+
}
|
|
14897
|
+
function deleteTag(idOrName, db) {
|
|
14898
|
+
const d = db || getDatabase();
|
|
14899
|
+
const existing = getTag(idOrName, d);
|
|
14900
|
+
if (!existing)
|
|
14901
|
+
return false;
|
|
14902
|
+
d.run("DELETE FROM tags WHERE id = ? OR lower(name) = lower(?)", [existing.id, existing.name]);
|
|
14903
|
+
d.run("DELETE FROM task_tags WHERE tag = ?", [existing.name]);
|
|
14904
|
+
updateTaskTagJson(existing.name, null, d);
|
|
14905
|
+
return true;
|
|
14906
|
+
}
|
|
14907
|
+
var init_tags = __esm(() => {
|
|
14908
|
+
init_database();
|
|
14909
|
+
});
|
|
14910
|
+
|
|
14445
14911
|
// src/lib/retention-cleanup.ts
|
|
14446
14912
|
import { existsSync as existsSync7, unlinkSync } from "fs";
|
|
14447
14913
|
function normalizeScopes(scopes) {
|
|
@@ -16676,18 +17142,18 @@ var init_local_fields = __esm(() => {
|
|
|
16676
17142
|
});
|
|
16677
17143
|
|
|
16678
17144
|
// src/lib/workflow-states.ts
|
|
16679
|
-
function
|
|
17145
|
+
function normalizeName3(value) {
|
|
16680
17146
|
return value.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
16681
17147
|
}
|
|
16682
17148
|
function isTaskStatus(value) {
|
|
16683
17149
|
return TASK_STATUSES.includes(value);
|
|
16684
17150
|
}
|
|
16685
17151
|
function normalizeState(input) {
|
|
16686
|
-
const name =
|
|
17152
|
+
const name = normalizeName3(input.name || "");
|
|
16687
17153
|
if (!name || !isTaskStatus(input.canonical_status))
|
|
16688
17154
|
return null;
|
|
16689
|
-
const aliases = [...new Set((input.aliases || []).map(
|
|
16690
|
-
const transitions = input.transitions ? [...new Set(input.transitions.map(
|
|
17155
|
+
const aliases = [...new Set((input.aliases || []).map(normalizeName3).filter((alias) => alias && alias !== name))].sort();
|
|
17156
|
+
const transitions = input.transitions ? [...new Set(input.transitions.map(normalizeName3).filter(Boolean))].sort() : null;
|
|
16691
17157
|
return {
|
|
16692
17158
|
name,
|
|
16693
17159
|
canonical_status: input.canonical_status,
|
|
@@ -16716,7 +17182,7 @@ function listWorkflowStates(projectPath) {
|
|
|
16716
17182
|
return mergeWorkflowConfig(workflowConfig);
|
|
16717
17183
|
}
|
|
16718
17184
|
function resolveWorkflowState(input, projectPath) {
|
|
16719
|
-
const normalized =
|
|
17185
|
+
const normalized = normalizeName3(input);
|
|
16720
17186
|
const states = listWorkflowStates(projectPath);
|
|
16721
17187
|
const byName = states.find((state) => state.name === normalized);
|
|
16722
17188
|
if (byName)
|
|
@@ -17706,10 +18172,10 @@ function summarizeTasks(taskIds, planIds, runIds, db) {
|
|
|
17706
18172
|
const completed = tasks.filter((task) => task.status === "completed");
|
|
17707
18173
|
const inProgress = tasks.filter((task) => task.status === "in_progress");
|
|
17708
18174
|
const pending = tasks.filter((task) => task.status === "pending");
|
|
17709
|
-
const
|
|
17710
|
-
const percent =
|
|
18175
|
+
const taskCount2 = tasks.length;
|
|
18176
|
+
const percent = taskCount2 === 0 ? 0 : Math.round(completed.length / taskCount2 * 100);
|
|
17711
18177
|
return {
|
|
17712
|
-
task_count:
|
|
18178
|
+
task_count: taskCount2,
|
|
17713
18179
|
completed_count: completed.length,
|
|
17714
18180
|
in_progress_count: inProgress.length,
|
|
17715
18181
|
pending_count: pending.length,
|
|
@@ -17717,7 +18183,7 @@ function summarizeTasks(taskIds, planIds, runIds, db) {
|
|
|
17717
18183
|
plan_count: planIds.length,
|
|
17718
18184
|
run_count: runIds.length,
|
|
17719
18185
|
percent_complete: percent,
|
|
17720
|
-
readiness:
|
|
18186
|
+
readiness: taskCount2 === 0 ? "empty" : blocked.length > 0 ? "blocked" : completed.length === taskCount2 ? "complete" : inProgress.length > 0 ? "in_progress" : "ready"
|
|
17721
18187
|
};
|
|
17722
18188
|
}
|
|
17723
18189
|
function createRoadmap(input) {
|
|
@@ -18896,7 +19362,7 @@ function normalizeScope(scope) {
|
|
|
18896
19362
|
}
|
|
18897
19363
|
return "tasks";
|
|
18898
19364
|
}
|
|
18899
|
-
function
|
|
19365
|
+
function normalizeName4(name) {
|
|
18900
19366
|
const normalized = name.trim();
|
|
18901
19367
|
if (!normalized)
|
|
18902
19368
|
throw new Error("Saved view name is required");
|
|
@@ -19146,7 +19612,7 @@ function runSavedSearch(filters = {}, scope = "tasks", db) {
|
|
|
19146
19612
|
}
|
|
19147
19613
|
function saveSearchView(input, db) {
|
|
19148
19614
|
const d = db || getDatabase();
|
|
19149
|
-
const name =
|
|
19615
|
+
const name = normalizeName4(input.name);
|
|
19150
19616
|
const timestamp3 = now();
|
|
19151
19617
|
const existing = getSearchView(name, d);
|
|
19152
19618
|
if (existing) {
|
|
@@ -20147,6 +20613,7 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
20147
20613
|
if (shouldRegisterTool("create_project")) {
|
|
20148
20614
|
server.tool("create_project", "Create a new project.", {
|
|
20149
20615
|
name: exports_external.string().describe("Project name"),
|
|
20616
|
+
path: exports_external.string().describe("Unique filesystem path for the project"),
|
|
20150
20617
|
description: exports_external.string().optional(),
|
|
20151
20618
|
status: exports_external.enum(["active", "completed", "on_hold", "archived"]).optional(),
|
|
20152
20619
|
short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if omitted)"),
|
|
@@ -20166,7 +20633,11 @@ function registerTaskProjectTools(server, ctx) {
|
|
|
20166
20633
|
limit: exports_external.number().optional()
|
|
20167
20634
|
}, async ({ status, limit }) => {
|
|
20168
20635
|
try {
|
|
20169
|
-
|
|
20636
|
+
let projects = listProjects();
|
|
20637
|
+
if (status)
|
|
20638
|
+
projects = projects.filter((p) => p.status === status);
|
|
20639
|
+
if (limit)
|
|
20640
|
+
projects = projects.slice(0, limit);
|
|
20170
20641
|
if (projects.length === 0)
|
|
20171
20642
|
return { content: [{ type: "text", text: "No projects found." }] };
|
|
20172
20643
|
const lines = projects.map((p) => `[${p.status}] ${p.short_id || p.id.slice(0, 8)} ${p.name}`);
|
|
@@ -20796,12 +21267,12 @@ Tasks:` : null,
|
|
|
20796
21267
|
});
|
|
20797
21268
|
}
|
|
20798
21269
|
if (shouldRegisterTool("list_tags")) {
|
|
20799
|
-
server.tool("list_tags", "List all tags.", async () => {
|
|
21270
|
+
server.tool("list_tags", "List all distinct task tags in use, with task counts.", async () => {
|
|
20800
21271
|
try {
|
|
20801
|
-
const
|
|
20802
|
-
if (
|
|
21272
|
+
const rows = listTags();
|
|
21273
|
+
if (rows.length === 0)
|
|
20803
21274
|
return { content: [{ type: "text", text: "No tags found." }] };
|
|
20804
|
-
const lines =
|
|
21275
|
+
const lines = rows.map((r) => `${r.color ? "[" + r.color + "] " : ""}${r.name} (${r.task_count})`);
|
|
20805
21276
|
return { content: [{ type: "text", text: lines.join(`
|
|
20806
21277
|
`) }] };
|
|
20807
21278
|
} catch (e) {
|
|
@@ -21370,6 +21841,8 @@ var init_task_project_tools = __esm(() => {
|
|
|
21370
21841
|
init_comments();
|
|
21371
21842
|
init_task_runs();
|
|
21372
21843
|
init_project_bootstrap();
|
|
21844
|
+
init_labels();
|
|
21845
|
+
init_tags();
|
|
21373
21846
|
init_redaction();
|
|
21374
21847
|
init_retention_cleanup();
|
|
21375
21848
|
init_mention_resolver();
|
|
@@ -24515,7 +24988,7 @@ var init_agent_run_dispatcher = __esm(() => {
|
|
|
24515
24988
|
|
|
24516
24989
|
// src/lib/verification-providers.ts
|
|
24517
24990
|
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
24518
|
-
function
|
|
24991
|
+
function normalizeName5(name) {
|
|
24519
24992
|
const normalized = name.trim().toLowerCase();
|
|
24520
24993
|
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
|
|
24521
24994
|
throw new Error("verification provider name must use lowercase letters, numbers, dashes, or underscores");
|
|
@@ -24534,10 +25007,10 @@ function timeoutMs(value) {
|
|
|
24534
25007
|
return Math.max(1, Math.min(24 * 60 * 60000, Math.floor(value)));
|
|
24535
25008
|
}
|
|
24536
25009
|
function getProvider(name) {
|
|
24537
|
-
return loadConfig().verification_providers?.[
|
|
25010
|
+
return loadConfig().verification_providers?.[normalizeName5(name)] || null;
|
|
24538
25011
|
}
|
|
24539
25012
|
function upsertVerificationProvider(input) {
|
|
24540
|
-
const name =
|
|
25013
|
+
const name = normalizeName5(input.name);
|
|
24541
25014
|
const config = loadConfig();
|
|
24542
25015
|
const existing = config.verification_providers?.[name];
|
|
24543
25016
|
const timestamp3 = new Date().toISOString();
|
|
@@ -24567,7 +25040,7 @@ function listVerificationProviders() {
|
|
|
24567
25040
|
return Object.values(loadConfig().verification_providers || {}).sort((a, b) => a.name.localeCompare(b.name));
|
|
24568
25041
|
}
|
|
24569
25042
|
function removeVerificationProvider(name) {
|
|
24570
|
-
const normalized =
|
|
25043
|
+
const normalized = normalizeName5(name);
|
|
24571
25044
|
const config = loadConfig();
|
|
24572
25045
|
if (!config.verification_providers?.[normalized])
|
|
24573
25046
|
return false;
|
|
@@ -28927,7 +29400,7 @@ import { basename as basename5, join as join9, resolve as resolve12 } from "path
|
|
|
28927
29400
|
function isObject2(value) {
|
|
28928
29401
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
28929
29402
|
}
|
|
28930
|
-
function
|
|
29403
|
+
function normalizeName6(name) {
|
|
28931
29404
|
const normalized = name.trim().toLowerCase();
|
|
28932
29405
|
if (!/^[a-z0-9][a-z0-9_.-]{0,63}$/.test(normalized)) {
|
|
28933
29406
|
throw new Error("extension name must use lowercase letters, numbers, dots, dashes, or underscores");
|
|
@@ -28945,7 +29418,7 @@ function permissionList(value) {
|
|
|
28945
29418
|
function normalizeManifest(input) {
|
|
28946
29419
|
if (!isObject2(input))
|
|
28947
29420
|
throw new Error("extension manifest must be a JSON object");
|
|
28948
|
-
const name =
|
|
29421
|
+
const name = normalizeName6(String(input["name"] || ""));
|
|
28949
29422
|
const version = String(input["version"] || "").trim();
|
|
28950
29423
|
if (!version)
|
|
28951
29424
|
throw new Error("extension manifest requires version");
|
|
@@ -28953,7 +29426,7 @@ function normalizeManifest(input) {
|
|
|
28953
29426
|
todos: typeof input["compatibility"]["todos"] === "string" ? input["compatibility"]["todos"] : undefined
|
|
28954
29427
|
} : undefined;
|
|
28955
29428
|
const commands = Array.isArray(input["commands"]) ? input["commands"].filter(isObject2).map((command) => ({
|
|
28956
|
-
name:
|
|
29429
|
+
name: normalizeName6(String(command["name"] || "")),
|
|
28957
29430
|
command: typeof command["command"] === "string" ? command["command"] : undefined,
|
|
28958
29431
|
description: typeof command["description"] === "string" ? command["description"] : undefined,
|
|
28959
29432
|
permissions: permissionList(command["permissions"]),
|
|
@@ -28962,12 +29435,12 @@ function normalizeManifest(input) {
|
|
|
28962
29435
|
network: typeof command["network"] === "boolean" ? command["network"] : undefined
|
|
28963
29436
|
})) : [];
|
|
28964
29437
|
const mcpTools = Array.isArray(input["mcp_tools"]) ? input["mcp_tools"].filter(isObject2).map((tool) => ({
|
|
28965
|
-
name:
|
|
29438
|
+
name: normalizeName6(String(tool["name"] || "")),
|
|
28966
29439
|
description: typeof tool["description"] === "string" ? tool["description"] : undefined,
|
|
28967
29440
|
permissions: permissionList(tool["permissions"])
|
|
28968
29441
|
})) : [];
|
|
28969
29442
|
const templates = Array.isArray(input["templates"]) ? input["templates"].filter(isObject2).map((template) => ({
|
|
28970
|
-
name:
|
|
29443
|
+
name: normalizeName6(String(template["name"] || "")),
|
|
28971
29444
|
kind: typeof template["kind"] === "string" ? template["kind"] : undefined,
|
|
28972
29445
|
description: typeof template["description"] === "string" ? template["description"] : undefined,
|
|
28973
29446
|
path: typeof template["path"] === "string" ? template["path"] : undefined,
|
|
@@ -28976,7 +29449,7 @@ function normalizeManifest(input) {
|
|
|
28976
29449
|
permissions: permissionList(template["permissions"])
|
|
28977
29450
|
})) : [];
|
|
28978
29451
|
const renderers = Array.isArray(input["renderers"]) ? input["renderers"].filter(isObject2).map((renderer) => ({
|
|
28979
|
-
name:
|
|
29452
|
+
name: normalizeName6(String(renderer["name"] || "")),
|
|
28980
29453
|
target: typeof renderer["target"] === "string" ? renderer["target"] : "",
|
|
28981
29454
|
description: typeof renderer["description"] === "string" ? renderer["description"] : undefined,
|
|
28982
29455
|
command: typeof renderer["command"] === "string" ? renderer["command"] : undefined,
|
|
@@ -29375,10 +29848,10 @@ function listLocalExtensions() {
|
|
|
29375
29848
|
return Object.values(loadConfig().extension_registry || {}).sort((a, b) => a.name.localeCompare(b.name));
|
|
29376
29849
|
}
|
|
29377
29850
|
function getLocalExtension(name) {
|
|
29378
|
-
return loadConfig().extension_registry?.[
|
|
29851
|
+
return loadConfig().extension_registry?.[normalizeName6(name)] || null;
|
|
29379
29852
|
}
|
|
29380
29853
|
function removeLocalExtension(name) {
|
|
29381
|
-
const normalized =
|
|
29854
|
+
const normalized = normalizeName6(name);
|
|
29382
29855
|
const config = loadConfig();
|
|
29383
29856
|
if (!config.extension_registry?.[normalized])
|
|
29384
29857
|
return false;
|
|
@@ -34889,8 +35362,6 @@ var init_builtin_templates = __esm(() => {
|
|
|
34889
35362
|
},
|
|
34890
35363
|
{
|
|
34891
35364
|
name: "open-source-project",
|
|
34892
|
-
version: 1,
|
|
34893
|
-
category: "project",
|
|
34894
35365
|
description: "Full open-source project bootstrap \u2014 scaffold to publish",
|
|
34895
35366
|
category: "open-source",
|
|
34896
35367
|
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
@@ -34916,7 +35387,7 @@ var init_builtin_templates = __esm(() => {
|
|
|
34916
35387
|
},
|
|
34917
35388
|
{
|
|
34918
35389
|
name: "release",
|
|
34919
|
-
version:
|
|
35390
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
34920
35391
|
category: "ops",
|
|
34921
35392
|
description: "Version release workflow \u2014 test, changelog, publish, verify",
|
|
34922
35393
|
variables: [
|
|
@@ -34933,7 +35404,7 @@ var init_builtin_templates = __esm(() => {
|
|
|
34933
35404
|
},
|
|
34934
35405
|
{
|
|
34935
35406
|
name: "docs-refresh",
|
|
34936
|
-
version:
|
|
35407
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
34937
35408
|
category: "workflow",
|
|
34938
35409
|
description: "Documentation refresh \u2014 audit, update, verify links",
|
|
34939
35410
|
variables: [{ name: "scope", required: true, description: "Docs scope (README, API, AGENTS.md)" }],
|
|
@@ -34946,7 +35417,7 @@ var init_builtin_templates = __esm(() => {
|
|
|
34946
35417
|
},
|
|
34947
35418
|
{
|
|
34948
35419
|
name: "migration",
|
|
34949
|
-
version:
|
|
35420
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
34950
35421
|
category: "ops",
|
|
34951
35422
|
description: "Schema/data migration workflow",
|
|
34952
35423
|
variables: [
|
|
@@ -34963,7 +35434,7 @@ var init_builtin_templates = __esm(() => {
|
|
|
34963
35434
|
},
|
|
34964
35435
|
{
|
|
34965
35436
|
name: "incident-response",
|
|
34966
|
-
version:
|
|
35437
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
34967
35438
|
category: "ops",
|
|
34968
35439
|
description: "Incident triage, mitigation, postmortem",
|
|
34969
35440
|
variables: [{ name: "incident", required: true, description: "Incident summary" }],
|
|
@@ -35010,8 +35481,8 @@ function registerTemplateTools(server, { shouldRegisterTool, resolveId, formatEr
|
|
|
35010
35481
|
const { createTemplate: createTemplate2, getTemplateWithTasks: getTemplateWithTasks2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
35011
35482
|
const t = createTemplate2(params);
|
|
35012
35483
|
const withTasks = getTemplateWithTasks2(t.id);
|
|
35013
|
-
const
|
|
35014
|
-
const taskInfo =
|
|
35484
|
+
const taskCount2 = withTasks?.tasks.length ?? 0;
|
|
35485
|
+
const taskInfo = taskCount2 > 0 ? ` | ${taskCount2} task(s)` : "";
|
|
35015
35486
|
return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"${taskInfo}` }] };
|
|
35016
35487
|
} catch (e) {
|
|
35017
35488
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -35930,22 +36401,27 @@ function getHeadlessBoundaryManifest() {
|
|
|
35930
36401
|
notes: [
|
|
35931
36402
|
"Use todos CLI or todos-mcp for agent workflows.",
|
|
35932
36403
|
"todos serve exposes a local-only REST API on 127.0.0.1 \u2014 not a hosted SaaS.",
|
|
35933
|
-
"
|
|
36404
|
+
"Remote sync is explicit opt-in from CLI/MCP, never from the dashboard."
|
|
35934
36405
|
]
|
|
35935
36406
|
};
|
|
35936
36407
|
}
|
|
35937
|
-
var HEADLESS_BOUNDARY_VERSION = "todos.headless-boundary.v1", FORBIDDEN_HOSTED_HOSTS, FORBIDDEN_WEB_PATTERNS, LOCAL_HOSTS;
|
|
36408
|
+
var HEADLESS_BOUNDARY_VERSION = "todos.headless-boundary.v1", FORBIDDEN_HOSTED_HOSTS, PRIVATE_PLATFORM_ORG, HOSTED_TODOS_PACKAGE, FORBIDDEN_WEB_PATTERNS, LOCAL_HOSTS;
|
|
35938
36409
|
var init_headless_boundaries = __esm(() => {
|
|
35939
36410
|
FORBIDDEN_HOSTED_HOSTS = [
|
|
35940
36411
|
"todos.md",
|
|
35941
36412
|
"www.todos.md",
|
|
35942
36413
|
"preview.todos.md",
|
|
35943
36414
|
"pay.hasna.tools",
|
|
35944
|
-
"platform
|
|
36415
|
+
["platform", "todos"].join("-")
|
|
35945
36416
|
];
|
|
36417
|
+
PRIVATE_PLATFORM_ORG = ["hasna", "studio"].join("");
|
|
36418
|
+
HOSTED_TODOS_PACKAGE = ["platform", "todos"].join("-");
|
|
35946
36419
|
FORBIDDEN_WEB_PATTERNS = [
|
|
35947
36420
|
{ name: "hosted todos.md API", pattern: /https?:\/\/(?:www\.)?todos\.md/i },
|
|
35948
|
-
{
|
|
36421
|
+
{
|
|
36422
|
+
name: "hosted platform package",
|
|
36423
|
+
pattern: new RegExp(`@${PRIVATE_PLATFORM_ORG}/${HOSTED_TODOS_PACKAGE}|${PRIVATE_PLATFORM_ORG}/${HOSTED_TODOS_PACKAGE}`, "i")
|
|
36424
|
+
},
|
|
35949
36425
|
{ name: "browser sign-in flow", pattern: /\/sign-?in\b|\/login\b.*(?:oauth|session|auth)/i },
|
|
35950
36426
|
{ name: "Stripe billing UI", pattern: /\bstripe\.(?:com|js)\b|\bcheckout\.sessions?\b/i },
|
|
35951
36427
|
{ name: "hosted OAuth redirect", pattern: /oauth.*redirect.*todos\.md/i }
|
|
@@ -36890,8 +37366,6 @@ Dashboard not found at: ${dashboardDir}`);
|
|
|
36890
37366
|
return handleMcpHttpRequest2(req, buildServer2);
|
|
36891
37367
|
}
|
|
36892
37368
|
if (method === "OPTIONS") {
|
|
36893
|
-
const reqOrigin2 = req.headers.get("origin") || undefined;
|
|
36894
|
-
const allowed = reqOrigin2 && (reqOrigin2 === `http://localhost:${port}` || reqOrigin2 === "http://localhost:0");
|
|
36895
37369
|
return new Response(null, {
|
|
36896
37370
|
headers: corsHeaders || {
|
|
36897
37371
|
Vary: "Origin"
|
|
@@ -37172,6 +37646,26 @@ function getMcpVersion() {
|
|
|
37172
37646
|
function hasVersionFlag() {
|
|
37173
37647
|
return process.argv.includes("--version") || process.argv.includes("-V");
|
|
37174
37648
|
}
|
|
37649
|
+
function hasHelpFlag() {
|
|
37650
|
+
return process.argv.includes("--help") || process.argv.includes("-h");
|
|
37651
|
+
}
|
|
37652
|
+
function printHelp() {
|
|
37653
|
+
console.log(`Usage: todos-mcp [options]
|
|
37654
|
+
|
|
37655
|
+
Start the @hasna/todos MCP server.
|
|
37656
|
+
|
|
37657
|
+
Options:
|
|
37658
|
+
--stdio Use stdio transport
|
|
37659
|
+
--port <port> Use Streamable HTTP on the given port
|
|
37660
|
+
-V, --version output the version number
|
|
37661
|
+
-h, --help display help for command
|
|
37662
|
+
|
|
37663
|
+
Environment:
|
|
37664
|
+
TODOS_MCP_STDIO=true Force stdio transport
|
|
37665
|
+
TODOS_MCP_PORT=<port> HTTP port when not using stdio
|
|
37666
|
+
TODOS_PROFILE=<profile> Tool profile filter
|
|
37667
|
+
TODOS_TOOL_GROUPS=<list> Comma-separated tool group filter`);
|
|
37668
|
+
}
|
|
37175
37669
|
function shouldRegisterTool(name) {
|
|
37176
37670
|
return shouldRegisterToolForProfile(name);
|
|
37177
37671
|
}
|
|
@@ -37312,8 +37806,17 @@ function buildServer() {
|
|
|
37312
37806
|
version: getMcpVersion()
|
|
37313
37807
|
});
|
|
37314
37808
|
installMcpTokenDiagnostics(server);
|
|
37809
|
+
const registeredToolNames = new Set;
|
|
37810
|
+
const shouldRegisterToolOnce = (name) => {
|
|
37811
|
+
if (!shouldRegisterTool(name))
|
|
37812
|
+
return false;
|
|
37813
|
+
if (registeredToolNames.has(name))
|
|
37814
|
+
return false;
|
|
37815
|
+
registeredToolNames.add(name);
|
|
37816
|
+
return true;
|
|
37817
|
+
};
|
|
37315
37818
|
const toolContext = {
|
|
37316
|
-
shouldRegisterTool,
|
|
37819
|
+
shouldRegisterTool: shouldRegisterToolOnce,
|
|
37317
37820
|
resolveId,
|
|
37318
37821
|
formatError,
|
|
37319
37822
|
formatTask,
|
|
@@ -37333,8 +37836,8 @@ function buildServer() {
|
|
|
37333
37836
|
registerTemplateTools(server, toolContext);
|
|
37334
37837
|
registerEnvironmentSnapshotTools(server, toolContext);
|
|
37335
37838
|
registerWorkflowPrompts(server);
|
|
37336
|
-
registerMachineTools(server, { shouldRegisterTool, formatError });
|
|
37337
|
-
registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
|
|
37839
|
+
registerMachineTools(server, { shouldRegisterTool: shouldRegisterToolOnce, formatError });
|
|
37840
|
+
registerDispatchTools(server, { shouldRegisterTool: shouldRegisterToolOnce, resolveId, formatError });
|
|
37338
37841
|
return server;
|
|
37339
37842
|
}
|
|
37340
37843
|
async function main() {
|
|
@@ -37376,6 +37879,10 @@ var init_mcp2 = __esm(() => {
|
|
|
37376
37879
|
console.log(getMcpVersion());
|
|
37377
37880
|
process.exit(0);
|
|
37378
37881
|
}
|
|
37882
|
+
if (hasHelpFlag()) {
|
|
37883
|
+
printHelp();
|
|
37884
|
+
process.exit(0);
|
|
37885
|
+
}
|
|
37379
37886
|
agentFocusMap = new Map;
|
|
37380
37887
|
isDirectRun = import.meta.main || process.argv[1]?.endsWith("/mcp/index.ts") || process.argv[1]?.endsWith("/mcp/index.js");
|
|
37381
37888
|
if (isDirectRun) {
|