@hasna/todos 0.11.42 → 0.11.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -9
- package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
- package/dist/cli/index.js +587 -120
- package/dist/cli-mcp-parity.d.ts +1 -1
- package/dist/cli-mcp-parity.d.ts.map +1 -1
- package/dist/db/builtin-templates.d.ts +17 -0
- package/dist/db/builtin-templates.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +266 -65
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +941 -3
- package/dist/mcp/token-utils.d.ts.map +1 -1
- package/dist/mcp/tools/templates.d.ts.map +1 -1
- package/dist/mcp.js +3 -1
- package/dist/registry.js +36 -1
- package/dist/release-provenance.json +3 -3
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -8908,9 +8908,83 @@ var init_plans = __esm(() => {
|
|
|
8908
8908
|
// src/db/builtin-templates.ts
|
|
8909
8909
|
var exports_builtin_templates = {};
|
|
8910
8910
|
__export(exports_builtin_templates, {
|
|
8911
|
+
writeBuiltinTemplateFiles: () => writeBuiltinTemplateFiles,
|
|
8912
|
+
listBuiltinTemplates: () => listBuiltinTemplates,
|
|
8911
8913
|
initBuiltinTemplates: () => initBuiltinTemplates,
|
|
8914
|
+
getBuiltinTemplate: () => getBuiltinTemplate,
|
|
8915
|
+
exportBuiltinTemplateFiles: () => exportBuiltinTemplateFiles,
|
|
8916
|
+
exportBuiltinTemplate: () => exportBuiltinTemplate,
|
|
8917
|
+
BUILTIN_TEMPLATE_LIBRARY_VERSION: () => BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
8918
|
+
BUILTIN_TEMPLATE_LIBRARY_SOURCE: () => BUILTIN_TEMPLATE_LIBRARY_SOURCE,
|
|
8912
8919
|
BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
|
|
8913
8920
|
});
|
|
8921
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
8922
|
+
import { join as join5 } from "path";
|
|
8923
|
+
function templateMetadata(template) {
|
|
8924
|
+
return {
|
|
8925
|
+
source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
|
|
8926
|
+
library_version: template.version,
|
|
8927
|
+
category: template.category,
|
|
8928
|
+
template_file: `${template.name}.json`,
|
|
8929
|
+
local_only: true,
|
|
8930
|
+
marketplace_free: true
|
|
8931
|
+
};
|
|
8932
|
+
}
|
|
8933
|
+
function listBuiltinTemplates() {
|
|
8934
|
+
return BUILTIN_TEMPLATES.map((template) => ({
|
|
8935
|
+
...template,
|
|
8936
|
+
variables: template.variables.map((variable) => ({ ...variable })),
|
|
8937
|
+
tasks: template.tasks.map((task) => ({ ...task, tags: task.tags ? [...task.tags] : undefined }))
|
|
8938
|
+
}));
|
|
8939
|
+
}
|
|
8940
|
+
function getBuiltinTemplate(name) {
|
|
8941
|
+
return listBuiltinTemplates().find((template) => template.name === name) ?? null;
|
|
8942
|
+
}
|
|
8943
|
+
function exportBuiltinTemplate(name) {
|
|
8944
|
+
const template = getBuiltinTemplate(name);
|
|
8945
|
+
if (!template)
|
|
8946
|
+
throw new Error(`Built-in template not found: ${name}`);
|
|
8947
|
+
return {
|
|
8948
|
+
name: template.name,
|
|
8949
|
+
title_pattern: `${template.name}: {${template.variables[0]?.name ?? "name"}}`,
|
|
8950
|
+
description: template.description,
|
|
8951
|
+
priority: "medium",
|
|
8952
|
+
tags: [template.category, "local-template"],
|
|
8953
|
+
variables: template.variables,
|
|
8954
|
+
project_id: null,
|
|
8955
|
+
plan_id: null,
|
|
8956
|
+
metadata: templateMetadata(template),
|
|
8957
|
+
tasks: template.tasks.map((task) => ({
|
|
8958
|
+
position: task.position,
|
|
8959
|
+
title_pattern: task.title_pattern,
|
|
8960
|
+
description: task.description ?? null,
|
|
8961
|
+
priority: task.priority ?? "medium",
|
|
8962
|
+
tags: task.tags ?? [template.category],
|
|
8963
|
+
task_type: task.task_type ?? null,
|
|
8964
|
+
condition: task.condition ?? null,
|
|
8965
|
+
include_template_id: task.include_template_id ?? null,
|
|
8966
|
+
depends_on_positions: task.depends_on_positions ?? task.depends_on ?? [],
|
|
8967
|
+
metadata: task.metadata ?? { category: template.category }
|
|
8968
|
+
}))
|
|
8969
|
+
};
|
|
8970
|
+
}
|
|
8971
|
+
function exportBuiltinTemplateFiles() {
|
|
8972
|
+
return BUILTIN_TEMPLATES.map((template) => ({
|
|
8973
|
+
filename: `${template.name}.json`,
|
|
8974
|
+
template: exportBuiltinTemplate(template.name)
|
|
8975
|
+
}));
|
|
8976
|
+
}
|
|
8977
|
+
function writeBuiltinTemplateFiles(directory) {
|
|
8978
|
+
mkdirSync4(directory, { recursive: true });
|
|
8979
|
+
const files = [];
|
|
8980
|
+
for (const entry of exportBuiltinTemplateFiles()) {
|
|
8981
|
+
const path = join5(directory, entry.filename);
|
|
8982
|
+
writeFileSync2(path, `${JSON.stringify(entry.template, null, 2)}
|
|
8983
|
+
`, "utf-8");
|
|
8984
|
+
files.push(path);
|
|
8985
|
+
}
|
|
8986
|
+
return { directory, written: files.length, files };
|
|
8987
|
+
}
|
|
8914
8988
|
function initBuiltinTemplates(db) {
|
|
8915
8989
|
const d = db || getDatabase();
|
|
8916
8990
|
const existing = listTemplates(d);
|
|
@@ -8936,7 +9010,9 @@ function initBuiltinTemplates(db) {
|
|
|
8936
9010
|
name: bt.name,
|
|
8937
9011
|
title_pattern: `${bt.name}: {${bt.variables[0]?.name || "name"}}`,
|
|
8938
9012
|
description: bt.description,
|
|
9013
|
+
tags: [bt.category, "local-template"],
|
|
8939
9014
|
variables: bt.variables,
|
|
9015
|
+
metadata: templateMetadata(bt),
|
|
8940
9016
|
tasks
|
|
8941
9017
|
}, d);
|
|
8942
9018
|
created++;
|
|
@@ -8944,14 +9020,141 @@ function initBuiltinTemplates(db) {
|
|
|
8944
9020
|
}
|
|
8945
9021
|
return { created, skipped, names };
|
|
8946
9022
|
}
|
|
8947
|
-
var BUILTIN_TEMPLATES;
|
|
9023
|
+
var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21", BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library", BUILTIN_TEMPLATES;
|
|
8948
9024
|
var init_builtin_templates = __esm(() => {
|
|
8949
9025
|
init_database();
|
|
8950
9026
|
init_templates();
|
|
8951
9027
|
BUILTIN_TEMPLATES = [
|
|
9028
|
+
{
|
|
9029
|
+
name: "bug-fix",
|
|
9030
|
+
description: "Reproduce, diagnose, fix, test, and release a defect.",
|
|
9031
|
+
category: "bug-fix",
|
|
9032
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
9033
|
+
variables: [{ name: "bug", required: true, description: "Bug description" }],
|
|
9034
|
+
tasks: [
|
|
9035
|
+
{ position: 0, title_pattern: "Reproduce: {bug}", priority: "critical", tags: ["bug", "repro"] },
|
|
9036
|
+
{ position: 1, title_pattern: "Diagnose root cause of {bug}", priority: "critical", tags: ["bug", "diagnosis"], depends_on_positions: [0] },
|
|
9037
|
+
{ position: 2, title_pattern: "Write regression test for {bug}", priority: "high", tags: ["bug", "test"], depends_on_positions: [1] },
|
|
9038
|
+
{ position: 3, title_pattern: "Implement fix for {bug}", priority: "critical", tags: ["bug", "implementation"], depends_on_positions: [2] },
|
|
9039
|
+
{ position: 4, title_pattern: "Run full verification for {bug}", priority: "high", tags: ["bug", "verification"], depends_on_positions: [3] },
|
|
9040
|
+
{ position: 5, title_pattern: "Publish and smoke test fix for {bug}", priority: "high", tags: ["bug", "release"], depends_on_positions: [4] }
|
|
9041
|
+
]
|
|
9042
|
+
},
|
|
9043
|
+
{
|
|
9044
|
+
name: "feature-implementation",
|
|
9045
|
+
description: "Plan, build, test, document, and release a product feature.",
|
|
9046
|
+
category: "feature",
|
|
9047
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
9048
|
+
variables: [
|
|
9049
|
+
{ name: "feature", required: true, description: "Feature name" },
|
|
9050
|
+
{ name: "scope", required: false, default: "medium", description: "Implementation size or risk" }
|
|
9051
|
+
],
|
|
9052
|
+
tasks: [
|
|
9053
|
+
{ position: 0, title_pattern: "Define acceptance criteria for {feature}", priority: "high", tags: ["feature", "spec"] },
|
|
9054
|
+
{ position: 1, title_pattern: "Design {scope} implementation plan for {feature}", priority: "high", tags: ["feature", "design"], depends_on_positions: [0] },
|
|
9055
|
+
{ position: 2, title_pattern: "Implement {feature}", priority: "critical", tags: ["feature", "implementation"], depends_on_positions: [1] },
|
|
9056
|
+
{ position: 3, title_pattern: "Add tests for {feature}", priority: "high", tags: ["feature", "test"], depends_on_positions: [2] },
|
|
9057
|
+
{ position: 4, title_pattern: "Update docs for {feature}", priority: "medium", tags: ["feature", "docs"], depends_on_positions: [2] },
|
|
9058
|
+
{ position: 5, title_pattern: "Run release checks for {feature}", priority: "high", tags: ["feature", "verification"], depends_on_positions: [3, 4] }
|
|
9059
|
+
]
|
|
9060
|
+
},
|
|
9061
|
+
{
|
|
9062
|
+
name: "security-review",
|
|
9063
|
+
description: "Threat model, test, remediate, and report on security posture.",
|
|
9064
|
+
category: "security",
|
|
9065
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
9066
|
+
variables: [{ name: "target", required: true, description: "System, package, or change under review" }],
|
|
9067
|
+
tasks: [
|
|
9068
|
+
{ position: 0, title_pattern: "Map trust boundaries for {target}", priority: "critical", tags: ["security", "threat-model"] },
|
|
9069
|
+
{ position: 1, title_pattern: "Scan {target} for vulnerabilities and secret exposure", priority: "critical", tags: ["security", "scan"], depends_on_positions: [0] },
|
|
9070
|
+
{ position: 2, title_pattern: "Review authz, data access, and dependency risks in {target}", priority: "critical", tags: ["security", "review"], depends_on_positions: [0] },
|
|
9071
|
+
{ position: 3, title_pattern: "Fix critical security findings in {target}", priority: "critical", tags: ["security", "fix"], depends_on_positions: [1, 2] },
|
|
9072
|
+
{ position: 4, title_pattern: "Retest {target} security fixes", priority: "high", tags: ["security", "verification"], depends_on_positions: [3] },
|
|
9073
|
+
{ position: 5, title_pattern: "Write local security review report for {target}", priority: "medium", tags: ["security", "report"], depends_on_positions: [4] }
|
|
9074
|
+
]
|
|
9075
|
+
},
|
|
9076
|
+
{
|
|
9077
|
+
name: "release",
|
|
9078
|
+
description: "Prepare, verify, publish, install, and smoke test a package release.",
|
|
9079
|
+
category: "release",
|
|
9080
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
9081
|
+
variables: [
|
|
9082
|
+
{ name: "package", required: true, description: "Package name" },
|
|
9083
|
+
{ name: "version", required: false, default: "patch", description: "Release version or bump type" }
|
|
9084
|
+
],
|
|
9085
|
+
tasks: [
|
|
9086
|
+
{ position: 0, title_pattern: "Prepare {package} {version} release notes", priority: "medium", tags: ["release", "docs"] },
|
|
9087
|
+
{ position: 1, title_pattern: "Run full tests for {package}", priority: "critical", tags: ["release", "test"] },
|
|
9088
|
+
{ position: 2, title_pattern: "Run build and release verification for {package}", priority: "critical", tags: ["release", "verification"], depends_on_positions: [1] },
|
|
9089
|
+
{ position: 3, title_pattern: "Scan {package} release diff for secrets", priority: "critical", tags: ["release", "security"], depends_on_positions: [2] },
|
|
9090
|
+
{ position: 4, title_pattern: "Publish {package} {version}", priority: "high", tags: ["release", "publish"], depends_on_positions: [3] },
|
|
9091
|
+
{ position: 5, title_pattern: "Install and smoke test published {package}", priority: "high", tags: ["release", "smoke"], depends_on_positions: [4] }
|
|
9092
|
+
]
|
|
9093
|
+
},
|
|
9094
|
+
{
|
|
9095
|
+
name: "migration",
|
|
9096
|
+
description: "Plan, test, apply, and verify a schema or data migration.",
|
|
9097
|
+
category: "migration",
|
|
9098
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
9099
|
+
variables: [{ name: "migration", required: true, description: "Migration name or objective" }],
|
|
9100
|
+
tasks: [
|
|
9101
|
+
{ position: 0, title_pattern: "Design migration plan for {migration}", priority: "critical", tags: ["migration", "plan"] },
|
|
9102
|
+
{ position: 1, title_pattern: "Write rollback plan for {migration}", priority: "critical", tags: ["migration", "rollback"], depends_on_positions: [0] },
|
|
9103
|
+
{ position: 2, title_pattern: "Implement migration {migration}", priority: "critical", tags: ["migration", "implementation"], depends_on_positions: [0] },
|
|
9104
|
+
{ position: 3, title_pattern: "Test migration {migration} on fixture data", priority: "high", tags: ["migration", "test"], depends_on_positions: [2] },
|
|
9105
|
+
{ position: 4, title_pattern: "Run migration {migration} verification and drift checks", priority: "high", tags: ["migration", "verification"], depends_on_positions: [1, 3] },
|
|
9106
|
+
{ position: 5, title_pattern: "Document migration {migration} evidence", priority: "medium", tags: ["migration", "docs"], depends_on_positions: [4] }
|
|
9107
|
+
]
|
|
9108
|
+
},
|
|
9109
|
+
{
|
|
9110
|
+
name: "incident",
|
|
9111
|
+
description: "Triage, mitigate, repair, verify, and retrospect an incident.",
|
|
9112
|
+
category: "incident",
|
|
9113
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
9114
|
+
variables: [{ name: "incident", required: true, description: "Incident summary" }],
|
|
9115
|
+
tasks: [
|
|
9116
|
+
{ position: 0, title_pattern: "Triage incident: {incident}", priority: "critical", tags: ["incident", "triage"] },
|
|
9117
|
+
{ position: 1, title_pattern: "Mitigate customer impact for {incident}", priority: "critical", tags: ["incident", "mitigation"], depends_on_positions: [0] },
|
|
9118
|
+
{ position: 2, title_pattern: "Diagnose root cause for {incident}", priority: "critical", tags: ["incident", "diagnosis"], depends_on_positions: [0] },
|
|
9119
|
+
{ position: 3, title_pattern: "Implement durable repair for {incident}", priority: "critical", tags: ["incident", "repair"], depends_on_positions: [2] },
|
|
9120
|
+
{ position: 4, title_pattern: "Verify recovery from {incident}", priority: "high", tags: ["incident", "verification"], depends_on_positions: [1, 3] },
|
|
9121
|
+
{ position: 5, title_pattern: "Write retrospective for {incident}", priority: "medium", tags: ["incident", "retro"], depends_on_positions: [4] }
|
|
9122
|
+
]
|
|
9123
|
+
},
|
|
9124
|
+
{
|
|
9125
|
+
name: "docs-refresh",
|
|
9126
|
+
description: "Audit, update, validate, and publish documentation changes.",
|
|
9127
|
+
category: "docs",
|
|
9128
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
9129
|
+
variables: [{ name: "area", required: true, description: "Documentation area or product surface" }],
|
|
9130
|
+
tasks: [
|
|
9131
|
+
{ position: 0, title_pattern: "Audit current docs for {area}", priority: "medium", tags: ["docs", "audit"] },
|
|
9132
|
+
{ position: 1, title_pattern: "Update examples and commands for {area}", priority: "medium", tags: ["docs", "examples"], depends_on_positions: [0] },
|
|
9133
|
+
{ position: 2, title_pattern: "Validate docs snippets for {area}", priority: "high", tags: ["docs", "verification"], depends_on_positions: [1] },
|
|
9134
|
+
{ position: 3, title_pattern: "Refresh screenshots or generated artifacts for {area}", priority: "medium", tags: ["docs", "assets"], depends_on_positions: [1] },
|
|
9135
|
+
{ position: 4, title_pattern: "Publish docs refresh for {area}", priority: "medium", tags: ["docs", "release"], depends_on_positions: [2, 3] }
|
|
9136
|
+
]
|
|
9137
|
+
},
|
|
9138
|
+
{
|
|
9139
|
+
name: "qa",
|
|
9140
|
+
description: "Build a focused QA plan, execute checks, file defects, and sign off.",
|
|
9141
|
+
category: "qa",
|
|
9142
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
9143
|
+
variables: [{ name: "target", required: true, description: "Feature, release, or workflow under QA" }],
|
|
9144
|
+
tasks: [
|
|
9145
|
+
{ position: 0, title_pattern: "Create QA matrix for {target}", priority: "high", tags: ["qa", "plan"] },
|
|
9146
|
+
{ position: 1, title_pattern: "Run happy-path QA for {target}", priority: "high", tags: ["qa", "manual"], depends_on_positions: [0] },
|
|
9147
|
+
{ position: 2, title_pattern: "Run edge-case QA for {target}", priority: "high", tags: ["qa", "edge-cases"], depends_on_positions: [0] },
|
|
9148
|
+
{ position: 3, title_pattern: "File and dedupe QA defects for {target}", priority: "medium", tags: ["qa", "bugs"], depends_on_positions: [1, 2] },
|
|
9149
|
+
{ position: 4, title_pattern: "Verify QA fixes for {target}", priority: "high", tags: ["qa", "verification"], depends_on_positions: [3] },
|
|
9150
|
+
{ position: 5, title_pattern: "Record QA signoff for {target}", priority: "medium", tags: ["qa", "signoff"], depends_on_positions: [4] }
|
|
9151
|
+
]
|
|
9152
|
+
},
|
|
8952
9153
|
{
|
|
8953
9154
|
name: "open-source-project",
|
|
8954
9155
|
description: "Full open-source project bootstrap \u2014 scaffold to publish",
|
|
9156
|
+
category: "open-source",
|
|
9157
|
+
version: BUILTIN_TEMPLATE_LIBRARY_VERSION,
|
|
8955
9158
|
variables: [
|
|
8956
9159
|
{ name: "name", required: true, description: "Service name" },
|
|
8957
9160
|
{ name: "org", required: false, default: "hasna", description: "GitHub org" }
|
|
@@ -8971,45 +9174,6 @@ var init_builtin_templates = __esm(() => {
|
|
|
8971
9174
|
{ position: 11, title_pattern: "Publish @hasna/{name} to npm", priority: "high", depends_on_positions: [6, 7, 8] },
|
|
8972
9175
|
{ position: 12, title_pattern: "Install @hasna/{name} globally and verify", priority: "medium", depends_on_positions: [11] }
|
|
8973
9176
|
]
|
|
8974
|
-
},
|
|
8975
|
-
{
|
|
8976
|
-
name: "bug-fix",
|
|
8977
|
-
description: "Standard bug fix workflow",
|
|
8978
|
-
variables: [{ name: "bug", required: true, description: "Bug description" }],
|
|
8979
|
-
tasks: [
|
|
8980
|
-
{ position: 0, title_pattern: "Reproduce: {bug}", priority: "critical" },
|
|
8981
|
-
{ position: 1, title_pattern: "Diagnose root cause of {bug}", priority: "critical", depends_on_positions: [0] },
|
|
8982
|
-
{ position: 2, title_pattern: "Implement fix for {bug}", priority: "critical", depends_on_positions: [1] },
|
|
8983
|
-
{ position: 3, title_pattern: "Write regression test for {bug}", priority: "high", depends_on_positions: [2] },
|
|
8984
|
-
{ position: 4, title_pattern: "Publish fix and verify in production", priority: "high", depends_on_positions: [3] }
|
|
8985
|
-
]
|
|
8986
|
-
},
|
|
8987
|
-
{
|
|
8988
|
-
name: "feature",
|
|
8989
|
-
description: "Standard feature development workflow",
|
|
8990
|
-
variables: [{ name: "feature", required: true }, { name: "scope", required: false, default: "medium" }],
|
|
8991
|
-
tasks: [
|
|
8992
|
-
{ position: 0, title_pattern: "Write spec for {feature}", priority: "high" },
|
|
8993
|
-
{ position: 1, title_pattern: "Design implementation approach for {feature}", priority: "high", depends_on_positions: [0] },
|
|
8994
|
-
{ position: 2, title_pattern: "Implement {feature}", priority: "critical", depends_on_positions: [1] },
|
|
8995
|
-
{ position: 3, title_pattern: "Write tests for {feature}", priority: "high", depends_on_positions: [2] },
|
|
8996
|
-
{ position: 4, title_pattern: "Code review for {feature}", priority: "medium", depends_on_positions: [3] },
|
|
8997
|
-
{ position: 5, title_pattern: "Update docs for {feature}", priority: "medium", depends_on_positions: [2] },
|
|
8998
|
-
{ position: 6, title_pattern: "Deploy {feature}", priority: "high", depends_on_positions: [4] }
|
|
8999
|
-
]
|
|
9000
|
-
},
|
|
9001
|
-
{
|
|
9002
|
-
name: "security-audit",
|
|
9003
|
-
description: "Security audit workflow",
|
|
9004
|
-
variables: [{ name: "target", required: true }],
|
|
9005
|
-
tasks: [
|
|
9006
|
-
{ position: 0, title_pattern: "Scan {target} for vulnerabilities", priority: "critical" },
|
|
9007
|
-
{ position: 1, title_pattern: "Review {target} security findings", priority: "critical", depends_on_positions: [0] },
|
|
9008
|
-
{ position: 2, title_pattern: "Fix critical issues in {target}", priority: "critical", depends_on_positions: [1] },
|
|
9009
|
-
{ position: 3, title_pattern: "Retest {target} after fixes", priority: "high", depends_on_positions: [2] },
|
|
9010
|
-
{ position: 4, title_pattern: "Write security report for {target}", priority: "medium", depends_on_positions: [3] },
|
|
9011
|
-
{ position: 5, title_pattern: "Close audit for {target}", priority: "low", depends_on_positions: [4] }
|
|
9012
|
-
]
|
|
9013
9177
|
}
|
|
9014
9178
|
];
|
|
9015
9179
|
});
|
|
@@ -9277,7 +9441,7 @@ function registerPlanTemplateCommands(program2) {
|
|
|
9277
9441
|
console.log(` ${chalk3.dim(t.id.slice(0, 8))} ${chalk3.bold(t.name)} ${chalk3.cyan(`"${t.title_pattern}"`)} ${chalk3.yellow(t.priority)}${vars}`);
|
|
9278
9442
|
}
|
|
9279
9443
|
});
|
|
9280
|
-
program2.command("template-init").alias("templates-init").description("Initialize
|
|
9444
|
+
program2.command("template-init").alias("templates-init").description("Initialize the bundled local template library").action(async () => {
|
|
9281
9445
|
const globalOpts = program2.opts();
|
|
9282
9446
|
const { initBuiltinTemplates: initBuiltinTemplates2 } = await Promise.resolve().then(() => (init_builtin_templates(), exports_builtin_templates));
|
|
9283
9447
|
const result = initBuiltinTemplates2();
|
|
@@ -9291,6 +9455,52 @@ function registerPlanTemplateCommands(program2) {
|
|
|
9291
9455
|
console.log(chalk3.green(`Created ${result.created} template(s): ${result.names.join(", ")}. Skipped ${result.skipped} existing.`));
|
|
9292
9456
|
}
|
|
9293
9457
|
});
|
|
9458
|
+
program2.command("template-library").alias("templates-library").description("List, show, or write the bundled local template library as editable JSON files").option("--show <name>", "Show one bundled template as JSON").option("--write <dir>", "Write all bundled templates to editable JSON files").action(async (opts) => {
|
|
9459
|
+
const globalOpts = program2.opts();
|
|
9460
|
+
const {
|
|
9461
|
+
exportBuiltinTemplate: exportBuiltinTemplate2,
|
|
9462
|
+
listBuiltinTemplates: listBuiltinTemplates2,
|
|
9463
|
+
writeBuiltinTemplateFiles: writeBuiltinTemplateFiles2
|
|
9464
|
+
} = await Promise.resolve().then(() => (init_builtin_templates(), exports_builtin_templates));
|
|
9465
|
+
try {
|
|
9466
|
+
if (opts.show) {
|
|
9467
|
+
const template = exportBuiltinTemplate2(opts.show);
|
|
9468
|
+
output(template, true);
|
|
9469
|
+
return;
|
|
9470
|
+
}
|
|
9471
|
+
if (opts.write) {
|
|
9472
|
+
const result = writeBuiltinTemplateFiles2(opts.write);
|
|
9473
|
+
if (globalOpts.json) {
|
|
9474
|
+
output(result, true);
|
|
9475
|
+
return;
|
|
9476
|
+
}
|
|
9477
|
+
console.log(chalk3.green(`Wrote ${result.written} editable template file(s) to ${result.directory}`));
|
|
9478
|
+
for (const file of result.files)
|
|
9479
|
+
console.log(chalk3.dim(` ${file}`));
|
|
9480
|
+
return;
|
|
9481
|
+
}
|
|
9482
|
+
const templates = listBuiltinTemplates2().map((template) => ({
|
|
9483
|
+
name: template.name,
|
|
9484
|
+
description: template.description,
|
|
9485
|
+
category: template.category,
|
|
9486
|
+
version: template.version,
|
|
9487
|
+
variables: template.variables,
|
|
9488
|
+
task_count: template.tasks.length
|
|
9489
|
+
}));
|
|
9490
|
+
if (globalOpts.json) {
|
|
9491
|
+
output(templates, true);
|
|
9492
|
+
return;
|
|
9493
|
+
}
|
|
9494
|
+
console.log(chalk3.bold(`${templates.length} bundled local template(s):
|
|
9495
|
+
`));
|
|
9496
|
+
for (const template of templates) {
|
|
9497
|
+
console.log(` ${chalk3.bold(template.name)} ${chalk3.dim(`[${template.category}]`)} ${chalk3.yellow(`${template.task_count} tasks`)}`);
|
|
9498
|
+
console.log(chalk3.dim(` ${template.description}`));
|
|
9499
|
+
}
|
|
9500
|
+
} catch (e) {
|
|
9501
|
+
handleError(e);
|
|
9502
|
+
}
|
|
9503
|
+
});
|
|
9294
9504
|
program2.command("template-preview <id>").alias("templates-preview").description("Preview a template without creating tasks \u2014 shows resolved titles, deps, and priorities").option("--var <vars...>", "Variable substitution in key=value format (e.g. --var name=invoices)").action(async (id, opts) => {
|
|
9295
9505
|
const globalOpts = program2.opts();
|
|
9296
9506
|
const { previewTemplate: previewTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
@@ -10008,16 +10218,16 @@ var init_saved_search_views = __esm(() => {
|
|
|
10008
10218
|
});
|
|
10009
10219
|
|
|
10010
10220
|
// src/lib/claude-tasks.ts
|
|
10011
|
-
import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as
|
|
10012
|
-
import { join as
|
|
10221
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
10222
|
+
import { join as join6 } from "path";
|
|
10013
10223
|
function getTaskListDir(taskListId) {
|
|
10014
|
-
return
|
|
10224
|
+
return join6(HOME, ".claude", "tasks", taskListId);
|
|
10015
10225
|
}
|
|
10016
10226
|
function readClaudeTask(dir, filename) {
|
|
10017
|
-
return readJsonFile(
|
|
10227
|
+
return readJsonFile(join6(dir, filename));
|
|
10018
10228
|
}
|
|
10019
10229
|
function writeClaudeTask(dir, task) {
|
|
10020
|
-
writeJsonFile(
|
|
10230
|
+
writeJsonFile(join6(dir, `${task.id}.json`), task);
|
|
10021
10231
|
}
|
|
10022
10232
|
function toClaudeStatus(status) {
|
|
10023
10233
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -10029,14 +10239,14 @@ function toSqliteStatus(status) {
|
|
|
10029
10239
|
return status;
|
|
10030
10240
|
}
|
|
10031
10241
|
function readPrefixCounter(dir) {
|
|
10032
|
-
const path =
|
|
10242
|
+
const path = join6(dir, ".prefix-counter");
|
|
10033
10243
|
if (!existsSync5(path))
|
|
10034
10244
|
return 0;
|
|
10035
10245
|
const val = parseInt(readFileSync3(path, "utf-8").trim(), 10);
|
|
10036
10246
|
return isNaN(val) ? 0 : val;
|
|
10037
10247
|
}
|
|
10038
10248
|
function writePrefixCounter(dir, value) {
|
|
10039
|
-
|
|
10249
|
+
writeFileSync3(join6(dir, ".prefix-counter"), String(value));
|
|
10040
10250
|
}
|
|
10041
10251
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
10042
10252
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -10072,7 +10282,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
10072
10282
|
const existingByTodosId = new Map;
|
|
10073
10283
|
const files = listJsonFiles(dir);
|
|
10074
10284
|
for (const f of files) {
|
|
10075
|
-
const path =
|
|
10285
|
+
const path = join6(dir, f);
|
|
10076
10286
|
const ct = readClaudeTask(dir, f);
|
|
10077
10287
|
if (ct?.metadata?.["todos_id"]) {
|
|
10078
10288
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -10179,7 +10389,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
10179
10389
|
}
|
|
10180
10390
|
for (const f of files) {
|
|
10181
10391
|
try {
|
|
10182
|
-
const filePath =
|
|
10392
|
+
const filePath = join6(dir, f);
|
|
10183
10393
|
const ct = readClaudeTask(dir, f);
|
|
10184
10394
|
if (!ct)
|
|
10185
10395
|
continue;
|
|
@@ -10253,26 +10463,26 @@ var init_claude_tasks = __esm(() => {
|
|
|
10253
10463
|
|
|
10254
10464
|
// src/lib/agent-tasks.ts
|
|
10255
10465
|
import { existsSync as existsSync6 } from "fs";
|
|
10256
|
-
import { join as
|
|
10466
|
+
import { join as join7 } from "path";
|
|
10257
10467
|
function getTodosGlobalDir2() {
|
|
10258
|
-
const newDir =
|
|
10259
|
-
const legacyDir =
|
|
10468
|
+
const newDir = join7(HOME, ".hasna", "todos");
|
|
10469
|
+
const legacyDir = join7(HOME, ".todos");
|
|
10260
10470
|
if (!existsSync6(newDir) && existsSync6(legacyDir))
|
|
10261
10471
|
return legacyDir;
|
|
10262
10472
|
return newDir;
|
|
10263
10473
|
}
|
|
10264
10474
|
function agentBaseDir(agent) {
|
|
10265
10475
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
10266
|
-
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] ||
|
|
10476
|
+
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join7(getTodosGlobalDir2(), "agents");
|
|
10267
10477
|
}
|
|
10268
10478
|
function getTaskListDir2(agent, taskListId) {
|
|
10269
|
-
return
|
|
10479
|
+
return join7(agentBaseDir(agent), agent, taskListId);
|
|
10270
10480
|
}
|
|
10271
10481
|
function readAgentTask(dir, filename) {
|
|
10272
|
-
return readJsonFile(
|
|
10482
|
+
return readJsonFile(join7(dir, filename));
|
|
10273
10483
|
}
|
|
10274
10484
|
function writeAgentTask(dir, task) {
|
|
10275
|
-
writeJsonFile(
|
|
10485
|
+
writeJsonFile(join7(dir, `${task.id}.json`), task);
|
|
10276
10486
|
}
|
|
10277
10487
|
function taskToAgentTask(task, externalId, existingMeta) {
|
|
10278
10488
|
return {
|
|
@@ -10306,7 +10516,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
10306
10516
|
const existingByTodosId = new Map;
|
|
10307
10517
|
const files = listJsonFiles(dir);
|
|
10308
10518
|
for (const f of files) {
|
|
10309
|
-
const path =
|
|
10519
|
+
const path = join7(dir, f);
|
|
10310
10520
|
const at = readAgentTask(dir, f);
|
|
10311
10521
|
if (at?.metadata?.["todos_id"]) {
|
|
10312
10522
|
existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -10399,7 +10609,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
10399
10609
|
}
|
|
10400
10610
|
for (const f of files) {
|
|
10401
10611
|
try {
|
|
10402
|
-
const filePath =
|
|
10612
|
+
const filePath = join7(dir, f);
|
|
10403
10613
|
const at = readAgentTask(dir, f);
|
|
10404
10614
|
if (!at)
|
|
10405
10615
|
continue;
|
|
@@ -10810,7 +11020,7 @@ __export(exports_extract, {
|
|
|
10810
11020
|
EXTRACT_TAGS: () => EXTRACT_TAGS
|
|
10811
11021
|
});
|
|
10812
11022
|
import { readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
10813
|
-
import { relative as relative3, resolve as resolve7, join as
|
|
11023
|
+
import { relative as relative3, resolve as resolve7, join as join8 } from "path";
|
|
10814
11024
|
function tagToPriority(tag) {
|
|
10815
11025
|
switch (tag) {
|
|
10816
11026
|
case "BUG":
|
|
@@ -10882,7 +11092,7 @@ function extractTodos(options, db) {
|
|
|
10882
11092
|
const files = collectFiles(basePath, extensions);
|
|
10883
11093
|
const allComments = [];
|
|
10884
11094
|
for (const file of files) {
|
|
10885
|
-
const fullPath = statSync3(basePath).isFile() ? basePath :
|
|
11095
|
+
const fullPath = statSync3(basePath).isFile() ? basePath : join8(basePath, file);
|
|
10886
11096
|
try {
|
|
10887
11097
|
const source = readFileSync5(fullPath, "utf-8");
|
|
10888
11098
|
const relPath = statSync3(basePath).isFile() ? relative3(resolve7(basePath, ".."), fullPath) : file;
|
|
@@ -11009,8 +11219,8 @@ var init_extract = __esm(() => {
|
|
|
11009
11219
|
|
|
11010
11220
|
// src/lib/artifact-store.ts
|
|
11011
11221
|
import { createHash as createHash2 } from "crypto";
|
|
11012
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
11013
|
-
import { dirname as dirname6, join as
|
|
11222
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
11223
|
+
import { dirname as dirname6, join as join9, resolve as resolve8 } from "path";
|
|
11014
11224
|
import { tmpdir } from "os";
|
|
11015
11225
|
function isInMemoryDb2(path) {
|
|
11016
11226
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
@@ -11022,15 +11232,15 @@ function artifactStoreRoot() {
|
|
|
11022
11232
|
return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
11023
11233
|
const dbPath = getDatabasePath();
|
|
11024
11234
|
if (isInMemoryDb2(dbPath))
|
|
11025
|
-
return
|
|
11026
|
-
return
|
|
11235
|
+
return join9(tmpdir(), "hasna-todos-artifacts");
|
|
11236
|
+
return join9(dirname6(resolve8(dbPath)), "artifacts");
|
|
11027
11237
|
}
|
|
11028
11238
|
function artifactStorePath(relativePath) {
|
|
11029
11239
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
11030
11240
|
if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
|
|
11031
11241
|
throw new Error("Invalid artifact store path");
|
|
11032
11242
|
}
|
|
11033
|
-
return
|
|
11243
|
+
return join9(artifactStoreRoot(), normalized);
|
|
11034
11244
|
}
|
|
11035
11245
|
function sha256(buffer) {
|
|
11036
11246
|
return createHash2("sha256").update(buffer).digest("hex");
|
|
@@ -11088,11 +11298,11 @@ function storeArtifactContent(input) {
|
|
|
11088
11298
|
redactionStatus = "redacted";
|
|
11089
11299
|
}
|
|
11090
11300
|
const storedSha = sha256(storedBuffer);
|
|
11091
|
-
const relativePath =
|
|
11301
|
+
const relativePath = join9("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
|
|
11092
11302
|
const destination = artifactStorePath(relativePath);
|
|
11093
11303
|
if (!existsSync8(destination)) {
|
|
11094
|
-
|
|
11095
|
-
|
|
11304
|
+
mkdirSync5(dirname6(destination), { recursive: true });
|
|
11305
|
+
writeFileSync4(destination, storedBuffer);
|
|
11096
11306
|
}
|
|
11097
11307
|
const createdAt = input.created_at || new Date().toISOString();
|
|
11098
11308
|
const retentionDays = input.retention_days ?? null;
|
|
@@ -11209,8 +11419,8 @@ function importStoredArtifactContent(content) {
|
|
|
11209
11419
|
};
|
|
11210
11420
|
}
|
|
11211
11421
|
const destination = artifactStorePath(content.relative_path);
|
|
11212
|
-
|
|
11213
|
-
|
|
11422
|
+
mkdirSync5(dirname6(destination), { recursive: true });
|
|
11423
|
+
writeFileSync4(destination, buffer);
|
|
11214
11424
|
return {
|
|
11215
11425
|
id: content.artifact_id,
|
|
11216
11426
|
path: content.relative_path,
|
|
@@ -13032,8 +13242,8 @@ function registerProjectCommands(program2) {
|
|
|
13032
13242
|
const projectId = autoProject(globalOpts);
|
|
13033
13243
|
const writeOutput = async (content) => {
|
|
13034
13244
|
if (opts.output) {
|
|
13035
|
-
const { writeFileSync:
|
|
13036
|
-
|
|
13245
|
+
const { writeFileSync: writeFileSync5 } = await import("fs");
|
|
13246
|
+
writeFileSync5(resolve9(opts.output), content.endsWith(`
|
|
13037
13247
|
`) ? content : `${content}
|
|
13038
13248
|
`);
|
|
13039
13249
|
} else {
|
|
@@ -14822,8 +15032,8 @@ var exports_doctor = {};
|
|
|
14822
15032
|
__export(exports_doctor, {
|
|
14823
15033
|
runTodosDoctor: () => runTodosDoctor
|
|
14824
15034
|
});
|
|
14825
|
-
import { chmodSync, copyFileSync, existsSync as existsSync9, mkdirSync as
|
|
14826
|
-
import { basename as basename4, dirname as dirname7, join as
|
|
15035
|
+
import { chmodSync, copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync6, statSync as statSync5 } from "fs";
|
|
15036
|
+
import { basename as basename4, dirname as dirname7, join as join10 } from "path";
|
|
14827
15037
|
function tableExists(db, table) {
|
|
14828
15038
|
return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
|
|
14829
15039
|
}
|
|
@@ -14980,13 +15190,13 @@ function createBackup(dbPath) {
|
|
|
14980
15190
|
if (!existsSync9(dbPath))
|
|
14981
15191
|
return;
|
|
14982
15192
|
const stamp = now().replace(/[:.]/g, "-");
|
|
14983
|
-
const backupDir =
|
|
15193
|
+
const backupDir = join10(dirname7(dbPath), `${basename4(dbPath)}.backup-${stamp}`);
|
|
14984
15194
|
const files = [];
|
|
14985
|
-
|
|
15195
|
+
mkdirSync6(backupDir, { recursive: true });
|
|
14986
15196
|
for (const source of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
14987
15197
|
if (!existsSync9(source))
|
|
14988
15198
|
continue;
|
|
14989
|
-
const target =
|
|
15199
|
+
const target = join10(backupDir, basename4(source));
|
|
14990
15200
|
copyFileSync(source, target);
|
|
14991
15201
|
files.push(target);
|
|
14992
15202
|
}
|
|
@@ -15212,7 +15422,7 @@ var init_doctor = __esm(() => {
|
|
|
15212
15422
|
});
|
|
15213
15423
|
|
|
15214
15424
|
// src/server/routes.ts
|
|
15215
|
-
import { join as
|
|
15425
|
+
import { join as join11, resolve as resolve11, sep } from "path";
|
|
15216
15426
|
function parseFieldsParam(url) {
|
|
15217
15427
|
const fieldsParam = url.searchParams.get("fields");
|
|
15218
15428
|
return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
|
|
@@ -15912,7 +16122,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
|
|
|
15912
16122
|
if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
|
|
15913
16123
|
return null;
|
|
15914
16124
|
if (path !== "/") {
|
|
15915
|
-
const filePath =
|
|
16125
|
+
const filePath = join11(ctx.dashboardDir, path);
|
|
15916
16126
|
const resolvedFile = resolve11(filePath);
|
|
15917
16127
|
const resolvedBase = resolve11(ctx.dashboardDir);
|
|
15918
16128
|
if (!resolvedFile.startsWith(resolvedBase + sep) && resolvedFile !== resolvedBase) {
|
|
@@ -15922,7 +16132,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
|
|
|
15922
16132
|
if (res2)
|
|
15923
16133
|
return res2;
|
|
15924
16134
|
}
|
|
15925
|
-
const indexPath =
|
|
16135
|
+
const indexPath = join11(ctx.dashboardDir, "index.html");
|
|
15926
16136
|
const res = serveStaticFile2(indexPath);
|
|
15927
16137
|
if (res)
|
|
15928
16138
|
return res;
|
|
@@ -15952,26 +16162,26 @@ __export(exports_serve, {
|
|
|
15952
16162
|
MIME_TYPES: () => MIME_TYPES
|
|
15953
16163
|
});
|
|
15954
16164
|
import { existsSync as existsSync10 } from "fs";
|
|
15955
|
-
import { join as
|
|
16165
|
+
import { join as join12, dirname as dirname8, extname } from "path";
|
|
15956
16166
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
15957
16167
|
function resolveDashboardDir() {
|
|
15958
16168
|
const candidates = [];
|
|
15959
16169
|
try {
|
|
15960
16170
|
const scriptDir = dirname8(fileURLToPath2(import.meta.url));
|
|
15961
|
-
candidates.push(
|
|
15962
|
-
candidates.push(
|
|
16171
|
+
candidates.push(join12(scriptDir, "..", "dashboard", "dist"));
|
|
16172
|
+
candidates.push(join12(scriptDir, "..", "..", "dashboard", "dist"));
|
|
15963
16173
|
} catch {}
|
|
15964
16174
|
if (process.argv[1]) {
|
|
15965
16175
|
const mainDir = dirname8(process.argv[1]);
|
|
15966
|
-
candidates.push(
|
|
15967
|
-
candidates.push(
|
|
16176
|
+
candidates.push(join12(mainDir, "..", "dashboard", "dist"));
|
|
16177
|
+
candidates.push(join12(mainDir, "..", "..", "dashboard", "dist"));
|
|
15968
16178
|
}
|
|
15969
|
-
candidates.push(
|
|
16179
|
+
candidates.push(join12(process.cwd(), "dashboard", "dist"));
|
|
15970
16180
|
for (const candidate of candidates) {
|
|
15971
16181
|
if (existsSync10(candidate))
|
|
15972
16182
|
return candidate;
|
|
15973
16183
|
}
|
|
15974
|
-
return
|
|
16184
|
+
return join12(process.cwd(), "dashboard", "dist");
|
|
15975
16185
|
}
|
|
15976
16186
|
function getProvidedApiKey(req) {
|
|
15977
16187
|
const headerKey = req.headers.get("x-api-key");
|
|
@@ -17747,14 +17957,14 @@ __export(exports_config_serve_commands, {
|
|
|
17747
17957
|
registerConfigServeCommands: () => registerConfigServeCommands
|
|
17748
17958
|
});
|
|
17749
17959
|
import chalk6 from "chalk";
|
|
17750
|
-
import { existsSync as existsSync11, mkdirSync as
|
|
17751
|
-
import { dirname as dirname9, join as
|
|
17960
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
17961
|
+
import { dirname as dirname9, join as join13 } from "path";
|
|
17752
17962
|
function registerConfigServeCommands(program2) {
|
|
17753
17963
|
program2.command("config").description("View or update configuration").option("--get <key>", "Get a config value").option("--set <key=value>", "Set a config value (e.g. completion_guard.enabled=true)").action((opts) => {
|
|
17754
17964
|
const globalOpts = program2.opts();
|
|
17755
17965
|
const home = process.env["HOME"] || "~";
|
|
17756
|
-
const newPath =
|
|
17757
|
-
const legacyPath =
|
|
17966
|
+
const newPath = join13(home, ".hasna", "todos", "config.json");
|
|
17967
|
+
const legacyPath = join13(home, ".todos", "config.json");
|
|
17758
17968
|
const configPath = !existsSync11(newPath) && existsSync11(legacyPath) ? legacyPath : newPath;
|
|
17759
17969
|
if (opts.get) {
|
|
17760
17970
|
const config2 = loadConfig();
|
|
@@ -17793,8 +18003,8 @@ function registerConfigServeCommands(program2) {
|
|
|
17793
18003
|
obj[keys[keys.length - 1]] = parsedValue;
|
|
17794
18004
|
const dir = dirname9(configPath);
|
|
17795
18005
|
if (!existsSync11(dir))
|
|
17796
|
-
|
|
17797
|
-
|
|
18006
|
+
mkdirSync7(dir, { recursive: true });
|
|
18007
|
+
writeFileSync5(configPath, JSON.stringify(config2, null, 2));
|
|
17798
18008
|
if (globalOpts.json) {
|
|
17799
18009
|
output({ key, value: parsedValue }, true);
|
|
17800
18010
|
} else {
|
|
@@ -20605,8 +20815,8 @@ Repairs`));
|
|
|
20605
20815
|
const db = getDatabase();
|
|
20606
20816
|
const row = db.query("SELECT COUNT(*) as count FROM tasks").get();
|
|
20607
20817
|
const { statSync: statSync6 } = await import("fs");
|
|
20608
|
-
const { join:
|
|
20609
|
-
const dbPath = process.env["TODOS_DB_PATH"] ||
|
|
20818
|
+
const { join: join14 } = await import("path");
|
|
20819
|
+
const dbPath = process.env["TODOS_DB_PATH"] || join14(process.env["HOME"] || "~", ".todos", "todos.db");
|
|
20610
20820
|
let size = "unknown";
|
|
20611
20821
|
try {
|
|
20612
20822
|
size = `${(statSync6(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
|
|
@@ -26525,10 +26735,12 @@ var init_token_utils = __esm(() => {
|
|
|
26525
26735
|
"export_template",
|
|
26526
26736
|
"import_template",
|
|
26527
26737
|
"init_templates",
|
|
26738
|
+
"list_template_library",
|
|
26528
26739
|
"list_templates",
|
|
26529
26740
|
"preview_template",
|
|
26530
26741
|
"template_history",
|
|
26531
|
-
"update_template"
|
|
26742
|
+
"update_template",
|
|
26743
|
+
"write_template_library"
|
|
26532
26744
|
],
|
|
26533
26745
|
webhooks: ["create_webhook", "delete_webhook", "list_webhooks"],
|
|
26534
26746
|
machines: [
|
|
@@ -32670,6 +32882,259 @@ var init_agents2 = __esm(() => {
|
|
|
32670
32882
|
init_database();
|
|
32671
32883
|
});
|
|
32672
32884
|
|
|
32885
|
+
// src/mcp/tools/templates.ts
|
|
32886
|
+
function registerTemplateTools(server, { shouldRegisterTool, resolveId, formatError }) {
|
|
32887
|
+
if (shouldRegisterTool("create_template")) {
|
|
32888
|
+
server.tool("create_template", "Create a reusable task template. Optionally include a tasks array to define a multi-task template with dependencies and variable placeholders ({name} syntax). Use variables to define typed variable definitions with defaults and required flags.", {
|
|
32889
|
+
name: exports_external.string(),
|
|
32890
|
+
title_pattern: exports_external.string(),
|
|
32891
|
+
description: exports_external.string().optional(),
|
|
32892
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
32893
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
32894
|
+
project_id: exports_external.string().optional(),
|
|
32895
|
+
plan_id: exports_external.string().optional(),
|
|
32896
|
+
variables: exports_external.array(exports_external.object({
|
|
32897
|
+
name: exports_external.string().describe("Variable name (used as {name} in patterns)"),
|
|
32898
|
+
required: exports_external.boolean().describe("Whether this variable must be provided"),
|
|
32899
|
+
default: exports_external.string().optional().describe("Default value if not provided"),
|
|
32900
|
+
description: exports_external.string().optional().describe("Help text for the variable")
|
|
32901
|
+
})).optional().describe("Typed variable definitions with defaults and required flags"),
|
|
32902
|
+
tasks: exports_external.array(exports_external.object({
|
|
32903
|
+
title_pattern: exports_external.string().describe("Title pattern with optional {variable} placeholders"),
|
|
32904
|
+
description: exports_external.string().optional(),
|
|
32905
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
32906
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
32907
|
+
task_type: exports_external.string().optional(),
|
|
32908
|
+
depends_on: exports_external.array(exports_external.number()).optional().describe("Position indices (0-based) of tasks this task depends on"),
|
|
32909
|
+
metadata: exports_external.record(exports_external.unknown()).optional()
|
|
32910
|
+
})).optional().describe("Multi-task template: ordered list of tasks to create together with dependencies")
|
|
32911
|
+
}, async (params) => {
|
|
32912
|
+
try {
|
|
32913
|
+
const { createTemplate: createTemplate2, getTemplateWithTasks: getTemplateWithTasks2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
32914
|
+
const t = createTemplate2(params);
|
|
32915
|
+
const withTasks = getTemplateWithTasks2(t.id);
|
|
32916
|
+
const taskCount = withTasks?.tasks.length ?? 0;
|
|
32917
|
+
const taskInfo = taskCount > 0 ? ` | ${taskCount} task(s)` : "";
|
|
32918
|
+
return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"${taskInfo}` }] };
|
|
32919
|
+
} catch (e) {
|
|
32920
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
32921
|
+
}
|
|
32922
|
+
});
|
|
32923
|
+
}
|
|
32924
|
+
if (shouldRegisterTool("list_templates")) {
|
|
32925
|
+
server.tool("list_templates", "List all task templates", {}, async () => {
|
|
32926
|
+
try {
|
|
32927
|
+
const { listTemplates: listTemplates2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
32928
|
+
const templates = listTemplates2();
|
|
32929
|
+
if (templates.length === 0)
|
|
32930
|
+
return { content: [{ type: "text", text: "No templates." }] };
|
|
32931
|
+
const text = templates.map((t) => {
|
|
32932
|
+
const vars = t.variables.length > 0 ? ` | vars: ${t.variables.map((v) => `${v.name}${v.required ? "*" : ""}${v.default ? `=${v.default}` : ""}`).join(", ")}` : "";
|
|
32933
|
+
return `${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}${vars}`;
|
|
32934
|
+
}).join(`
|
|
32935
|
+
`);
|
|
32936
|
+
return { content: [{ type: "text", text: `${templates.length} template(s):
|
|
32937
|
+
${text}` }] };
|
|
32938
|
+
} catch (e) {
|
|
32939
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
32940
|
+
}
|
|
32941
|
+
});
|
|
32942
|
+
}
|
|
32943
|
+
if (shouldRegisterTool("create_task_from_template")) {
|
|
32944
|
+
server.tool("create_task_from_template", "Create task(s) from a template. For multi-task templates, creates all tasks with dependencies wired. Supports {variable} substitution in titles/descriptions.", {
|
|
32945
|
+
template_id: exports_external.string(),
|
|
32946
|
+
title: exports_external.string().optional().describe("Override title (single-task templates only)"),
|
|
32947
|
+
description: exports_external.string().optional().describe("Override description (single-task templates only)"),
|
|
32948
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
32949
|
+
assigned_to: exports_external.string().optional(),
|
|
32950
|
+
project_id: exports_external.string().optional(),
|
|
32951
|
+
task_list_id: exports_external.string().optional(),
|
|
32952
|
+
variables: exports_external.record(exports_external.string()).optional().describe("Variable substitution map for {name} placeholders in multi-task templates")
|
|
32953
|
+
}, async (params) => {
|
|
32954
|
+
try {
|
|
32955
|
+
const { taskFromTemplate: taskFromTemplate2, getTemplateWithTasks: getTemplateWithTasks2, tasksFromTemplate: tasksFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
32956
|
+
const resolvedTemplateId = resolveId(params.template_id, "task_templates");
|
|
32957
|
+
const resolvedProjectId = params.project_id ? resolveId(params.project_id, "projects") : undefined;
|
|
32958
|
+
const resolvedTaskListId = params.task_list_id ? resolveId(params.task_list_id, "task_lists") : undefined;
|
|
32959
|
+
const templateWithTasks = getTemplateWithTasks2(resolvedTemplateId);
|
|
32960
|
+
if (templateWithTasks && templateWithTasks.tasks.length > 0) {
|
|
32961
|
+
const effectiveProjectId = resolvedProjectId || templateWithTasks.project_id || undefined;
|
|
32962
|
+
const tasks = tasksFromTemplate2(resolvedTemplateId, effectiveProjectId, params.variables, resolvedTaskListId);
|
|
32963
|
+
const text = tasks.map((t) => `${t.id.slice(0, 8)} | ${t.priority} | ${t.title}`).join(`
|
|
32964
|
+
`);
|
|
32965
|
+
return { content: [{ type: "text", text: `${tasks.length} task(s) created from template:
|
|
32966
|
+
${text}` }] };
|
|
32967
|
+
}
|
|
32968
|
+
const input = taskFromTemplate2(resolvedTemplateId, {
|
|
32969
|
+
title: params.title,
|
|
32970
|
+
description: params.description,
|
|
32971
|
+
priority: params.priority,
|
|
32972
|
+
assigned_to: params.assigned_to,
|
|
32973
|
+
project_id: resolvedProjectId,
|
|
32974
|
+
task_list_id: resolvedTaskListId
|
|
32975
|
+
});
|
|
32976
|
+
const task = createTask(input);
|
|
32977
|
+
return { content: [{ type: "text", text: `Task created from template:
|
|
32978
|
+
${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
|
|
32979
|
+
} catch (e) {
|
|
32980
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
32981
|
+
}
|
|
32982
|
+
});
|
|
32983
|
+
}
|
|
32984
|
+
if (shouldRegisterTool("delete_template")) {
|
|
32985
|
+
server.tool("delete_template", "Delete a task template by ID.", { id: exports_external.string() }, async ({ id }) => {
|
|
32986
|
+
try {
|
|
32987
|
+
const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
32988
|
+
const resolvedId = resolveId(id, "task_templates");
|
|
32989
|
+
const deleted = deleteTemplate2(resolvedId);
|
|
32990
|
+
return { content: [{ type: "text", text: deleted ? "Template deleted." : "Template not found." }] };
|
|
32991
|
+
} catch (e) {
|
|
32992
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
32993
|
+
}
|
|
32994
|
+
});
|
|
32995
|
+
}
|
|
32996
|
+
if (shouldRegisterTool("update_template")) {
|
|
32997
|
+
server.tool("update_template", "Update a task template's name, title pattern, description, priority, tags, or other fields.", {
|
|
32998
|
+
id: exports_external.string(),
|
|
32999
|
+
name: exports_external.string().optional(),
|
|
33000
|
+
title_pattern: exports_external.string().optional(),
|
|
33001
|
+
description: exports_external.string().optional(),
|
|
33002
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional(),
|
|
33003
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
33004
|
+
project_id: exports_external.string().optional(),
|
|
33005
|
+
plan_id: exports_external.string().optional()
|
|
33006
|
+
}, async ({ id, ...updates }) => {
|
|
33007
|
+
try {
|
|
33008
|
+
const { updateTemplate: updateTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
33009
|
+
const resolvedId = resolveId(id, "task_templates");
|
|
33010
|
+
const t = updateTemplate2(resolvedId, updates);
|
|
33011
|
+
if (!t)
|
|
33012
|
+
return { content: [{ type: "text", text: `Template not found: ${id}` }], isError: true };
|
|
33013
|
+
return { content: [{ type: "text", text: `Template updated: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}` }] };
|
|
33014
|
+
} catch (e) {
|
|
33015
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
33016
|
+
}
|
|
33017
|
+
});
|
|
33018
|
+
}
|
|
33019
|
+
if (shouldRegisterTool("init_templates")) {
|
|
33020
|
+
server.tool("init_templates", "Initialize the bundled local template library. Skips templates that already exist by name.", {}, async () => {
|
|
33021
|
+
try {
|
|
33022
|
+
const { initBuiltinTemplates: initBuiltinTemplates2 } = await Promise.resolve().then(() => (init_builtin_templates(), exports_builtin_templates));
|
|
33023
|
+
const result = initBuiltinTemplates2();
|
|
33024
|
+
if (result.created === 0)
|
|
33025
|
+
return { content: [{ type: "text", text: `All ${result.skipped} built-in template(s) already exist.` }] };
|
|
33026
|
+
return { content: [{ type: "text", text: `Created ${result.created} template(s): ${result.names.join(", ")}. Skipped ${result.skipped} existing.` }] };
|
|
33027
|
+
} catch (e) {
|
|
33028
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
33029
|
+
}
|
|
33030
|
+
});
|
|
33031
|
+
}
|
|
33032
|
+
if (shouldRegisterTool("list_template_library")) {
|
|
33033
|
+
server.tool("list_template_library", "List the bundled marketplace-free local template library without mutating the database.", {}, async () => {
|
|
33034
|
+
try {
|
|
33035
|
+
const { listBuiltinTemplates: listBuiltinTemplates2 } = await Promise.resolve().then(() => (init_builtin_templates(), exports_builtin_templates));
|
|
33036
|
+
const templates = listBuiltinTemplates2().map((template) => ({
|
|
33037
|
+
name: template.name,
|
|
33038
|
+
description: template.description,
|
|
33039
|
+
category: template.category,
|
|
33040
|
+
version: template.version,
|
|
33041
|
+
variables: template.variables,
|
|
33042
|
+
task_count: template.tasks.length
|
|
33043
|
+
}));
|
|
33044
|
+
return { content: [{ type: "text", text: JSON.stringify(templates, null, 2) }] };
|
|
33045
|
+
} catch (e) {
|
|
33046
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
33047
|
+
}
|
|
33048
|
+
});
|
|
33049
|
+
}
|
|
33050
|
+
if (shouldRegisterTool("write_template_library")) {
|
|
33051
|
+
server.tool("write_template_library", "Write the bundled local template library to editable JSON files for review or import.", { directory: exports_external.string() }, async ({ directory }) => {
|
|
33052
|
+
try {
|
|
33053
|
+
const { writeBuiltinTemplateFiles: writeBuiltinTemplateFiles2 } = await Promise.resolve().then(() => (init_builtin_templates(), exports_builtin_templates));
|
|
33054
|
+
return { content: [{ type: "text", text: JSON.stringify(writeBuiltinTemplateFiles2(directory), null, 2) }] };
|
|
33055
|
+
} catch (e) {
|
|
33056
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
33057
|
+
}
|
|
33058
|
+
});
|
|
33059
|
+
}
|
|
33060
|
+
if (shouldRegisterTool("preview_template")) {
|
|
33061
|
+
server.tool("preview_template", "Preview a template without creating tasks. Shows resolved titles (variables substituted), dependencies, and priorities.", {
|
|
33062
|
+
template_id: exports_external.string(),
|
|
33063
|
+
variables: exports_external.record(exports_external.string()).optional().describe("Variable substitution map for {name} placeholders")
|
|
33064
|
+
}, async (params) => {
|
|
33065
|
+
try {
|
|
33066
|
+
const { previewTemplate: previewTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
33067
|
+
const resolvedId = resolveId(params.template_id, "task_templates");
|
|
33068
|
+
const preview = previewTemplate2(resolvedId, params.variables);
|
|
33069
|
+
const lines = preview.tasks.map((t) => {
|
|
33070
|
+
const deps = t.depends_on_positions.length > 0 ? ` (after: ${t.depends_on_positions.join(", ")})` : "";
|
|
33071
|
+
return ` [${t.position}] ${t.priority} | ${t.title}${deps}`;
|
|
33072
|
+
});
|
|
33073
|
+
const varsInfo = preview.variables.length > 0 ? `
|
|
33074
|
+
Variables: ${preview.variables.map((v) => `${v.name}${v.required ? "*" : ""}${v.default ? `=${v.default}` : ""}`).join(", ")}` : "";
|
|
33075
|
+
const resolvedVars = Object.keys(preview.resolved_variables).length > 0 ? `
|
|
33076
|
+
Resolved: ${Object.entries(preview.resolved_variables).map(([k, v]) => `${k}=${v}`).join(", ")}` : "";
|
|
33077
|
+
return { content: [{ type: "text", text: `Preview: ${preview.template_name} (${preview.tasks.length} tasks)${varsInfo}${resolvedVars}
|
|
33078
|
+
${lines.join(`
|
|
33079
|
+
`)}` }] };
|
|
33080
|
+
} catch (e) {
|
|
33081
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
33082
|
+
}
|
|
33083
|
+
});
|
|
33084
|
+
}
|
|
33085
|
+
if (shouldRegisterTool("export_template")) {
|
|
33086
|
+
server.tool("export_template", "Export a template as a full JSON object (template + tasks + variables). Useful for sharing or backup.", { template_id: exports_external.string() }, async ({ template_id }) => {
|
|
33087
|
+
try {
|
|
33088
|
+
const { exportTemplate: exportTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
33089
|
+
const resolvedId = resolveId(template_id, "task_templates");
|
|
33090
|
+
const json2 = exportTemplate2(resolvedId);
|
|
33091
|
+
return { content: [{ type: "text", text: JSON.stringify(json2, null, 2) }] };
|
|
33092
|
+
} catch (e) {
|
|
33093
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
33094
|
+
}
|
|
33095
|
+
});
|
|
33096
|
+
}
|
|
33097
|
+
if (shouldRegisterTool("import_template")) {
|
|
33098
|
+
server.tool("import_template", "Import a template from a JSON string (as returned by export_template). Creates new template with new IDs.", { json: exports_external.string().describe("JSON string of the template export") }, async ({ json: json2 }) => {
|
|
33099
|
+
try {
|
|
33100
|
+
const { importTemplate: importTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
33101
|
+
const parsed = JSON.parse(json2);
|
|
33102
|
+
const t = importTemplate2(parsed);
|
|
33103
|
+
return { content: [{ type: "text", text: `Template imported: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"` }] };
|
|
33104
|
+
} catch (e) {
|
|
33105
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
33106
|
+
}
|
|
33107
|
+
});
|
|
33108
|
+
}
|
|
33109
|
+
if (shouldRegisterTool("template_history")) {
|
|
33110
|
+
server.tool("template_history", "Show version history of a template. Each update creates a snapshot of the previous state.", { template_id: exports_external.string() }, async ({ template_id }) => {
|
|
33111
|
+
try {
|
|
33112
|
+
const { listTemplateVersions: listTemplateVersions2, getTemplate: getTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
33113
|
+
const resolvedId = resolveId(template_id, "task_templates");
|
|
33114
|
+
const template = getTemplate2(resolvedId);
|
|
33115
|
+
if (!template)
|
|
33116
|
+
return { content: [{ type: "text", text: `Template not found: ${template_id}` }], isError: true };
|
|
33117
|
+
const versions = listTemplateVersions2(resolvedId);
|
|
33118
|
+
if (versions.length === 0)
|
|
33119
|
+
return { content: [{ type: "text", text: `${template.name} v${template.version} \u2014 no previous versions.` }] };
|
|
33120
|
+
const lines = versions.map((v) => {
|
|
33121
|
+
const snap = JSON.parse(v.snapshot);
|
|
33122
|
+
return `v${v.version} | ${v.created_at} | ${snap.name} | "${snap.title_pattern}"`;
|
|
33123
|
+
});
|
|
33124
|
+
return { content: [{ type: "text", text: `${template.name} \u2014 current: v${template.version}
|
|
33125
|
+
${lines.join(`
|
|
33126
|
+
`)}` }] };
|
|
33127
|
+
} catch (e) {
|
|
33128
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
33129
|
+
}
|
|
33130
|
+
});
|
|
33131
|
+
}
|
|
33132
|
+
}
|
|
33133
|
+
var init_templates2 = __esm(() => {
|
|
33134
|
+
init_zod();
|
|
33135
|
+
init_tasks();
|
|
33136
|
+
});
|
|
33137
|
+
|
|
32673
33138
|
// src/mcp/index.ts
|
|
32674
33139
|
var exports_mcp = {};
|
|
32675
33140
|
__export(exports_mcp, {
|
|
@@ -32838,6 +33303,7 @@ var init_mcp = __esm(() => {
|
|
|
32838
33303
|
init_code_tools();
|
|
32839
33304
|
init_machines2();
|
|
32840
33305
|
init_agents2();
|
|
33306
|
+
init_templates2();
|
|
32841
33307
|
init_package_version();
|
|
32842
33308
|
init_token_utils();
|
|
32843
33309
|
if (hasVersionFlag()) {
|
|
@@ -32868,6 +33334,7 @@ var init_mcp = __esm(() => {
|
|
|
32868
33334
|
registerTaskRelTools(server, toolContext);
|
|
32869
33335
|
registerCodeTools(server, toolContext);
|
|
32870
33336
|
registerAgentTools(server, { ...toolContext, agentFocusMap });
|
|
33337
|
+
registerTemplateTools(server, toolContext);
|
|
32871
33338
|
registerMachineTools(server, { shouldRegisterTool, formatError });
|
|
32872
33339
|
registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
|
|
32873
33340
|
main().catch(async (err) => {
|
|
@@ -32887,15 +33354,15 @@ __export(exports_mcp_hooks_commands, {
|
|
|
32887
33354
|
});
|
|
32888
33355
|
import chalk8 from "chalk";
|
|
32889
33356
|
import { execSync as execSync3 } from "child_process";
|
|
32890
|
-
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as
|
|
32891
|
-
import { dirname as dirname10, join as
|
|
33357
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync8, chmodSync as chmodSync2 } from "fs";
|
|
33358
|
+
import { dirname as dirname10, join as join14 } from "path";
|
|
32892
33359
|
function getMcpBinaryPath() {
|
|
32893
33360
|
try {
|
|
32894
33361
|
const p = execSync3("which todos-mcp", { encoding: "utf-8" }).trim();
|
|
32895
33362
|
if (p)
|
|
32896
33363
|
return p;
|
|
32897
33364
|
} catch {}
|
|
32898
|
-
const bunBin =
|
|
33365
|
+
const bunBin = join14(HOME2, ".bun", "bin", "todos-mcp");
|
|
32899
33366
|
if (existsSync13(bunBin))
|
|
32900
33367
|
return bunBin;
|
|
32901
33368
|
return "todos-mcp";
|
|
@@ -32912,8 +33379,8 @@ function readJsonFile2(path) {
|
|
|
32912
33379
|
function writeJsonFile2(path, data) {
|
|
32913
33380
|
const dir = dirname10(path);
|
|
32914
33381
|
if (!existsSync13(dir))
|
|
32915
|
-
|
|
32916
|
-
|
|
33382
|
+
mkdirSync8(dir, { recursive: true });
|
|
33383
|
+
writeFileSync6(path, JSON.stringify(data, null, 2) + `
|
|
32917
33384
|
`);
|
|
32918
33385
|
}
|
|
32919
33386
|
function readTomlFile(path) {
|
|
@@ -32924,8 +33391,8 @@ function readTomlFile(path) {
|
|
|
32924
33391
|
function writeTomlFile(path, content) {
|
|
32925
33392
|
const dir = dirname10(path);
|
|
32926
33393
|
if (!existsSync13(dir))
|
|
32927
|
-
|
|
32928
|
-
|
|
33394
|
+
mkdirSync8(dir, { recursive: true });
|
|
33395
|
+
writeFileSync6(path, content);
|
|
32929
33396
|
}
|
|
32930
33397
|
function removeTomlBlock(content, blockName) {
|
|
32931
33398
|
const lines = content.split(`
|
|
@@ -32989,7 +33456,7 @@ function unregisterClaude(_global) {
|
|
|
32989
33456
|
}
|
|
32990
33457
|
}
|
|
32991
33458
|
function registerCodex(binPath) {
|
|
32992
|
-
const configPath =
|
|
33459
|
+
const configPath = join14(HOME2, ".codex", "config.toml");
|
|
32993
33460
|
let content = readTomlFile(configPath);
|
|
32994
33461
|
content = removeTomlBlock(content, "mcp_servers.todos");
|
|
32995
33462
|
const block = `
|
|
@@ -33003,7 +33470,7 @@ args = []
|
|
|
33003
33470
|
console.log(chalk8.green(`Codex CLI: registered in ${configPath}`));
|
|
33004
33471
|
}
|
|
33005
33472
|
function unregisterCodex() {
|
|
33006
|
-
const configPath =
|
|
33473
|
+
const configPath = join14(HOME2, ".codex", "config.toml");
|
|
33007
33474
|
let content = readTomlFile(configPath);
|
|
33008
33475
|
if (!content.includes("[mcp_servers.todos]")) {
|
|
33009
33476
|
console.log(chalk8.dim(`Codex CLI: todos not found in ${configPath}`));
|
|
@@ -33015,7 +33482,7 @@ function unregisterCodex() {
|
|
|
33015
33482
|
console.log(chalk8.green(`Codex CLI: unregistered from ${configPath}`));
|
|
33016
33483
|
}
|
|
33017
33484
|
function registerGemini(binPath) {
|
|
33018
|
-
const configPath =
|
|
33485
|
+
const configPath = join14(HOME2, ".gemini", "settings.json");
|
|
33019
33486
|
const config = readJsonFile2(configPath);
|
|
33020
33487
|
if (!config["mcpServers"]) {
|
|
33021
33488
|
config["mcpServers"] = {};
|
|
@@ -33029,7 +33496,7 @@ function registerGemini(binPath) {
|
|
|
33029
33496
|
console.log(chalk8.green(`Gemini CLI: registered in ${configPath}`));
|
|
33030
33497
|
}
|
|
33031
33498
|
function unregisterGemini() {
|
|
33032
|
-
const configPath =
|
|
33499
|
+
const configPath = join14(HOME2, ".gemini", "settings.json");
|
|
33033
33500
|
const config = readJsonFile2(configPath);
|
|
33034
33501
|
const servers = config["mcpServers"];
|
|
33035
33502
|
if (!servers || !("todos" in servers)) {
|
|
@@ -33086,9 +33553,9 @@ function registerMcpHooksCommands(program2) {
|
|
|
33086
33553
|
if (p)
|
|
33087
33554
|
todosBin = p;
|
|
33088
33555
|
} catch {}
|
|
33089
|
-
const hooksDir =
|
|
33556
|
+
const hooksDir = join14(process.cwd(), ".claude", "hooks");
|
|
33090
33557
|
if (!existsSync13(hooksDir))
|
|
33091
|
-
|
|
33558
|
+
mkdirSync8(hooksDir, { recursive: true });
|
|
33092
33559
|
const hookScript = `#!/usr/bin/env bash
|
|
33093
33560
|
# Auto-generated by: todos hooks install
|
|
33094
33561
|
# Syncs todos with Claude Code task list on tool use events.
|
|
@@ -33112,11 +33579,11 @@ esac
|
|
|
33112
33579
|
|
|
33113
33580
|
exit 0
|
|
33114
33581
|
`;
|
|
33115
|
-
const hookPath =
|
|
33116
|
-
|
|
33582
|
+
const hookPath = join14(hooksDir, "todos-sync.sh");
|
|
33583
|
+
writeFileSync6(hookPath, hookScript);
|
|
33117
33584
|
execSync3(`chmod +x "${hookPath}"`);
|
|
33118
33585
|
console.log(chalk8.green(`Hook script created: ${hookPath}`));
|
|
33119
|
-
const settingsPath =
|
|
33586
|
+
const settingsPath = join14(process.cwd(), ".claude", "settings.json");
|
|
33120
33587
|
const settings = readJsonFile2(settingsPath);
|
|
33121
33588
|
if (!settings["hooks"]) {
|
|
33122
33589
|
settings["hooks"] = {};
|
|
@@ -33865,12 +34332,12 @@ Artifacts:`));
|
|
|
33865
34332
|
console.log(chalk8.yellow("Hook already installed."));
|
|
33866
34333
|
return;
|
|
33867
34334
|
}
|
|
33868
|
-
|
|
34335
|
+
writeFileSync6(hookPath, existing + `
|
|
33869
34336
|
${marker}
|
|
33870
34337
|
$(dirname "$0")/../../scripts/post-commit-hook.sh
|
|
33871
34338
|
`);
|
|
33872
34339
|
} else {
|
|
33873
|
-
|
|
34340
|
+
writeFileSync6(hookPath, `#!/usr/bin/env bash
|
|
33874
34341
|
${marker}
|
|
33875
34342
|
$(dirname "$0")/../../scripts/post-commit-hook.sh
|
|
33876
34343
|
`);
|
|
@@ -33902,7 +34369,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
|
|
|
33902
34369
|
if (cleaned === "#!/usr/bin/env bash" || cleaned === "") {
|
|
33903
34370
|
(await import("fs")).unlinkSync(hookPath);
|
|
33904
34371
|
} else {
|
|
33905
|
-
|
|
34372
|
+
writeFileSync6(hookPath, cleaned + `
|
|
33906
34373
|
`);
|
|
33907
34374
|
}
|
|
33908
34375
|
console.log(chalk8.green("Post-commit hook removed."));
|
|
@@ -34067,9 +34534,9 @@ __export(exports_machines2, {
|
|
|
34067
34534
|
});
|
|
34068
34535
|
import chalk10 from "chalk";
|
|
34069
34536
|
import { execSync as execSync4 } from "child_process";
|
|
34070
|
-
import { writeFileSync as
|
|
34537
|
+
import { writeFileSync as writeFileSync7 } from "fs";
|
|
34071
34538
|
import { tmpdir as tmpdir2 } from "os";
|
|
34072
|
-
import { join as
|
|
34539
|
+
import { join as join15 } from "path";
|
|
34073
34540
|
function getOrCreateLocalMachineName() {
|
|
34074
34541
|
return process.env["TODOS_MACHINE_NAME"] || __require("os").hostname() || "unknown";
|
|
34075
34542
|
}
|
|
@@ -34263,8 +34730,8 @@ Warning: No primary machine set.`));
|
|
|
34263
34730
|
if (opts.push) {
|
|
34264
34731
|
try {
|
|
34265
34732
|
const localTasks = listTasks3();
|
|
34266
|
-
const tmpFile =
|
|
34267
|
-
|
|
34733
|
+
const tmpFile = join15(tmpdir2(), `todos-export-${uuid()}.json`);
|
|
34734
|
+
writeFileSync7(tmpFile, JSON.stringify(localTasks, null, 2));
|
|
34268
34735
|
execSync4(`scp ${tmpFile} ${ssh}:/tmp/todos-import.json`, { timeout: 15000 });
|
|
34269
34736
|
const importCmd = `ssh ${ssh} 'node -e "const fs=require(\\'fs\\');const tasks=JSON.parse(fs.readFileSync(\\'/tmp/todos-import.json\\',\\'utf-8\\'));console.log(JSON.stringify(tasks.length))"'`;
|
|
34270
34737
|
const count = execSync4(importCmd, { encoding: "utf-8", timeout: 1e4 }).trim();
|