@hasna/todos 0.11.42 → 0.11.44
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 +17 -9
- package/dist/cli/commands/environment-snapshots.d.ts +3 -0
- package/dist/cli/commands/environment-snapshots.d.ts.map +1 -0
- package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
- package/dist/cli/index.js +1017 -137
- package/dist/cli-mcp-parity.d.ts +1 -1
- package/dist/cli-mcp-parity.d.ts.map +1 -1
- package/dist/contracts.js +44 -0
- package/dist/db/builtin-templates.d.ts +17 -0
- package/dist/db/builtin-templates.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +602 -65
- package/dist/json-contracts.d.ts.map +1 -1
- package/dist/lib/environment-snapshots.d.ts +111 -0
- package/dist/lib/environment-snapshots.d.ts.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1274 -8
- package/dist/mcp/token-utils.d.ts.map +1 -1
- package/dist/mcp/tools/environment-snapshots.d.ts +8 -0
- package/dist/mcp/tools/environment-snapshots.d.ts.map +1 -0
- package/dist/mcp/tools/templates.d.ts.map +1 -1
- package/dist/mcp.js +5 -1
- package/dist/registry.js +94 -1
- package/dist/release-provenance.json +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2538,6 +2538,50 @@ var TODOS_JSON_CONTRACTS = [
|
|
|
2538
2538
|
},
|
|
2539
2539
|
optional: {}
|
|
2540
2540
|
}),
|
|
2541
|
+
contract({
|
|
2542
|
+
id: "environment_snapshot",
|
|
2543
|
+
name: "Environment Snapshot",
|
|
2544
|
+
description: "Local reproducibility snapshot for task and run verification context.",
|
|
2545
|
+
surfaces: ["cli", "mcp", "sdk"],
|
|
2546
|
+
stability: "stable",
|
|
2547
|
+
required: {
|
|
2548
|
+
schema_version: field("integer", "Snapshot schema version."),
|
|
2549
|
+
id: field("string", "Content-derived snapshot identifier."),
|
|
2550
|
+
captured_at: isoDateField,
|
|
2551
|
+
root: field("string", "Canonical local project root inspected."),
|
|
2552
|
+
machine: field("object", "Local hostname, platform, and architecture metadata."),
|
|
2553
|
+
target: field("object", "Optional task, run, and agent attachment IDs."),
|
|
2554
|
+
runtime: field("object", "Bun, Node, and executable metadata."),
|
|
2555
|
+
package_manager: field("object", "Detected package manager, lockfile hashes, and redacted manifests."),
|
|
2556
|
+
git: field("object", "Local git branch, commit, dirty state, and porcelain status."),
|
|
2557
|
+
config_hashes: field("array", "SHA-256 hashes of relevant local config files."),
|
|
2558
|
+
command_env: field("object", "Redacted command and environment metadata."),
|
|
2559
|
+
warnings: field("array", "Warnings about missing or unavailable local data.")
|
|
2560
|
+
},
|
|
2561
|
+
optional: {}
|
|
2562
|
+
}),
|
|
2563
|
+
contract({
|
|
2564
|
+
id: "environment_snapshot_comparison",
|
|
2565
|
+
name: "Environment Snapshot Comparison",
|
|
2566
|
+
description: "Drift summary returned when comparing two local environment snapshots.",
|
|
2567
|
+
surfaces: ["cli", "mcp", "sdk"],
|
|
2568
|
+
stability: "stable",
|
|
2569
|
+
required: {
|
|
2570
|
+
schema_version: field("integer", "Comparison schema version."),
|
|
2571
|
+
left_id: field("string", "Left snapshot ID."),
|
|
2572
|
+
right_id: field("string", "Right snapshot ID."),
|
|
2573
|
+
same_root: field("boolean", "Whether both snapshots were captured for the same root."),
|
|
2574
|
+
same_machine: field("boolean", "Whether hostname, platform, and architecture match."),
|
|
2575
|
+
same_runtime: field("boolean", "Whether Bun and Node versions match."),
|
|
2576
|
+
same_git_commit: field("boolean", "Whether git commit IDs match."),
|
|
2577
|
+
dirty_state_changed: field("boolean", "Whether the git dirty flag changed."),
|
|
2578
|
+
changed_config_hashes: field("array", "Changed config hash records."),
|
|
2579
|
+
changed_lockfiles: field("array", "Changed lockfile hash records."),
|
|
2580
|
+
changed_manifests: field("array", "Changed manifest hash records."),
|
|
2581
|
+
warnings: field("array", "Comparison warnings.")
|
|
2582
|
+
},
|
|
2583
|
+
optional: {}
|
|
2584
|
+
}),
|
|
2541
2585
|
contract({
|
|
2542
2586
|
id: "local_event_hook",
|
|
2543
2587
|
name: "Local Event Hook",
|
|
@@ -7393,6 +7437,8 @@ var MCP_TOOL_GROUPS = {
|
|
|
7393
7437
|
"check_file_lock",
|
|
7394
7438
|
"create_comment",
|
|
7395
7439
|
"create_handoff",
|
|
7440
|
+
"capture_environment_snapshot",
|
|
7441
|
+
"compare_environment_snapshots",
|
|
7396
7442
|
"create_inbox_item",
|
|
7397
7443
|
"delete_comment",
|
|
7398
7444
|
"detect_file_relationships",
|
|
@@ -7515,10 +7561,12 @@ var MCP_TOOL_GROUPS = {
|
|
|
7515
7561
|
"export_template",
|
|
7516
7562
|
"import_template",
|
|
7517
7563
|
"init_templates",
|
|
7564
|
+
"list_template_library",
|
|
7518
7565
|
"list_templates",
|
|
7519
7566
|
"preview_template",
|
|
7520
7567
|
"template_history",
|
|
7521
|
-
"update_template"
|
|
7568
|
+
"update_template",
|
|
7569
|
+
"write_template_library"
|
|
7522
7570
|
],
|
|
7523
7571
|
webhooks: ["create_webhook", "delete_webhook", "list_webhooks"],
|
|
7524
7572
|
machines: [
|
|
@@ -8033,6 +8081,39 @@ var TODOS_CLI_MCP_PARITY = [
|
|
|
8033
8081
|
mcpTool: "create_plan"
|
|
8034
8082
|
}
|
|
8035
8083
|
},
|
|
8084
|
+
{
|
|
8085
|
+
domain: "templates",
|
|
8086
|
+
cliCommands: [
|
|
8087
|
+
"todos template-library",
|
|
8088
|
+
"todos template-init",
|
|
8089
|
+
"todos template-preview",
|
|
8090
|
+
"todos templates --use",
|
|
8091
|
+
"todos template-export",
|
|
8092
|
+
"todos template-import",
|
|
8093
|
+
"todos template-history"
|
|
8094
|
+
],
|
|
8095
|
+
mcpTools: [
|
|
8096
|
+
"list_template_library",
|
|
8097
|
+
"write_template_library",
|
|
8098
|
+
"init_templates",
|
|
8099
|
+
"preview_template",
|
|
8100
|
+
"create_task_from_template",
|
|
8101
|
+
"create_template",
|
|
8102
|
+
"list_templates",
|
|
8103
|
+
"update_template",
|
|
8104
|
+
"delete_template",
|
|
8105
|
+
"export_template",
|
|
8106
|
+
"import_template",
|
|
8107
|
+
"template_history"
|
|
8108
|
+
],
|
|
8109
|
+
jsonContracts: ["template", "task", "structured_error", "api_error"],
|
|
8110
|
+
errorContracts: ["structured_error", "api_error"],
|
|
8111
|
+
status: "matched",
|
|
8112
|
+
example: {
|
|
8113
|
+
cli: "todos template-library --json",
|
|
8114
|
+
mcpTool: "list_template_library"
|
|
8115
|
+
}
|
|
8116
|
+
},
|
|
8036
8117
|
{
|
|
8037
8118
|
domain: "workspace-trust",
|
|
8038
8119
|
cliCommands: [
|
|
@@ -8332,6 +8413,18 @@ var TODOS_CLI_MCP_PARITY = [
|
|
|
8332
8413
|
mcpTool: "build_agent_context_pack"
|
|
8333
8414
|
}
|
|
8334
8415
|
},
|
|
8416
|
+
{
|
|
8417
|
+
domain: "environment-snapshots",
|
|
8418
|
+
cliCommands: ["todos env-snapshot"],
|
|
8419
|
+
mcpTools: ["capture_environment_snapshot", "compare_environment_snapshots"],
|
|
8420
|
+
jsonContracts: ["environment_snapshot", "environment_snapshot_comparison", "structured_error", "api_error"],
|
|
8421
|
+
errorContracts: ["structured_error", "api_error"],
|
|
8422
|
+
status: "matched",
|
|
8423
|
+
example: {
|
|
8424
|
+
cli: "todos env-snapshot capture --task 1234abcd --json",
|
|
8425
|
+
mcpTool: "capture_environment_snapshot"
|
|
8426
|
+
}
|
|
8427
|
+
},
|
|
8335
8428
|
{
|
|
8336
8429
|
domain: "imports",
|
|
8337
8430
|
cliCommands: [
|
|
@@ -10381,10 +10474,141 @@ function clearActiveModel() {
|
|
|
10381
10474
|
}
|
|
10382
10475
|
// src/db/builtin-templates.ts
|
|
10383
10476
|
init_database();
|
|
10477
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
10478
|
+
import { join as join7 } from "path";
|
|
10479
|
+
var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21";
|
|
10480
|
+
var BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library";
|
|
10384
10481
|
var BUILTIN_TEMPLATES = [
|
|
10482
|
+
{
|
|
10483
|
+
name: "bug-fix",
|
|
10484
|
+
description: "Reproduce, diagnose, fix, test, and release a defect.",
|
|
10485
|
+
category: "bug-fix",
|
|
10486
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10487
|
+
variables: [{ name: "bug", required: true, description: "Bug description" }],
|
|
10488
|
+
tasks: [
|
|
10489
|
+
{ position: 0, title_pattern: "Reproduce: {bug}", priority: "critical", tags: ["bug", "repro"] },
|
|
10490
|
+
{ position: 1, title_pattern: "Diagnose root cause of {bug}", priority: "critical", tags: ["bug", "diagnosis"], depends_on_positions: [0] },
|
|
10491
|
+
{ position: 2, title_pattern: "Write regression test for {bug}", priority: "high", tags: ["bug", "test"], depends_on_positions: [1] },
|
|
10492
|
+
{ position: 3, title_pattern: "Implement fix for {bug}", priority: "critical", tags: ["bug", "implementation"], depends_on_positions: [2] },
|
|
10493
|
+
{ position: 4, title_pattern: "Run full verification for {bug}", priority: "high", tags: ["bug", "verification"], depends_on_positions: [3] },
|
|
10494
|
+
{ position: 5, title_pattern: "Publish and smoke test fix for {bug}", priority: "high", tags: ["bug", "release"], depends_on_positions: [4] }
|
|
10495
|
+
]
|
|
10496
|
+
},
|
|
10497
|
+
{
|
|
10498
|
+
name: "feature-implementation",
|
|
10499
|
+
description: "Plan, build, test, document, and release a product feature.",
|
|
10500
|
+
category: "feature",
|
|
10501
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10502
|
+
variables: [
|
|
10503
|
+
{ name: "feature", required: true, description: "Feature name" },
|
|
10504
|
+
{ name: "scope", required: false, default: "medium", description: "Implementation size or risk" }
|
|
10505
|
+
],
|
|
10506
|
+
tasks: [
|
|
10507
|
+
{ position: 0, title_pattern: "Define acceptance criteria for {feature}", priority: "high", tags: ["feature", "spec"] },
|
|
10508
|
+
{ position: 1, title_pattern: "Design {scope} implementation plan for {feature}", priority: "high", tags: ["feature", "design"], depends_on_positions: [0] },
|
|
10509
|
+
{ position: 2, title_pattern: "Implement {feature}", priority: "critical", tags: ["feature", "implementation"], depends_on_positions: [1] },
|
|
10510
|
+
{ position: 3, title_pattern: "Add tests for {feature}", priority: "high", tags: ["feature", "test"], depends_on_positions: [2] },
|
|
10511
|
+
{ position: 4, title_pattern: "Update docs for {feature}", priority: "medium", tags: ["feature", "docs"], depends_on_positions: [2] },
|
|
10512
|
+
{ position: 5, title_pattern: "Run release checks for {feature}", priority: "high", tags: ["feature", "verification"], depends_on_positions: [3, 4] }
|
|
10513
|
+
]
|
|
10514
|
+
},
|
|
10515
|
+
{
|
|
10516
|
+
name: "security-review",
|
|
10517
|
+
description: "Threat model, test, remediate, and report on security posture.",
|
|
10518
|
+
category: "security",
|
|
10519
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10520
|
+
variables: [{ name: "target", required: true, description: "System, package, or change under review" }],
|
|
10521
|
+
tasks: [
|
|
10522
|
+
{ position: 0, title_pattern: "Map trust boundaries for {target}", priority: "critical", tags: ["security", "threat-model"] },
|
|
10523
|
+
{ position: 1, title_pattern: "Scan {target} for vulnerabilities and secret exposure", priority: "critical", tags: ["security", "scan"], depends_on_positions: [0] },
|
|
10524
|
+
{ position: 2, title_pattern: "Review authz, data access, and dependency risks in {target}", priority: "critical", tags: ["security", "review"], depends_on_positions: [0] },
|
|
10525
|
+
{ position: 3, title_pattern: "Fix critical security findings in {target}", priority: "critical", tags: ["security", "fix"], depends_on_positions: [1, 2] },
|
|
10526
|
+
{ position: 4, title_pattern: "Retest {target} security fixes", priority: "high", tags: ["security", "verification"], depends_on_positions: [3] },
|
|
10527
|
+
{ position: 5, title_pattern: "Write local security review report for {target}", priority: "medium", tags: ["security", "report"], depends_on_positions: [4] }
|
|
10528
|
+
]
|
|
10529
|
+
},
|
|
10530
|
+
{
|
|
10531
|
+
name: "release",
|
|
10532
|
+
description: "Prepare, verify, publish, install, and smoke test a package release.",
|
|
10533
|
+
category: "release",
|
|
10534
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10535
|
+
variables: [
|
|
10536
|
+
{ name: "package", required: true, description: "Package name" },
|
|
10537
|
+
{ name: "version", required: false, default: "patch", description: "Release version or bump type" }
|
|
10538
|
+
],
|
|
10539
|
+
tasks: [
|
|
10540
|
+
{ position: 0, title_pattern: "Prepare {package} {version} release notes", priority: "medium", tags: ["release", "docs"] },
|
|
10541
|
+
{ position: 1, title_pattern: "Run full tests for {package}", priority: "critical", tags: ["release", "test"] },
|
|
10542
|
+
{ position: 2, title_pattern: "Run build and release verification for {package}", priority: "critical", tags: ["release", "verification"], depends_on_positions: [1] },
|
|
10543
|
+
{ position: 3, title_pattern: "Scan {package} release diff for secrets", priority: "critical", tags: ["release", "security"], depends_on_positions: [2] },
|
|
10544
|
+
{ position: 4, title_pattern: "Publish {package} {version}", priority: "high", tags: ["release", "publish"], depends_on_positions: [3] },
|
|
10545
|
+
{ position: 5, title_pattern: "Install and smoke test published {package}", priority: "high", tags: ["release", "smoke"], depends_on_positions: [4] }
|
|
10546
|
+
]
|
|
10547
|
+
},
|
|
10548
|
+
{
|
|
10549
|
+
name: "migration",
|
|
10550
|
+
description: "Plan, test, apply, and verify a schema or data migration.",
|
|
10551
|
+
category: "migration",
|
|
10552
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10553
|
+
variables: [{ name: "migration", required: true, description: "Migration name or objective" }],
|
|
10554
|
+
tasks: [
|
|
10555
|
+
{ position: 0, title_pattern: "Design migration plan for {migration}", priority: "critical", tags: ["migration", "plan"] },
|
|
10556
|
+
{ position: 1, title_pattern: "Write rollback plan for {migration}", priority: "critical", tags: ["migration", "rollback"], depends_on_positions: [0] },
|
|
10557
|
+
{ position: 2, title_pattern: "Implement migration {migration}", priority: "critical", tags: ["migration", "implementation"], depends_on_positions: [0] },
|
|
10558
|
+
{ position: 3, title_pattern: "Test migration {migration} on fixture data", priority: "high", tags: ["migration", "test"], depends_on_positions: [2] },
|
|
10559
|
+
{ position: 4, title_pattern: "Run migration {migration} verification and drift checks", priority: "high", tags: ["migration", "verification"], depends_on_positions: [1, 3] },
|
|
10560
|
+
{ position: 5, title_pattern: "Document migration {migration} evidence", priority: "medium", tags: ["migration", "docs"], depends_on_positions: [4] }
|
|
10561
|
+
]
|
|
10562
|
+
},
|
|
10563
|
+
{
|
|
10564
|
+
name: "incident",
|
|
10565
|
+
description: "Triage, mitigate, repair, verify, and retrospect an incident.",
|
|
10566
|
+
category: "incident",
|
|
10567
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10568
|
+
variables: [{ name: "incident", required: true, description: "Incident summary" }],
|
|
10569
|
+
tasks: [
|
|
10570
|
+
{ position: 0, title_pattern: "Triage incident: {incident}", priority: "critical", tags: ["incident", "triage"] },
|
|
10571
|
+
{ position: 1, title_pattern: "Mitigate customer impact for {incident}", priority: "critical", tags: ["incident", "mitigation"], depends_on_positions: [0] },
|
|
10572
|
+
{ position: 2, title_pattern: "Diagnose root cause for {incident}", priority: "critical", tags: ["incident", "diagnosis"], depends_on_positions: [0] },
|
|
10573
|
+
{ position: 3, title_pattern: "Implement durable repair for {incident}", priority: "critical", tags: ["incident", "repair"], depends_on_positions: [2] },
|
|
10574
|
+
{ position: 4, title_pattern: "Verify recovery from {incident}", priority: "high", tags: ["incident", "verification"], depends_on_positions: [1, 3] },
|
|
10575
|
+
{ position: 5, title_pattern: "Write retrospective for {incident}", priority: "medium", tags: ["incident", "retro"], depends_on_positions: [4] }
|
|
10576
|
+
]
|
|
10577
|
+
},
|
|
10578
|
+
{
|
|
10579
|
+
name: "docs-refresh",
|
|
10580
|
+
description: "Audit, update, validate, and publish documentation changes.",
|
|
10581
|
+
category: "docs",
|
|
10582
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10583
|
+
variables: [{ name: "area", required: true, description: "Documentation area or product surface" }],
|
|
10584
|
+
tasks: [
|
|
10585
|
+
{ position: 0, title_pattern: "Audit current docs for {area}", priority: "medium", tags: ["docs", "audit"] },
|
|
10586
|
+
{ position: 1, title_pattern: "Update examples and commands for {area}", priority: "medium", tags: ["docs", "examples"], depends_on_positions: [0] },
|
|
10587
|
+
{ position: 2, title_pattern: "Validate docs snippets for {area}", priority: "high", tags: ["docs", "verification"], depends_on_positions: [1] },
|
|
10588
|
+
{ position: 3, title_pattern: "Refresh screenshots or generated artifacts for {area}", priority: "medium", tags: ["docs", "assets"], depends_on_positions: [1] },
|
|
10589
|
+
{ position: 4, title_pattern: "Publish docs refresh for {area}", priority: "medium", tags: ["docs", "release"], depends_on_positions: [2, 3] }
|
|
10590
|
+
]
|
|
10591
|
+
},
|
|
10592
|
+
{
|
|
10593
|
+
name: "qa",
|
|
10594
|
+
description: "Build a focused QA plan, execute checks, file defects, and sign off.",
|
|
10595
|
+
category: "qa",
|
|
10596
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10597
|
+
variables: [{ name: "target", required: true, description: "Feature, release, or workflow under QA" }],
|
|
10598
|
+
tasks: [
|
|
10599
|
+
{ position: 0, title_pattern: "Create QA matrix for {target}", priority: "high", tags: ["qa", "plan"] },
|
|
10600
|
+
{ position: 1, title_pattern: "Run happy-path QA for {target}", priority: "high", tags: ["qa", "manual"], depends_on_positions: [0] },
|
|
10601
|
+
{ position: 2, title_pattern: "Run edge-case QA for {target}", priority: "high", tags: ["qa", "edge-cases"], depends_on_positions: [0] },
|
|
10602
|
+
{ position: 3, title_pattern: "File and dedupe QA defects for {target}", priority: "medium", tags: ["qa", "bugs"], depends_on_positions: [1, 2] },
|
|
10603
|
+
{ position: 4, title_pattern: "Verify QA fixes for {target}", priority: "high", tags: ["qa", "verification"], depends_on_positions: [3] },
|
|
10604
|
+
{ position: 5, title_pattern: "Record QA signoff for {target}", priority: "medium", tags: ["qa", "signoff"], depends_on_positions: [4] }
|
|
10605
|
+
]
|
|
10606
|
+
},
|
|
10385
10607
|
{
|
|
10386
10608
|
name: "open-source-project",
|
|
10387
10609
|
description: "Full open-source project bootstrap \u2014 scaffold to publish",
|
|
10610
|
+
category: "open-source",
|
|
10611
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
10388
10612
|
variables: [
|
|
10389
10613
|
{ name: "name", required: true, description: "Service name" },
|
|
10390
10614
|
{ name: "org", required: false, default: "hasna", description: "GitHub org" }
|
|
@@ -10404,47 +10628,73 @@ var BUILTIN_TEMPLATES = [
|
|
|
10404
10628
|
{ position: 11, title_pattern: "Publish @hasna/{name} to npm", priority: "high", depends_on_positions: [6, 7, 8] },
|
|
10405
10629
|
{ position: 12, title_pattern: "Install @hasna/{name} globally and verify", priority: "medium", depends_on_positions: [11] }
|
|
10406
10630
|
]
|
|
10407
|
-
},
|
|
10408
|
-
{
|
|
10409
|
-
name: "bug-fix",
|
|
10410
|
-
description: "Standard bug fix workflow",
|
|
10411
|
-
variables: [{ name: "bug", required: true, description: "Bug description" }],
|
|
10412
|
-
tasks: [
|
|
10413
|
-
{ position: 0, title_pattern: "Reproduce: {bug}", priority: "critical" },
|
|
10414
|
-
{ position: 1, title_pattern: "Diagnose root cause of {bug}", priority: "critical", depends_on_positions: [0] },
|
|
10415
|
-
{ position: 2, title_pattern: "Implement fix for {bug}", priority: "critical", depends_on_positions: [1] },
|
|
10416
|
-
{ position: 3, title_pattern: "Write regression test for {bug}", priority: "high", depends_on_positions: [2] },
|
|
10417
|
-
{ position: 4, title_pattern: "Publish fix and verify in production", priority: "high", depends_on_positions: [3] }
|
|
10418
|
-
]
|
|
10419
|
-
},
|
|
10420
|
-
{
|
|
10421
|
-
name: "feature",
|
|
10422
|
-
description: "Standard feature development workflow",
|
|
10423
|
-
variables: [{ name: "feature", required: true }, { name: "scope", required: false, default: "medium" }],
|
|
10424
|
-
tasks: [
|
|
10425
|
-
{ position: 0, title_pattern: "Write spec for {feature}", priority: "high" },
|
|
10426
|
-
{ position: 1, title_pattern: "Design implementation approach for {feature}", priority: "high", depends_on_positions: [0] },
|
|
10427
|
-
{ position: 2, title_pattern: "Implement {feature}", priority: "critical", depends_on_positions: [1] },
|
|
10428
|
-
{ position: 3, title_pattern: "Write tests for {feature}", priority: "high", depends_on_positions: [2] },
|
|
10429
|
-
{ position: 4, title_pattern: "Code review for {feature}", priority: "medium", depends_on_positions: [3] },
|
|
10430
|
-
{ position: 5, title_pattern: "Update docs for {feature}", priority: "medium", depends_on_positions: [2] },
|
|
10431
|
-
{ position: 6, title_pattern: "Deploy {feature}", priority: "high", depends_on_positions: [4] }
|
|
10432
|
-
]
|
|
10433
|
-
},
|
|
10434
|
-
{
|
|
10435
|
-
name: "security-audit",
|
|
10436
|
-
description: "Security audit workflow",
|
|
10437
|
-
variables: [{ name: "target", required: true }],
|
|
10438
|
-
tasks: [
|
|
10439
|
-
{ position: 0, title_pattern: "Scan {target} for vulnerabilities", priority: "critical" },
|
|
10440
|
-
{ position: 1, title_pattern: "Review {target} security findings", priority: "critical", depends_on_positions: [0] },
|
|
10441
|
-
{ position: 2, title_pattern: "Fix critical issues in {target}", priority: "critical", depends_on_positions: [1] },
|
|
10442
|
-
{ position: 3, title_pattern: "Retest {target} after fixes", priority: "high", depends_on_positions: [2] },
|
|
10443
|
-
{ position: 4, title_pattern: "Write security report for {target}", priority: "medium", depends_on_positions: [3] },
|
|
10444
|
-
{ position: 5, title_pattern: "Close audit for {target}", priority: "low", depends_on_positions: [4] }
|
|
10445
|
-
]
|
|
10446
10631
|
}
|
|
10447
10632
|
];
|
|
10633
|
+
function templateMetadata(template) {
|
|
10634
|
+
return {
|
|
10635
|
+
source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
|
|
10636
|
+
library_version: template.version,
|
|
10637
|
+
category: template.category,
|
|
10638
|
+
template_file: `${template.name}.json`,
|
|
10639
|
+
local_only: true,
|
|
10640
|
+
marketplace_free: true
|
|
10641
|
+
};
|
|
10642
|
+
}
|
|
10643
|
+
function listBuiltinTemplates() {
|
|
10644
|
+
return BUILTIN_TEMPLATES.map((template) => ({
|
|
10645
|
+
...template,
|
|
10646
|
+
variables: template.variables.map((variable) => ({ ...variable })),
|
|
10647
|
+
tasks: template.tasks.map((task) => ({ ...task, tags: task.tags ? [...task.tags] : undefined }))
|
|
10648
|
+
}));
|
|
10649
|
+
}
|
|
10650
|
+
function getBuiltinTemplate(name) {
|
|
10651
|
+
return listBuiltinTemplates().find((template) => template.name === name) ?? null;
|
|
10652
|
+
}
|
|
10653
|
+
function exportBuiltinTemplate(name) {
|
|
10654
|
+
const template = getBuiltinTemplate(name);
|
|
10655
|
+
if (!template)
|
|
10656
|
+
throw new Error(`Built-in template not found: ${name}`);
|
|
10657
|
+
return {
|
|
10658
|
+
name: template.name,
|
|
10659
|
+
title_pattern: `${template.name}: {${template.variables[0]?.name ?? "name"}}`,
|
|
10660
|
+
description: template.description,
|
|
10661
|
+
priority: "medium",
|
|
10662
|
+
tags: [template.category, "local-template"],
|
|
10663
|
+
variables: template.variables,
|
|
10664
|
+
project_id: null,
|
|
10665
|
+
plan_id: null,
|
|
10666
|
+
metadata: templateMetadata(template),
|
|
10667
|
+
tasks: template.tasks.map((task) => ({
|
|
10668
|
+
position: task.position,
|
|
10669
|
+
title_pattern: task.title_pattern,
|
|
10670
|
+
description: task.description ?? null,
|
|
10671
|
+
priority: task.priority ?? "medium",
|
|
10672
|
+
tags: task.tags ?? [template.category],
|
|
10673
|
+
task_type: task.task_type ?? null,
|
|
10674
|
+
condition: task.condition ?? null,
|
|
10675
|
+
include_template_id: task.include_template_id ?? null,
|
|
10676
|
+
depends_on_positions: task.depends_on_positions ?? task.depends_on ?? [],
|
|
10677
|
+
metadata: task.metadata ?? { category: template.category }
|
|
10678
|
+
}))
|
|
10679
|
+
};
|
|
10680
|
+
}
|
|
10681
|
+
function exportBuiltinTemplateFiles() {
|
|
10682
|
+
return BUILTIN_TEMPLATES.map((template) => ({
|
|
10683
|
+
filename: `${template.name}.json`,
|
|
10684
|
+
template: exportBuiltinTemplate(template.name)
|
|
10685
|
+
}));
|
|
10686
|
+
}
|
|
10687
|
+
function writeBuiltinTemplateFiles(directory) {
|
|
10688
|
+
mkdirSync6(directory, { recursive: true });
|
|
10689
|
+
const files = [];
|
|
10690
|
+
for (const entry of exportBuiltinTemplateFiles()) {
|
|
10691
|
+
const path = join7(directory, entry.filename);
|
|
10692
|
+
writeFileSync4(path, `${JSON.stringify(entry.template, null, 2)}
|
|
10693
|
+
`, "utf-8");
|
|
10694
|
+
files.push(path);
|
|
10695
|
+
}
|
|
10696
|
+
return { directory, written: files.length, files };
|
|
10697
|
+
}
|
|
10448
10698
|
function initBuiltinTemplates(db) {
|
|
10449
10699
|
const d = db || getDatabase();
|
|
10450
10700
|
const existing = listTemplates(d);
|
|
@@ -10470,7 +10720,9 @@ function initBuiltinTemplates(db) {
|
|
|
10470
10720
|
name: bt.name,
|
|
10471
10721
|
title_pattern: `${bt.name}: {${bt.variables[0]?.name || "name"}}`,
|
|
10472
10722
|
description: bt.description,
|
|
10723
|
+
tags: [bt.category, "local-template"],
|
|
10473
10724
|
variables: bt.variables,
|
|
10725
|
+
metadata: templateMetadata(bt),
|
|
10474
10726
|
tasks
|
|
10475
10727
|
}, d);
|
|
10476
10728
|
created++;
|
|
@@ -11859,16 +12111,16 @@ function runSearchView(idOrName, db) {
|
|
|
11859
12111
|
return { ...runSavedSearch(view.filters, view.scope, d), view };
|
|
11860
12112
|
}
|
|
11861
12113
|
// src/lib/claude-tasks.ts
|
|
11862
|
-
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync2, writeFileSync as
|
|
11863
|
-
import { join as
|
|
12114
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
12115
|
+
import { join as join8 } from "path";
|
|
11864
12116
|
function getTaskListDir(taskListId) {
|
|
11865
|
-
return
|
|
12117
|
+
return join8(HOME, ".claude", "tasks", taskListId);
|
|
11866
12118
|
}
|
|
11867
12119
|
function readClaudeTask(dir, filename) {
|
|
11868
|
-
return readJsonFile(
|
|
12120
|
+
return readJsonFile(join8(dir, filename));
|
|
11869
12121
|
}
|
|
11870
12122
|
function writeClaudeTask(dir, task) {
|
|
11871
|
-
writeJsonFile(
|
|
12123
|
+
writeJsonFile(join8(dir, `${task.id}.json`), task);
|
|
11872
12124
|
}
|
|
11873
12125
|
function toClaudeStatus(status) {
|
|
11874
12126
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -11880,14 +12132,14 @@ function toSqliteStatus(status) {
|
|
|
11880
12132
|
return status;
|
|
11881
12133
|
}
|
|
11882
12134
|
function readPrefixCounter(dir) {
|
|
11883
|
-
const path =
|
|
12135
|
+
const path = join8(dir, ".prefix-counter");
|
|
11884
12136
|
if (!existsSync8(path))
|
|
11885
12137
|
return 0;
|
|
11886
12138
|
const val = parseInt(readFileSync6(path, "utf-8").trim(), 10);
|
|
11887
12139
|
return isNaN(val) ? 0 : val;
|
|
11888
12140
|
}
|
|
11889
12141
|
function writePrefixCounter(dir, value) {
|
|
11890
|
-
|
|
12142
|
+
writeFileSync5(join8(dir, ".prefix-counter"), String(value));
|
|
11891
12143
|
}
|
|
11892
12144
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
11893
12145
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -11923,7 +12175,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
11923
12175
|
const existingByTodosId = new Map;
|
|
11924
12176
|
const files = listJsonFiles(dir);
|
|
11925
12177
|
for (const f of files) {
|
|
11926
|
-
const path =
|
|
12178
|
+
const path = join8(dir, f);
|
|
11927
12179
|
const ct = readClaudeTask(dir, f);
|
|
11928
12180
|
if (ct?.metadata?.["todos_id"]) {
|
|
11929
12181
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -12030,7 +12282,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
12030
12282
|
}
|
|
12031
12283
|
for (const f of files) {
|
|
12032
12284
|
try {
|
|
12033
|
-
const filePath =
|
|
12285
|
+
const filePath = join8(dir, f);
|
|
12034
12286
|
const ct = readClaudeTask(dir, f);
|
|
12035
12287
|
if (!ct)
|
|
12036
12288
|
continue;
|
|
@@ -12099,26 +12351,26 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
12099
12351
|
|
|
12100
12352
|
// src/lib/agent-tasks.ts
|
|
12101
12353
|
import { existsSync as existsSync9 } from "fs";
|
|
12102
|
-
import { join as
|
|
12354
|
+
import { join as join9 } from "path";
|
|
12103
12355
|
function getTodosGlobalDir3() {
|
|
12104
|
-
const newDir =
|
|
12105
|
-
const legacyDir =
|
|
12356
|
+
const newDir = join9(HOME, ".hasna", "todos");
|
|
12357
|
+
const legacyDir = join9(HOME, ".todos");
|
|
12106
12358
|
if (!existsSync9(newDir) && existsSync9(legacyDir))
|
|
12107
12359
|
return legacyDir;
|
|
12108
12360
|
return newDir;
|
|
12109
12361
|
}
|
|
12110
12362
|
function agentBaseDir(agent) {
|
|
12111
12363
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
12112
|
-
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] ||
|
|
12364
|
+
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join9(getTodosGlobalDir3(), "agents");
|
|
12113
12365
|
}
|
|
12114
12366
|
function getTaskListDir2(agent, taskListId) {
|
|
12115
|
-
return
|
|
12367
|
+
return join9(agentBaseDir(agent), agent, taskListId);
|
|
12116
12368
|
}
|
|
12117
12369
|
function readAgentTask(dir, filename) {
|
|
12118
|
-
return readJsonFile(
|
|
12370
|
+
return readJsonFile(join9(dir, filename));
|
|
12119
12371
|
}
|
|
12120
12372
|
function writeAgentTask(dir, task) {
|
|
12121
|
-
writeJsonFile(
|
|
12373
|
+
writeJsonFile(join9(dir, `${task.id}.json`), task);
|
|
12122
12374
|
}
|
|
12123
12375
|
function taskToAgentTask(task, externalId, existingMeta) {
|
|
12124
12376
|
return {
|
|
@@ -12152,7 +12404,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
12152
12404
|
const existingByTodosId = new Map;
|
|
12153
12405
|
const files = listJsonFiles(dir);
|
|
12154
12406
|
for (const f of files) {
|
|
12155
|
-
const path =
|
|
12407
|
+
const path = join9(dir, f);
|
|
12156
12408
|
const at = readAgentTask(dir, f);
|
|
12157
12409
|
if (at?.metadata?.["todos_id"]) {
|
|
12158
12410
|
existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -12245,7 +12497,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
12245
12497
|
}
|
|
12246
12498
|
for (const f of files) {
|
|
12247
12499
|
try {
|
|
12248
|
-
const filePath =
|
|
12500
|
+
const filePath = join9(dir, f);
|
|
12249
12501
|
const at = readAgentTask(dir, f);
|
|
12250
12502
|
if (!at)
|
|
12251
12503
|
continue;
|
|
@@ -12383,7 +12635,7 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
12383
12635
|
}
|
|
12384
12636
|
// src/lib/extract.ts
|
|
12385
12637
|
import { readFileSync as readFileSync7, statSync as statSync4 } from "fs";
|
|
12386
|
-
import { relative as relative3, resolve as resolve7, join as
|
|
12638
|
+
import { relative as relative3, resolve as resolve7, join as join10 } from "path";
|
|
12387
12639
|
var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
|
|
12388
12640
|
var DEFAULT_EXTENSIONS = new Set([
|
|
12389
12641
|
".ts",
|
|
@@ -12517,7 +12769,7 @@ function extractTodos(options, db) {
|
|
|
12517
12769
|
const files = collectFiles(basePath, extensions);
|
|
12518
12770
|
const allComments = [];
|
|
12519
12771
|
for (const file of files) {
|
|
12520
|
-
const fullPath = statSync4(basePath).isFile() ? basePath :
|
|
12772
|
+
const fullPath = statSync4(basePath).isFile() ? basePath : join10(basePath, file);
|
|
12521
12773
|
try {
|
|
12522
12774
|
const source7 = readFileSync7(fullPath, "utf-8");
|
|
12523
12775
|
const relPath = statSync4(basePath).isFile() ? relative3(resolve7(basePath, ".."), fullPath) : file;
|
|
@@ -15720,8 +15972,8 @@ function getLocalActivityTimeline(options = {}, db) {
|
|
|
15720
15972
|
init_database();
|
|
15721
15973
|
init_migrations();
|
|
15722
15974
|
init_schema();
|
|
15723
|
-
import { chmodSync, copyFileSync, existsSync as existsSync11, mkdirSync as
|
|
15724
|
-
import { basename as basename2, dirname as dirname7, join as
|
|
15975
|
+
import { chmodSync, copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync7, statSync as statSync5 } from "fs";
|
|
15976
|
+
import { basename as basename2, dirname as dirname7, join as join11 } from "path";
|
|
15725
15977
|
var REQUIRED_TABLES = [
|
|
15726
15978
|
"_migrations",
|
|
15727
15979
|
"projects",
|
|
@@ -15891,13 +16143,13 @@ function createBackup(dbPath) {
|
|
|
15891
16143
|
if (!existsSync11(dbPath))
|
|
15892
16144
|
return;
|
|
15893
16145
|
const stamp = now().replace(/[:.]/g, "-");
|
|
15894
|
-
const backupDir =
|
|
16146
|
+
const backupDir = join11(dirname7(dbPath), `${basename2(dbPath)}.backup-${stamp}`);
|
|
15895
16147
|
const files = [];
|
|
15896
|
-
|
|
16148
|
+
mkdirSync7(backupDir, { recursive: true });
|
|
15897
16149
|
for (const source7 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
15898
16150
|
if (!existsSync11(source7))
|
|
15899
16151
|
continue;
|
|
15900
|
-
const target =
|
|
16152
|
+
const target = join11(backupDir, basename2(source7));
|
|
15901
16153
|
copyFileSync(source7, target);
|
|
15902
16154
|
files.push(target);
|
|
15903
16155
|
}
|
|
@@ -16101,6 +16353,278 @@ function runTodosDoctor(options = {}) {
|
|
|
16101
16353
|
summary
|
|
16102
16354
|
};
|
|
16103
16355
|
}
|
|
16356
|
+
// src/lib/environment-snapshots.ts
|
|
16357
|
+
import { createHash as createHash6 } from "crypto";
|
|
16358
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
|
|
16359
|
+
import { hostname, platform, arch } from "os";
|
|
16360
|
+
import { dirname as dirname8, join as join12, resolve as resolve9 } from "path";
|
|
16361
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
16362
|
+
init_database();
|
|
16363
|
+
var MANIFEST_FILES = ["package.json", "dashboard/package.json", "sdk/package.json"];
|
|
16364
|
+
var LOCKFILES = ["bun.lock", "bun.lockb", "package-lock.json", "npm-shrinkwrap.json"];
|
|
16365
|
+
var CONFIG_FILES = [
|
|
16366
|
+
"AGENTS.md",
|
|
16367
|
+
"CLAUDE.md",
|
|
16368
|
+
"README.md",
|
|
16369
|
+
"SECURITY.md",
|
|
16370
|
+
"bunfig.toml",
|
|
16371
|
+
"tsconfig.json",
|
|
16372
|
+
"components.json",
|
|
16373
|
+
"next.config.js",
|
|
16374
|
+
"next.config.mjs",
|
|
16375
|
+
"next.config.ts",
|
|
16376
|
+
"vite.config.ts",
|
|
16377
|
+
"dashboard/vite.config.ts"
|
|
16378
|
+
];
|
|
16379
|
+
function sha2563(value) {
|
|
16380
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
16381
|
+
}
|
|
16382
|
+
function fileRecord(root, relativePath) {
|
|
16383
|
+
const path = join12(root, relativePath);
|
|
16384
|
+
if (!existsSync12(path))
|
|
16385
|
+
return null;
|
|
16386
|
+
const stat = statSync6(path);
|
|
16387
|
+
if (!stat.isFile())
|
|
16388
|
+
return null;
|
|
16389
|
+
const content = readFileSync9(path);
|
|
16390
|
+
return { path: relativePath, sha256: sha2563(content), size_bytes: content.length };
|
|
16391
|
+
}
|
|
16392
|
+
function manifestRecord(root, relativePath) {
|
|
16393
|
+
const base = fileRecord(root, relativePath);
|
|
16394
|
+
if (!base)
|
|
16395
|
+
return null;
|
|
16396
|
+
const parsed = readJsonFile(join12(root, relativePath));
|
|
16397
|
+
if (!parsed)
|
|
16398
|
+
return { ...base, redacted: {} };
|
|
16399
|
+
const redacted = redactValue({
|
|
16400
|
+
name: parsed["name"] ?? null,
|
|
16401
|
+
version: parsed["version"] ?? null,
|
|
16402
|
+
packageManager: parsed["packageManager"] ?? null,
|
|
16403
|
+
scripts: parsed["scripts"] ?? {},
|
|
16404
|
+
dependencies: parsed["dependencies"] ?? {},
|
|
16405
|
+
devDependencies: parsed["devDependencies"] ?? {},
|
|
16406
|
+
peerDependencies: parsed["peerDependencies"] ?? {},
|
|
16407
|
+
optionalDependencies: parsed["optionalDependencies"] ?? {}
|
|
16408
|
+
});
|
|
16409
|
+
return { ...base, redacted };
|
|
16410
|
+
}
|
|
16411
|
+
function runLocalCommand(root, args) {
|
|
16412
|
+
const result = Bun.spawnSync({
|
|
16413
|
+
cmd: args,
|
|
16414
|
+
cwd: root,
|
|
16415
|
+
stdout: "pipe",
|
|
16416
|
+
stderr: "pipe",
|
|
16417
|
+
env: { PATH: process.env["PATH"] || "" }
|
|
16418
|
+
});
|
|
16419
|
+
return {
|
|
16420
|
+
exitCode: result.exitCode,
|
|
16421
|
+
stdout: redactEvidenceText(result.stdout.toString("utf8").trim()),
|
|
16422
|
+
stderr: redactEvidenceText(result.stderr.toString("utf8").trim())
|
|
16423
|
+
};
|
|
16424
|
+
}
|
|
16425
|
+
function summarizeGitStatus(lines) {
|
|
16426
|
+
const summary = { added: 0, modified: 0, deleted: 0, renamed: 0, untracked: 0 };
|
|
16427
|
+
for (const line of lines) {
|
|
16428
|
+
if (line.startsWith("??"))
|
|
16429
|
+
summary.untracked += 1;
|
|
16430
|
+
else if (line.includes("R"))
|
|
16431
|
+
summary.renamed += 1;
|
|
16432
|
+
else if (line.includes("D"))
|
|
16433
|
+
summary.deleted += 1;
|
|
16434
|
+
else if (line.includes("A"))
|
|
16435
|
+
summary.added += 1;
|
|
16436
|
+
else if (line.includes("M"))
|
|
16437
|
+
summary.modified += 1;
|
|
16438
|
+
}
|
|
16439
|
+
return summary;
|
|
16440
|
+
}
|
|
16441
|
+
function captureGit(root, warnings) {
|
|
16442
|
+
const inside = runLocalCommand(root, ["git", "rev-parse", "--is-inside-work-tree"]);
|
|
16443
|
+
if (inside.exitCode !== 0 || inside.stdout !== "true") {
|
|
16444
|
+
return { present: false, branch: null, commit: null, is_dirty: false, status_porcelain: [], status_summary: summarizeGitStatus([]) };
|
|
16445
|
+
}
|
|
16446
|
+
const branch = runLocalCommand(root, ["git", "branch", "--show-current"]);
|
|
16447
|
+
const commit = runLocalCommand(root, ["git", "rev-parse", "HEAD"]);
|
|
16448
|
+
const status = runLocalCommand(root, ["git", "status", "--porcelain=v1"]);
|
|
16449
|
+
if (commit.exitCode !== 0)
|
|
16450
|
+
warnings.push(`git commit unavailable: ${commit.stderr || commit.stdout || "unknown error"}`);
|
|
16451
|
+
if (status.exitCode !== 0)
|
|
16452
|
+
warnings.push(`git status unavailable: ${status.stderr || status.stdout || "unknown error"}`);
|
|
16453
|
+
const lines = status.stdout ? status.stdout.split(/\r?\n/).filter(Boolean) : [];
|
|
16454
|
+
return {
|
|
16455
|
+
present: true,
|
|
16456
|
+
branch: branch.stdout || null,
|
|
16457
|
+
commit: commit.stdout || null,
|
|
16458
|
+
is_dirty: lines.length > 0,
|
|
16459
|
+
status_porcelain: lines,
|
|
16460
|
+
status_summary: summarizeGitStatus(lines)
|
|
16461
|
+
};
|
|
16462
|
+
}
|
|
16463
|
+
function packageManager(env, lockfiles) {
|
|
16464
|
+
const userAgent = (env["npm_config_user_agent"] || "").toLowerCase();
|
|
16465
|
+
if (userAgent.includes("bun"))
|
|
16466
|
+
return "bun";
|
|
16467
|
+
if (lockfiles.some((file) => file.path.startsWith("bun.lock")))
|
|
16468
|
+
return "bun";
|
|
16469
|
+
if (userAgent.includes("npm") || lockfiles.some((file) => file.path.includes("package-lock")))
|
|
16470
|
+
return "npm";
|
|
16471
|
+
return "unknown";
|
|
16472
|
+
}
|
|
16473
|
+
function isSecretEnvKey(key) {
|
|
16474
|
+
return /api[_-]?key|token|secret|password|credential|private|session|cookie/i.test(key);
|
|
16475
|
+
}
|
|
16476
|
+
function commandEnv(env, includeValues) {
|
|
16477
|
+
const keys = Object.keys(env).sort();
|
|
16478
|
+
const interesting = keys.filter((key) => isSecretEnvKey(key) || ["CI", "NODE_ENV", "BUN_ENV", "SHELL", "TERM", "PATH", "PWD", "USER", "npm_config_user_agent"].includes(key) || key.startsWith("TODOS_") || key.startsWith("BUN_"));
|
|
16479
|
+
const redactedKeys = interesting.filter(isSecretEnvKey);
|
|
16480
|
+
const values = includeValues ? Object.fromEntries(interesting.map((key) => [key, isSecretEnvKey(key) ? "[REDACTED]" : redactEvidenceText(String(env[key] ?? ""))])) : null;
|
|
16481
|
+
return {
|
|
16482
|
+
command: null,
|
|
16483
|
+
env_keys: interesting,
|
|
16484
|
+
env: values,
|
|
16485
|
+
redacted_keys: redactedKeys
|
|
16486
|
+
};
|
|
16487
|
+
}
|
|
16488
|
+
function defaultSnapshotDir() {
|
|
16489
|
+
const dbPath = getDatabasePath();
|
|
16490
|
+
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
16491
|
+
return join12(tmpdir2(), "hasna-todos", "environment-snapshots");
|
|
16492
|
+
return join12(dirname8(resolve9(dbPath)), "environment-snapshots");
|
|
16493
|
+
}
|
|
16494
|
+
function snapshotWithId(snapshot) {
|
|
16495
|
+
const digest = sha2563(JSON.stringify(snapshot)).slice(0, 24);
|
|
16496
|
+
return { id: `env_${digest}`, ...snapshot };
|
|
16497
|
+
}
|
|
16498
|
+
function captureEnvironmentSnapshot(input = {}) {
|
|
16499
|
+
const root = resolve9(input.root || process.cwd());
|
|
16500
|
+
const env = input.env || process.env;
|
|
16501
|
+
const warnings = [];
|
|
16502
|
+
const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
|
|
16503
|
+
const lockfiles = LOCKFILES.map((file) => fileRecord(root, file)).filter((file) => Boolean(file));
|
|
16504
|
+
const configHashes = CONFIG_FILES.map((file) => fileRecord(root, file)).filter((file) => Boolean(file));
|
|
16505
|
+
const commandMetadata = commandEnv(env, Boolean(input.include_env_values));
|
|
16506
|
+
commandMetadata.command = input.command ? redactEvidenceText(input.command) : null;
|
|
16507
|
+
if (manifests.length === 0)
|
|
16508
|
+
warnings.push("no package manifest found");
|
|
16509
|
+
if (lockfiles.length === 0)
|
|
16510
|
+
warnings.push("no package lockfile found");
|
|
16511
|
+
return snapshotWithId({
|
|
16512
|
+
schema_version: 1,
|
|
16513
|
+
captured_at: input.now ? new Date(input.now).toISOString() : new Date().toISOString(),
|
|
16514
|
+
root,
|
|
16515
|
+
machine: { hostname: hostname(), platform: platform(), arch: arch() },
|
|
16516
|
+
target: {
|
|
16517
|
+
task_id: input.task_id ?? null,
|
|
16518
|
+
run_id: input.run_id ?? null,
|
|
16519
|
+
agent_id: input.agent_id ?? null
|
|
16520
|
+
},
|
|
16521
|
+
runtime: {
|
|
16522
|
+
bun: Bun.version || null,
|
|
16523
|
+
node: process.version,
|
|
16524
|
+
executable: process.execPath
|
|
16525
|
+
},
|
|
16526
|
+
package_manager: {
|
|
16527
|
+
manager: packageManager(env, lockfiles),
|
|
16528
|
+
user_agent: env["npm_config_user_agent"] ? redactEvidenceText(env["npm_config_user_agent"]) : null,
|
|
16529
|
+
manifests,
|
|
16530
|
+
lockfiles
|
|
16531
|
+
},
|
|
16532
|
+
git: captureGit(root, warnings),
|
|
16533
|
+
config_hashes: configHashes,
|
|
16534
|
+
command_env: commandMetadata,
|
|
16535
|
+
warnings
|
|
16536
|
+
});
|
|
16537
|
+
}
|
|
16538
|
+
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
16539
|
+
const path = outputPath ? resolve9(outputPath) : join12(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
16540
|
+
ensureDir2(dirname8(path));
|
|
16541
|
+
writeJsonFile(path, snapshot);
|
|
16542
|
+
return path;
|
|
16543
|
+
}
|
|
16544
|
+
function readEnvironmentSnapshot(path) {
|
|
16545
|
+
const snapshot = readJsonFile(resolve9(path));
|
|
16546
|
+
if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
|
|
16547
|
+
throw new Error(`Invalid environment snapshot: ${path}`);
|
|
16548
|
+
}
|
|
16549
|
+
return snapshot;
|
|
16550
|
+
}
|
|
16551
|
+
function recordEnvironmentSnapshot(input = {}, db) {
|
|
16552
|
+
let taskId = input.task_id;
|
|
16553
|
+
let runId = input.run_id;
|
|
16554
|
+
const needsDatabase = Boolean(taskId || runId);
|
|
16555
|
+
const d = needsDatabase ? db || getDatabase() : null;
|
|
16556
|
+
if (runId) {
|
|
16557
|
+
runId = resolveTaskRunId(runId, d);
|
|
16558
|
+
const run = getTaskRun(runId, d);
|
|
16559
|
+
if (!run)
|
|
16560
|
+
throw new Error(`Run not found: ${input.run_id}`);
|
|
16561
|
+
taskId = taskId || run.task_id;
|
|
16562
|
+
}
|
|
16563
|
+
if (taskId && d) {
|
|
16564
|
+
taskId = resolvePartialId(d, "tasks", taskId) || taskId;
|
|
16565
|
+
if (!getTask(taskId, d))
|
|
16566
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
16567
|
+
}
|
|
16568
|
+
const snapshot = captureEnvironmentSnapshot({ ...input, task_id: taskId, run_id: runId });
|
|
16569
|
+
const outputPath = writeEnvironmentSnapshot(snapshot, input.output_path);
|
|
16570
|
+
let taskVerificationId = null;
|
|
16571
|
+
let runArtifactId = null;
|
|
16572
|
+
if (runId) {
|
|
16573
|
+
const artifact = addTaskRunArtifact({
|
|
16574
|
+
run_id: runId,
|
|
16575
|
+
path: outputPath,
|
|
16576
|
+
artifact_type: "environment_snapshot",
|
|
16577
|
+
description: "Reproducible local environment snapshot",
|
|
16578
|
+
metadata: { environment_snapshot_id: snapshot.id, schema_version: snapshot.schema_version },
|
|
16579
|
+
store_content: input.store_content ?? true,
|
|
16580
|
+
agent_id: input.agent_id
|
|
16581
|
+
}, d);
|
|
16582
|
+
runArtifactId = artifact.id;
|
|
16583
|
+
} else if (taskId) {
|
|
16584
|
+
const verification = addTaskVerification({
|
|
16585
|
+
task_id: taskId,
|
|
16586
|
+
command: input.command || "capture environment snapshot",
|
|
16587
|
+
status: "unknown",
|
|
16588
|
+
output_summary: `environment snapshot ${snapshot.id}`,
|
|
16589
|
+
artifact_path: outputPath,
|
|
16590
|
+
agent_id: input.agent_id,
|
|
16591
|
+
run_at: snapshot.captured_at
|
|
16592
|
+
}, d);
|
|
16593
|
+
taskVerificationId = verification.id;
|
|
16594
|
+
}
|
|
16595
|
+
return { snapshot, output_path: outputPath, task_verification_id: taskVerificationId, run_artifact_id: runArtifactId };
|
|
16596
|
+
}
|
|
16597
|
+
function keyed(files) {
|
|
16598
|
+
return new Map(files.map((file) => [file.path, file]));
|
|
16599
|
+
}
|
|
16600
|
+
function diffFiles(left, right) {
|
|
16601
|
+
const leftMap = keyed(left);
|
|
16602
|
+
const rightMap = keyed(right);
|
|
16603
|
+
const paths = [...new Set([...leftMap.keys(), ...rightMap.keys()])].sort((a, b) => a.localeCompare(b));
|
|
16604
|
+
return paths.map((path) => ({ path, left_sha256: leftMap.get(path)?.sha256 ?? null, right_sha256: rightMap.get(path)?.sha256 ?? null })).filter((entry) => entry.left_sha256 !== entry.right_sha256);
|
|
16605
|
+
}
|
|
16606
|
+
function compareEnvironmentSnapshots(left, right) {
|
|
16607
|
+
const warnings = [];
|
|
16608
|
+
if (left.schema_version !== right.schema_version)
|
|
16609
|
+
warnings.push("snapshot schema versions differ");
|
|
16610
|
+
return {
|
|
16611
|
+
schema_version: 1,
|
|
16612
|
+
left_id: left.id,
|
|
16613
|
+
right_id: right.id,
|
|
16614
|
+
same_root: left.root === right.root,
|
|
16615
|
+
same_machine: left.machine.hostname === right.machine.hostname && left.machine.platform === right.machine.platform && left.machine.arch === right.machine.arch,
|
|
16616
|
+
same_runtime: left.runtime.bun === right.runtime.bun && left.runtime.node === right.runtime.node,
|
|
16617
|
+
same_git_commit: left.git.commit === right.git.commit,
|
|
16618
|
+
dirty_state_changed: left.git.is_dirty !== right.git.is_dirty,
|
|
16619
|
+
changed_config_hashes: diffFiles(left.config_hashes, right.config_hashes),
|
|
16620
|
+
changed_lockfiles: diffFiles(left.package_manager.lockfiles, right.package_manager.lockfiles),
|
|
16621
|
+
changed_manifests: diffFiles(left.package_manager.manifests, right.package_manager.manifests),
|
|
16622
|
+
warnings
|
|
16623
|
+
};
|
|
16624
|
+
}
|
|
16625
|
+
function compareEnvironmentSnapshotFiles(leftPath, rightPath) {
|
|
16626
|
+
return compareEnvironmentSnapshots(readEnvironmentSnapshot(leftPath), readEnvironmentSnapshot(rightPath));
|
|
16627
|
+
}
|
|
16104
16628
|
// src/lib/task-contracts.ts
|
|
16105
16629
|
init_database();
|
|
16106
16630
|
var CONTRACT_KEY = "_contract";
|
|
@@ -16315,6 +16839,8 @@ function checkTaskDoneContract(taskId, db) {
|
|
|
16315
16839
|
};
|
|
16316
16840
|
}
|
|
16317
16841
|
export {
|
|
16842
|
+
writeEnvironmentSnapshot,
|
|
16843
|
+
writeBuiltinTemplateFiles,
|
|
16318
16844
|
verifyTaskRunArtifacts,
|
|
16319
16845
|
verifyStoredArtifact,
|
|
16320
16846
|
verifyApiKey,
|
|
@@ -16413,6 +16939,8 @@ export {
|
|
|
16413
16939
|
redistributeStaleTasks,
|
|
16414
16940
|
redactEvidenceText,
|
|
16415
16941
|
recordTaskReview,
|
|
16942
|
+
recordEnvironmentSnapshot,
|
|
16943
|
+
readEnvironmentSnapshot,
|
|
16416
16944
|
queueAgentRun,
|
|
16417
16945
|
queryTasksByLocalFields,
|
|
16418
16946
|
previewTemplate,
|
|
@@ -16465,6 +16993,7 @@ export {
|
|
|
16465
16993
|
listCyclesWithStats,
|
|
16466
16994
|
listCycles,
|
|
16467
16995
|
listComments,
|
|
16996
|
+
listBuiltinTemplates,
|
|
16468
16997
|
listApprovalGates,
|
|
16469
16998
|
listApiKeys,
|
|
16470
16999
|
listAgents,
|
|
@@ -16559,6 +17088,7 @@ export {
|
|
|
16559
17088
|
getChecklist,
|
|
16560
17089
|
getCapableAgents,
|
|
16561
17090
|
getBurndown,
|
|
17091
|
+
getBuiltinTemplate,
|
|
16562
17092
|
getBudget,
|
|
16563
17093
|
getBlockingDeps,
|
|
16564
17094
|
getAgentMetrics,
|
|
@@ -16584,6 +17114,8 @@ export {
|
|
|
16584
17114
|
exportTodosMarkdown,
|
|
16585
17115
|
exportTemplate,
|
|
16586
17116
|
exportStoredArtifactContent,
|
|
17117
|
+
exportBuiltinTemplateFiles,
|
|
17118
|
+
exportBuiltinTemplate,
|
|
16587
17119
|
explainRunnerSandbox,
|
|
16588
17120
|
explainPolicyPack,
|
|
16589
17121
|
expireApprovalGate,
|
|
@@ -16649,6 +17181,8 @@ export {
|
|
|
16649
17181
|
createAgentContextPack,
|
|
16650
17182
|
countTasks,
|
|
16651
17183
|
completeTask,
|
|
17184
|
+
compareEnvironmentSnapshots,
|
|
17185
|
+
compareEnvironmentSnapshotFiles,
|
|
16652
17186
|
closeDatabase,
|
|
16653
17187
|
cloneTask,
|
|
16654
17188
|
clearChecklist,
|
|
@@ -16664,6 +17198,7 @@ export {
|
|
|
16664
17198
|
checkChecklistItem,
|
|
16665
17199
|
checkBudget,
|
|
16666
17200
|
checkApprovalGate,
|
|
17201
|
+
captureEnvironmentSnapshot,
|
|
16667
17202
|
cancelDispatch,
|
|
16668
17203
|
cancelAgentRunDispatch,
|
|
16669
17204
|
calculateDelay,
|
|
@@ -16736,6 +17271,8 @@ export {
|
|
|
16736
17271
|
DEFAULT_ENCRYPTION_KEY_ENV,
|
|
16737
17272
|
CompletionGuardError,
|
|
16738
17273
|
CORE_MCP_TOOLS,
|
|
17274
|
+
BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
17275
|
+
BUILTIN_TEMPLATE_LIBRARY_SOURCE,
|
|
16739
17276
|
BUILTIN_TEMPLATES,
|
|
16740
17277
|
AgentNotFoundError
|
|
16741
17278
|
};
|