@hasna/todos 0.11.73 → 0.11.74
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/dist/cli/index.js
CHANGED
|
@@ -12874,6 +12874,7 @@ __export(exports_task_routing, {
|
|
|
12874
12874
|
setTaskWorkflowPointers: () => setTaskWorkflowPointers,
|
|
12875
12875
|
getTaskRouteState: () => getTaskRouteState
|
|
12876
12876
|
});
|
|
12877
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
12877
12878
|
function machineLocalPath(project, db) {
|
|
12878
12879
|
const machineId = process.env["TODOS_MACHINE_ID"];
|
|
12879
12880
|
if (!machineId)
|
|
@@ -12911,7 +12912,33 @@ function routeConcurrencyKey(task, project, taskList, projectPath) {
|
|
|
12911
12912
|
return `path:${projectPath}`;
|
|
12912
12913
|
return `task:${task.id}`;
|
|
12913
12914
|
}
|
|
12914
|
-
function
|
|
12915
|
+
function directoryExists(path) {
|
|
12916
|
+
try {
|
|
12917
|
+
return existsSync8(path) && statSync3(path).isDirectory();
|
|
12918
|
+
} catch {
|
|
12919
|
+
return false;
|
|
12920
|
+
}
|
|
12921
|
+
}
|
|
12922
|
+
function classifyRoute(input) {
|
|
12923
|
+
if (input.terminal)
|
|
12924
|
+
return "terminal";
|
|
12925
|
+
if (input.notPending)
|
|
12926
|
+
return "in_progress";
|
|
12927
|
+
if (input.blocked)
|
|
12928
|
+
return "blocked";
|
|
12929
|
+
if (input.locked)
|
|
12930
|
+
return "locked";
|
|
12931
|
+
if (input.missingProjectRoot)
|
|
12932
|
+
return "missing_metadata";
|
|
12933
|
+
if (!input.eligible)
|
|
12934
|
+
return "unroutable";
|
|
12935
|
+
if (input.workflowPointerActive)
|
|
12936
|
+
return "deduped_active";
|
|
12937
|
+
if (input.workflowPointerTerminal)
|
|
12938
|
+
return "terminal_requeue_needed";
|
|
12939
|
+
return "eligible";
|
|
12940
|
+
}
|
|
12941
|
+
function getTaskRouteState(taskOrId, db, options = {}) {
|
|
12915
12942
|
const d = db || getDatabase();
|
|
12916
12943
|
const task = typeof taskOrId === "string" ? getTask(taskOrId, d) : taskOrId;
|
|
12917
12944
|
if (!task)
|
|
@@ -12919,8 +12946,10 @@ function getTaskRouteState(taskOrId, db) {
|
|
|
12919
12946
|
const { project, projectPath } = resolveProject(task, d);
|
|
12920
12947
|
const taskList = resolveTaskList(task, project, d);
|
|
12921
12948
|
const automation = routingAutomationMetadata(task, taskList) ?? {};
|
|
12922
|
-
const
|
|
12949
|
+
const explicitRouteEnabled = routeEnabledForTask(task, taskList);
|
|
12923
12950
|
const tagOptIn = task.tags.includes("auto:route") || task.tags.includes("route:enabled");
|
|
12951
|
+
const routeEnabled = explicitRouteEnabled === undefined ? tagOptIn : explicitRouteEnabled;
|
|
12952
|
+
const tagNoAuto = task.tags.includes("no-auto") || task.tags.includes("noauto") || task.tags.includes("no:auto");
|
|
12924
12953
|
const projectKind = projectKindFromMetadata(task.metadata, taskList?.metadata);
|
|
12925
12954
|
const locked = Boolean(task.locked_by && !isLockExpired(task.locked_at));
|
|
12926
12955
|
const blockers = getBlockingDeps(task.id, d);
|
|
@@ -12929,10 +12958,21 @@ function getTaskRouteState(taskOrId, db) {
|
|
|
12929
12958
|
const requiresApproval = automation.requires_approval === true || task.requires_approval === true;
|
|
12930
12959
|
const approvalRequired = automation.approval_required === true;
|
|
12931
12960
|
const approved = Boolean(task.approved_by);
|
|
12961
|
+
const pointers = workflowPointersFromMetadata(task.metadata);
|
|
12962
|
+
const workflowState = (pointers.workflow_state ?? "").trim().toLowerCase();
|
|
12963
|
+
const hasWorkflowPointer = Boolean(pointers.current_workflow_invocation_id || pointers.current_run_id || workflowState);
|
|
12964
|
+
const workflowPointerTerminal = hasWorkflowPointer && TERMINAL_WORKFLOW_STATES.has(workflowState);
|
|
12965
|
+
const workflowPointerActive = hasWorkflowPointer && !workflowPointerTerminal;
|
|
12966
|
+
const verifyProjectRoot = options.verifyProjectRoot === true;
|
|
12967
|
+
let projectRootExists = null;
|
|
12968
|
+
if (verifyProjectRoot) {
|
|
12969
|
+
projectRootExists = projectPath ? directoryExists(projectPath) : false;
|
|
12970
|
+
}
|
|
12971
|
+
const missingProjectRoot = verifyProjectRoot && projectRootExists !== true;
|
|
12932
12972
|
const gates = {
|
|
12933
12973
|
route_enabled: routeEnabled,
|
|
12934
12974
|
tag_opt_in: tagOptIn,
|
|
12935
|
-
no_auto: automation.no_auto === true,
|
|
12975
|
+
no_auto: automation.no_auto === true || tagNoAuto,
|
|
12936
12976
|
manual: automation.manual === true,
|
|
12937
12977
|
manual_required: automation.manual_required === true,
|
|
12938
12978
|
requires_approval: requiresApproval,
|
|
@@ -12940,7 +12980,10 @@ function getTaskRouteState(taskOrId, db) {
|
|
|
12940
12980
|
approved,
|
|
12941
12981
|
locked,
|
|
12942
12982
|
blocked,
|
|
12943
|
-
terminal
|
|
12983
|
+
terminal,
|
|
12984
|
+
missing_project_root: missingProjectRoot,
|
|
12985
|
+
workflow_pointer_active: workflowPointerActive,
|
|
12986
|
+
workflow_pointer_terminal: workflowPointerTerminal
|
|
12944
12987
|
};
|
|
12945
12988
|
const reasons = [];
|
|
12946
12989
|
if (task.status !== "pending")
|
|
@@ -12965,12 +13008,44 @@ function getTaskRouteState(taskOrId, db) {
|
|
|
12965
13008
|
reasons.push("approval_required");
|
|
12966
13009
|
if (automation.allowed === false)
|
|
12967
13010
|
reasons.push("automation_disallowed");
|
|
13011
|
+
if (missingProjectRoot)
|
|
13012
|
+
reasons.push("missing_project_root");
|
|
13013
|
+
const eligible = reasons.length === 0;
|
|
13014
|
+
const staleAfterMs = options.staleInProgressAfterMs ?? DEFAULT_STALE_IN_PROGRESS_MS;
|
|
13015
|
+
const updatedAtMs = Date.parse(task.updated_at);
|
|
13016
|
+
const ageMs = Number.isNaN(updatedAtMs) ? 0 : Math.max(0, Date.now() - updatedAtMs);
|
|
13017
|
+
const evidence = {
|
|
13018
|
+
owner: task.assigned_to ?? task.locked_by ?? null,
|
|
13019
|
+
assigned_to: task.assigned_to ?? null,
|
|
13020
|
+
locked_by: task.locked_by ?? null,
|
|
13021
|
+
locked_at: task.locked_at ?? null,
|
|
13022
|
+
updated_at: task.updated_at,
|
|
13023
|
+
age_ms: ageMs,
|
|
13024
|
+
stale: task.status === "in_progress" && ageMs > staleAfterMs,
|
|
13025
|
+
stale_after_ms: staleAfterMs,
|
|
13026
|
+
current_run_id: pointers.current_run_id ?? null,
|
|
13027
|
+
current_workflow_invocation_id: pointers.current_workflow_invocation_id ?? null,
|
|
13028
|
+
workflow_state: pointers.workflow_state ?? null,
|
|
13029
|
+
project_root_verified: verifyProjectRoot,
|
|
13030
|
+
project_root_exists: projectRootExists
|
|
13031
|
+
};
|
|
13032
|
+
const route_class = classifyRoute({
|
|
13033
|
+
terminal,
|
|
13034
|
+
notPending: task.status !== "pending",
|
|
13035
|
+
blocked,
|
|
13036
|
+
locked,
|
|
13037
|
+
missingProjectRoot,
|
|
13038
|
+
eligible,
|
|
13039
|
+
workflowPointerActive,
|
|
13040
|
+
workflowPointerTerminal
|
|
13041
|
+
});
|
|
12968
13042
|
return {
|
|
12969
13043
|
schema_version: TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION,
|
|
12970
13044
|
task_id: task.id,
|
|
12971
13045
|
task_short_id: task.short_id,
|
|
12972
13046
|
status: task.status,
|
|
12973
|
-
eligible
|
|
13047
|
+
eligible,
|
|
13048
|
+
route_class,
|
|
12974
13049
|
reasons,
|
|
12975
13050
|
blockers: blockers.map((blocker) => ({
|
|
12976
13051
|
id: blocker.id,
|
|
@@ -12990,7 +13065,8 @@ function getTaskRouteState(taskOrId, db) {
|
|
|
12990
13065
|
task_list_name: taskList?.name ?? null,
|
|
12991
13066
|
concurrency_key: routeConcurrencyKey(task, project, taskList, projectPath)
|
|
12992
13067
|
},
|
|
12993
|
-
pointers
|
|
13068
|
+
pointers,
|
|
13069
|
+
evidence
|
|
12994
13070
|
};
|
|
12995
13071
|
}
|
|
12996
13072
|
function setTaskWorkflowPointers(taskId, input, db) {
|
|
@@ -13036,12 +13112,28 @@ function pointerPatch(previous, input, key) {
|
|
|
13036
13112
|
return previous;
|
|
13037
13113
|
return typeof value === "string" && value.trim() ? value : undefined;
|
|
13038
13114
|
}
|
|
13115
|
+
var DEFAULT_STALE_IN_PROGRESS_MS, TERMINAL_WORKFLOW_STATES;
|
|
13039
13116
|
var init_task_routing = __esm(() => {
|
|
13040
13117
|
init_database();
|
|
13041
13118
|
init_projects();
|
|
13042
13119
|
init_task_crud();
|
|
13043
13120
|
init_task_lifecycle();
|
|
13044
13121
|
init_task_lists();
|
|
13122
|
+
DEFAULT_STALE_IN_PROGRESS_MS = 3 * 24 * 60 * 60 * 1000;
|
|
13123
|
+
TERMINAL_WORKFLOW_STATES = new Set([
|
|
13124
|
+
"failed",
|
|
13125
|
+
"cancelled",
|
|
13126
|
+
"canceled",
|
|
13127
|
+
"completed",
|
|
13128
|
+
"complete",
|
|
13129
|
+
"done",
|
|
13130
|
+
"error",
|
|
13131
|
+
"errored",
|
|
13132
|
+
"timeout",
|
|
13133
|
+
"timed_out",
|
|
13134
|
+
"aborted",
|
|
13135
|
+
"superseded"
|
|
13136
|
+
]);
|
|
13045
13137
|
});
|
|
13046
13138
|
|
|
13047
13139
|
// src/cli/commands/task-commands.ts
|
|
@@ -13277,13 +13369,13 @@ function registerTaskCommands(program2) {
|
|
|
13277
13369
|
console.log(formatTaskLine(result.task));
|
|
13278
13370
|
}
|
|
13279
13371
|
});
|
|
13280
|
-
task.command("route-state <id>").description("Show deterministic routing eligibility and workflow pointers for a task").action(async (id) => {
|
|
13372
|
+
task.command("route-state <id>").description("Show deterministic routing eligibility and workflow pointers for a task").option("--verify-project-root", "Filesystem-check the resolved project root and surface missing_project_root before admission").action(async (id, opts) => {
|
|
13281
13373
|
const globalOpts = program2.opts();
|
|
13282
13374
|
const resolvedId = resolveTaskId(id);
|
|
13283
13375
|
const { getTaskRouteState: getTaskRouteState2 } = await Promise.resolve().then(() => (init_task_routing(), exports_task_routing));
|
|
13284
13376
|
let state;
|
|
13285
13377
|
try {
|
|
13286
|
-
state = getTaskRouteState2(resolvedId);
|
|
13378
|
+
state = getTaskRouteState2(resolvedId, undefined, { verifyProjectRoot: Boolean(opts.verifyProjectRoot) });
|
|
13287
13379
|
} catch (e) {
|
|
13288
13380
|
handleError(e);
|
|
13289
13381
|
}
|
|
@@ -13294,8 +13386,12 @@ function registerTaskCommands(program2) {
|
|
|
13294
13386
|
console.log(chalk2.bold("Task route state"));
|
|
13295
13387
|
console.log(` ${chalk2.dim("Task:")} ${state.task_short_id || state.task_id.slice(0, 8)}`);
|
|
13296
13388
|
console.log(` ${chalk2.dim("Eligible:")} ${state.eligible ? chalk2.green("yes") : chalk2.yellow("no")}`);
|
|
13389
|
+
console.log(` ${chalk2.dim("Class:")} ${state.route_class}`);
|
|
13297
13390
|
console.log(` ${chalk2.dim("Reasons:")} ${state.reasons.length > 0 ? state.reasons.join(", ") : "none"}`);
|
|
13298
13391
|
console.log(` ${chalk2.dim("Route:")} ${state.route.concurrency_key}`);
|
|
13392
|
+
if (state.evidence.owner) {
|
|
13393
|
+
console.log(` ${chalk2.dim("Owner:")} ${state.evidence.owner}${state.evidence.stale ? chalk2.yellow(" (stale)") : ""}`);
|
|
13394
|
+
}
|
|
13299
13395
|
if (state.pointers.current_workflow_invocation_id) {
|
|
13300
13396
|
console.log(` ${chalk2.dim("Invocation:")} ${state.pointers.current_workflow_invocation_id}`);
|
|
13301
13397
|
}
|
|
@@ -13978,7 +14074,7 @@ var init_task_commands = __esm(() => {
|
|
|
13978
14074
|
});
|
|
13979
14075
|
|
|
13980
14076
|
// src/lib/plan-artifacts.ts
|
|
13981
|
-
import { existsSync as
|
|
14077
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
13982
14078
|
import { join as join7, resolve as resolve10 } from "path";
|
|
13983
14079
|
function assertSafePathSegment(value, label) {
|
|
13984
14080
|
const trimmed = value.trim();
|
|
@@ -14214,7 +14310,7 @@ function readPlanArtifact(plan, db) {
|
|
|
14214
14310
|
return null;
|
|
14215
14311
|
const d = db || getDatabase();
|
|
14216
14312
|
const paths = resolvePlanArtifactCandidatePaths(plan, d);
|
|
14217
|
-
const path =
|
|
14313
|
+
const path = existsSync9(paths.primary.file_path) ? paths.primary.file_path : existsSync9(paths.legacy.file_path) ? paths.legacy.file_path : null;
|
|
14218
14314
|
if (!path)
|
|
14219
14315
|
return null;
|
|
14220
14316
|
const markdown = readFileSync4(path, "utf8");
|
|
@@ -14229,7 +14325,7 @@ function inspectPlanArtifact(plan, db) {
|
|
|
14229
14325
|
return null;
|
|
14230
14326
|
const d = db || getDatabase();
|
|
14231
14327
|
const paths = resolvePlanArtifactCandidatePaths(plan, d);
|
|
14232
|
-
const path =
|
|
14328
|
+
const path = existsSync9(paths.primary.file_path) ? paths.primary.file_path : existsSync9(paths.legacy.file_path) ? paths.legacy.file_path : null;
|
|
14233
14329
|
if (!path) {
|
|
14234
14330
|
return {
|
|
14235
14331
|
path: paths.primary.file_path,
|
|
@@ -15705,7 +15801,7 @@ var init_saved_search_views = __esm(() => {
|
|
|
15705
15801
|
});
|
|
15706
15802
|
|
|
15707
15803
|
// src/lib/claude-tasks.ts
|
|
15708
|
-
import { existsSync as
|
|
15804
|
+
import { existsSync as existsSync10, readFileSync as readFileSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
15709
15805
|
import { join as join9 } from "path";
|
|
15710
15806
|
function getTaskListDir(taskListId) {
|
|
15711
15807
|
return join9(HOME, ".claude", "tasks", taskListId);
|
|
@@ -15727,7 +15823,7 @@ function toSqliteStatus(status) {
|
|
|
15727
15823
|
}
|
|
15728
15824
|
function readPrefixCounter(dir) {
|
|
15729
15825
|
const path = join9(dir, ".prefix-counter");
|
|
15730
|
-
if (!
|
|
15826
|
+
if (!existsSync10(path))
|
|
15731
15827
|
return 0;
|
|
15732
15828
|
const val = parseInt(readFileSync5(path, "utf-8").trim(), 10);
|
|
15733
15829
|
return isNaN(val) ? 0 : val;
|
|
@@ -15760,7 +15856,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
|
|
|
15760
15856
|
}
|
|
15761
15857
|
function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
15762
15858
|
const dir = getTaskListDir(taskListId);
|
|
15763
|
-
if (!
|
|
15859
|
+
if (!existsSync10(dir))
|
|
15764
15860
|
ensureDir2(dir);
|
|
15765
15861
|
const filter = {};
|
|
15766
15862
|
if (projectId)
|
|
@@ -15856,7 +15952,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
15856
15952
|
}
|
|
15857
15953
|
function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
15858
15954
|
const dir = getTaskListDir(taskListId);
|
|
15859
|
-
if (!
|
|
15955
|
+
if (!existsSync10(dir)) {
|
|
15860
15956
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
15861
15957
|
}
|
|
15862
15958
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
|
|
@@ -15949,7 +16045,7 @@ var init_claude_tasks = __esm(() => {
|
|
|
15949
16045
|
});
|
|
15950
16046
|
|
|
15951
16047
|
// src/lib/agent-tasks.ts
|
|
15952
|
-
import { existsSync as
|
|
16048
|
+
import { existsSync as existsSync11 } from "fs";
|
|
15953
16049
|
import { join as join10 } from "path";
|
|
15954
16050
|
function agentBaseDir(agent) {
|
|
15955
16051
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
@@ -15987,7 +16083,7 @@ function metadataKey(agent) {
|
|
|
15987
16083
|
}
|
|
15988
16084
|
function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
15989
16085
|
const dir = getTaskListDir2(agent, taskListId);
|
|
15990
|
-
if (!
|
|
16086
|
+
if (!existsSync11(dir))
|
|
15991
16087
|
ensureDir2(dir);
|
|
15992
16088
|
const filter = {};
|
|
15993
16089
|
if (projectId)
|
|
@@ -16070,7 +16166,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
16070
16166
|
}
|
|
16071
16167
|
function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
16072
16168
|
const dir = getTaskListDir2(agent, taskListId);
|
|
16073
|
-
if (!
|
|
16169
|
+
if (!existsSync11(dir)) {
|
|
16074
16170
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
16075
16171
|
}
|
|
16076
16172
|
const files = listJsonFiles(dir);
|
|
@@ -16242,11 +16338,11 @@ __export(exports_project_bootstrap, {
|
|
|
16242
16338
|
discoverProjectWorkspace: () => discoverProjectWorkspace,
|
|
16243
16339
|
bootstrapProject: () => bootstrapProject
|
|
16244
16340
|
});
|
|
16245
|
-
import { existsSync as
|
|
16341
|
+
import { existsSync as existsSync12, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
16246
16342
|
import { basename as basename4, dirname as dirname6, resolve as resolve11 } from "path";
|
|
16247
16343
|
function safeStat(path) {
|
|
16248
16344
|
try {
|
|
16249
|
-
return
|
|
16345
|
+
return statSync4(path);
|
|
16250
16346
|
} catch {
|
|
16251
16347
|
return null;
|
|
16252
16348
|
}
|
|
@@ -16261,7 +16357,7 @@ function canonicalPath(input) {
|
|
|
16261
16357
|
function findUp(start, marker) {
|
|
16262
16358
|
let current = canonicalPath(start);
|
|
16263
16359
|
while (true) {
|
|
16264
|
-
if (
|
|
16360
|
+
if (existsSync12(resolve11(current, marker)))
|
|
16265
16361
|
return current;
|
|
16266
16362
|
const parent = dirname6(current);
|
|
16267
16363
|
if (parent === current)
|
|
@@ -16273,7 +16369,7 @@ function readPackageJson(path) {
|
|
|
16273
16369
|
if (!path)
|
|
16274
16370
|
return null;
|
|
16275
16371
|
const file = resolve11(path, "package.json");
|
|
16276
|
-
if (!
|
|
16372
|
+
if (!existsSync12(file))
|
|
16277
16373
|
return null;
|
|
16278
16374
|
try {
|
|
16279
16375
|
const parsed = JSON.parse(readFileSync6(file, "utf-8"));
|
|
@@ -16295,7 +16391,7 @@ function workspaceMarker(root, rootPackage) {
|
|
|
16295
16391
|
if (rootPackage?.workspaces)
|
|
16296
16392
|
markers.push("package.json#workspaces");
|
|
16297
16393
|
for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
|
|
16298
|
-
if (
|
|
16394
|
+
if (existsSync12(resolve11(root, marker)))
|
|
16299
16395
|
markers.push(marker);
|
|
16300
16396
|
}
|
|
16301
16397
|
const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
|
|
@@ -21818,7 +21914,7 @@ __export(exports_extract, {
|
|
|
21818
21914
|
buildCodebaseIndex: () => buildCodebaseIndex,
|
|
21819
21915
|
EXTRACT_TAGS: () => EXTRACT_TAGS
|
|
21820
21916
|
});
|
|
21821
|
-
import { existsSync as
|
|
21917
|
+
import { existsSync as existsSync13, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
21822
21918
|
import { createHash as createHash3 } from "crypto";
|
|
21823
21919
|
import { relative as relative3, resolve as resolve12, join as join11 } from "path";
|
|
21824
21920
|
function stableHash(value) {
|
|
@@ -21828,9 +21924,9 @@ function normalizePathForMatch(value) {
|
|
|
21828
21924
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
21829
21925
|
}
|
|
21830
21926
|
function readGitignorePatterns(basePath) {
|
|
21831
|
-
const root =
|
|
21927
|
+
const root = statSync5(basePath).isFile() ? resolve12(basePath, "..") : basePath;
|
|
21832
21928
|
const gitignorePath = join11(root, ".gitignore");
|
|
21833
|
-
if (!
|
|
21929
|
+
if (!existsSync13(gitignorePath))
|
|
21834
21930
|
return [];
|
|
21835
21931
|
try {
|
|
21836
21932
|
return readFileSync7(gitignorePath, "utf-8").split(`
|
|
@@ -21939,7 +22035,7 @@ function extractFromSource(source, filePath, tags = [...EXTRACT_TAGS]) {
|
|
|
21939
22035
|
return results;
|
|
21940
22036
|
}
|
|
21941
22037
|
function collectFiles(basePath, extensions, excludes, respectGitignore) {
|
|
21942
|
-
const stat =
|
|
22038
|
+
const stat = statSync5(basePath);
|
|
21943
22039
|
if (stat.isFile()) {
|
|
21944
22040
|
return [basePath];
|
|
21945
22041
|
}
|
|
@@ -21972,10 +22068,10 @@ function buildCodebaseIndex(options) {
|
|
|
21972
22068
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
21973
22069
|
const indexed = [];
|
|
21974
22070
|
for (const file of files) {
|
|
21975
|
-
const fullPath =
|
|
22071
|
+
const fullPath = statSync5(basePath).isFile() ? basePath : join11(basePath, file);
|
|
21976
22072
|
try {
|
|
21977
22073
|
const source = readFileSync7(fullPath, "utf-8");
|
|
21978
|
-
const relPath =
|
|
22074
|
+
const relPath = statSync5(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
|
|
21979
22075
|
indexed.push({
|
|
21980
22076
|
file: relPath,
|
|
21981
22077
|
checksum: stableHash(source).slice(0, 24),
|
|
@@ -22003,10 +22099,10 @@ function extractTodos(options, db) {
|
|
|
22003
22099
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
22004
22100
|
const allComments = [];
|
|
22005
22101
|
for (const file of files) {
|
|
22006
|
-
const fullPath =
|
|
22102
|
+
const fullPath = statSync5(basePath).isFile() ? basePath : join11(basePath, file);
|
|
22007
22103
|
try {
|
|
22008
22104
|
const source = readFileSync7(fullPath, "utf-8");
|
|
22009
|
-
const relPath =
|
|
22105
|
+
const relPath = statSync5(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
|
|
22010
22106
|
const comments = extractFromSource(source, relPath, tags);
|
|
22011
22107
|
allComments.push(...comments);
|
|
22012
22108
|
} catch {}
|
|
@@ -25133,7 +25229,7 @@ __export(exports_retention_cleanup, {
|
|
|
25133
25229
|
applyRetentionCleanup: () => applyRetentionCleanup,
|
|
25134
25230
|
RETENTION_CLEANUP_CONFIRMATION: () => RETENTION_CLEANUP_CONFIRMATION
|
|
25135
25231
|
});
|
|
25136
|
-
import { existsSync as
|
|
25232
|
+
import { existsSync as existsSync14, unlinkSync } from "fs";
|
|
25137
25233
|
function normalizeScopes(scopes) {
|
|
25138
25234
|
if (!scopes || scopes.length === 0)
|
|
25139
25235
|
return [...ALL_SCOPES];
|
|
@@ -25336,7 +25432,7 @@ function applyRetentionCleanup(input, db) {
|
|
|
25336
25432
|
for (const artifact of report.candidates.artifact_files) {
|
|
25337
25433
|
try {
|
|
25338
25434
|
const path = artifactStorePath(artifact.relative_path);
|
|
25339
|
-
if (!
|
|
25435
|
+
if (!existsSync14(path)) {
|
|
25340
25436
|
report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
|
|
25341
25437
|
continue;
|
|
25342
25438
|
}
|
|
@@ -26012,7 +26108,7 @@ __export(exports_local_extensions, {
|
|
|
26012
26108
|
discoverLocalExtensions: () => discoverLocalExtensions
|
|
26013
26109
|
});
|
|
26014
26110
|
import { createHash as createHash5, createVerify } from "crypto";
|
|
26015
|
-
import { existsSync as
|
|
26111
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
26016
26112
|
import { basename as basename6, join as join12, resolve as resolve14 } from "path";
|
|
26017
26113
|
function isObject(value) {
|
|
26018
26114
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -26272,10 +26368,10 @@ function verifyExtensionSignature(input) {
|
|
|
26272
26368
|
}
|
|
26273
26369
|
function inspectExtensionSource(source2) {
|
|
26274
26370
|
const resolved = resolve14(source2);
|
|
26275
|
-
if (!
|
|
26371
|
+
if (!existsSync15(resolved))
|
|
26276
26372
|
throw new Error(`extension source not found: ${source2}`);
|
|
26277
|
-
const stat =
|
|
26278
|
-
const manifestPath = stat.isDirectory() ? [join12(resolved, "todos.extension.json"), join12(resolved, "extension.json")].find(
|
|
26373
|
+
const stat = statSync6(resolved);
|
|
26374
|
+
const manifestPath = stat.isDirectory() ? [join12(resolved, "todos.extension.json"), join12(resolved, "extension.json")].find(existsSync15) : resolved;
|
|
26279
26375
|
if (!manifestPath)
|
|
26280
26376
|
throw new Error(`extension directory ${source2} is missing todos.extension.json`);
|
|
26281
26377
|
const raw = readFileSync8(manifestPath);
|
|
@@ -26375,16 +26471,16 @@ function projectExtensionSources(projectPath) {
|
|
|
26375
26471
|
join12(root, ".todos", "todos.extension.json")
|
|
26376
26472
|
];
|
|
26377
26473
|
const extensionDir = join12(root, ".todos", "extensions");
|
|
26378
|
-
if (
|
|
26474
|
+
if (existsSync15(extensionDir)) {
|
|
26379
26475
|
for (const entry of readdirSync3(extensionDir)) {
|
|
26380
26476
|
if (entry.startsWith("."))
|
|
26381
26477
|
continue;
|
|
26382
26478
|
const full = join12(extensionDir, entry);
|
|
26383
|
-
if (
|
|
26479
|
+
if (statSync6(full).isDirectory() || entry.endsWith(".json"))
|
|
26384
26480
|
candidates.push(full);
|
|
26385
26481
|
}
|
|
26386
26482
|
}
|
|
26387
|
-
return candidates.filter(
|
|
26483
|
+
return candidates.filter(existsSync15);
|
|
26388
26484
|
}
|
|
26389
26485
|
function discoverLocalExtensions(options = {}) {
|
|
26390
26486
|
const config = loadConfig();
|
|
@@ -27684,7 +27780,7 @@ var exports_doctor = {};
|
|
|
27684
27780
|
__export(exports_doctor, {
|
|
27685
27781
|
runTodosDoctor: () => runTodosDoctor
|
|
27686
27782
|
});
|
|
27687
|
-
import { chmodSync, copyFileSync, existsSync as
|
|
27783
|
+
import { chmodSync, copyFileSync, existsSync as existsSync16, mkdirSync as mkdirSync7, statSync as statSync7 } from "fs";
|
|
27688
27784
|
import { basename as basename7, dirname as dirname7, join as join13 } from "path";
|
|
27689
27785
|
function tableExists(db, table) {
|
|
27690
27786
|
return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
|
|
@@ -27779,7 +27875,7 @@ function findMissingProjectRoots(db) {
|
|
|
27779
27875
|
continue;
|
|
27780
27876
|
if (!row.path.startsWith("/"))
|
|
27781
27877
|
continue;
|
|
27782
|
-
if (!
|
|
27878
|
+
if (!existsSync16(row.path))
|
|
27783
27879
|
missing++;
|
|
27784
27880
|
}
|
|
27785
27881
|
return missing;
|
|
@@ -27831,7 +27927,7 @@ function databasePermissionsAreUnsafe(dbPath) {
|
|
|
27831
27927
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
27832
27928
|
return false;
|
|
27833
27929
|
try {
|
|
27834
|
-
return (
|
|
27930
|
+
return (statSync7(dbPath).mode & 63) !== 0;
|
|
27835
27931
|
} catch {
|
|
27836
27932
|
return false;
|
|
27837
27933
|
}
|
|
@@ -27839,14 +27935,14 @@ function databasePermissionsAreUnsafe(dbPath) {
|
|
|
27839
27935
|
function createBackup(dbPath) {
|
|
27840
27936
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
27841
27937
|
return;
|
|
27842
|
-
if (!
|
|
27938
|
+
if (!existsSync16(dbPath))
|
|
27843
27939
|
return;
|
|
27844
27940
|
const stamp = now().replace(/[:.]/g, "-");
|
|
27845
27941
|
const backupDir = join13(dirname7(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
|
|
27846
27942
|
const files = [];
|
|
27847
27943
|
mkdirSync7(backupDir, { recursive: true });
|
|
27848
27944
|
for (const source2 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
27849
|
-
if (!
|
|
27945
|
+
if (!existsSync16(source2))
|
|
27850
27946
|
continue;
|
|
27851
27947
|
const target = join13(backupDir, basename7(source2));
|
|
27852
27948
|
copyFileSync(source2, target);
|
|
@@ -33872,7 +33968,7 @@ var exports_mention_resolver = {};
|
|
|
33872
33968
|
__export(exports_mention_resolver, {
|
|
33873
33969
|
resolveMentions: () => resolveMentions
|
|
33874
33970
|
});
|
|
33875
|
-
import { existsSync as
|
|
33971
|
+
import { existsSync as existsSync17, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync8 } from "fs";
|
|
33876
33972
|
import { basename as basename8, isAbsolute, join as join15, relative as relative5, resolve as resolve17, sep as sep3 } from "path";
|
|
33877
33973
|
function blankResolution(parsed) {
|
|
33878
33974
|
return {
|
|
@@ -33971,11 +34067,11 @@ function resolveFile(parsed, workspace) {
|
|
|
33971
34067
|
return resolution;
|
|
33972
34068
|
}
|
|
33973
34069
|
resolution.path = relPath;
|
|
33974
|
-
if (!
|
|
34070
|
+
if (!existsSync17(absolutePath)) {
|
|
33975
34071
|
resolution.warnings.push("file does not exist in the local workspace");
|
|
33976
34072
|
return resolution;
|
|
33977
34073
|
}
|
|
33978
|
-
const stats =
|
|
34074
|
+
const stats = statSync8(absolutePath);
|
|
33979
34075
|
if (!stats.isFile()) {
|
|
33980
34076
|
resolution.warnings.push("path exists but is not a file");
|
|
33981
34077
|
return resolution;
|
|
@@ -34013,7 +34109,7 @@ function walkSourceFiles(root, current = root, files = []) {
|
|
|
34013
34109
|
if (!entry.isFile())
|
|
34014
34110
|
continue;
|
|
34015
34111
|
const extension = `.${basename8(entry.name).split(".").pop() || ""}`;
|
|
34016
|
-
if (SOURCE_EXTENSIONS.has(extension) &&
|
|
34112
|
+
if (SOURCE_EXTENSIONS.has(extension) && statSync8(absolutePath).size <= 512 * 1024) {
|
|
34017
34113
|
files.push(absolutePath);
|
|
34018
34114
|
}
|
|
34019
34115
|
}
|
|
@@ -42461,7 +42557,7 @@ __export(exports_verification_providers, {
|
|
|
42461
42557
|
getVerificationRecord: () => getVerificationRecord,
|
|
42462
42558
|
discoverVerificationProviderCapabilities: () => discoverVerificationProviderCapabilities
|
|
42463
42559
|
});
|
|
42464
|
-
import { existsSync as
|
|
42560
|
+
import { existsSync as existsSync18, readFileSync as readFileSync11 } from "fs";
|
|
42465
42561
|
function normalizeName6(name) {
|
|
42466
42562
|
const normalized = name.trim().toLowerCase();
|
|
42467
42563
|
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
|
|
@@ -42613,7 +42709,7 @@ Timed out after ${provider.timeout_ms}ms`);
|
|
|
42613
42709
|
};
|
|
42614
42710
|
}
|
|
42615
42711
|
function runCiLogProvider(input) {
|
|
42616
|
-
const text = input.log_text ?? (input.log_path &&
|
|
42712
|
+
const text = input.log_text ?? (input.log_path && existsSync18(input.log_path) ? readFileSync11(input.log_path, "utf-8") : "");
|
|
42617
42713
|
return {
|
|
42618
42714
|
status: classifyLog(text),
|
|
42619
42715
|
attempts: 1,
|
|
@@ -42625,7 +42721,7 @@ function runBrowserProvider(input) {
|
|
|
42625
42721
|
if (!input.artifact_path) {
|
|
42626
42722
|
return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
|
|
42627
42723
|
}
|
|
42628
|
-
if (!
|
|
42724
|
+
if (!existsSync18(input.artifact_path)) {
|
|
42629
42725
|
return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
|
|
42630
42726
|
}
|
|
42631
42727
|
return {
|
|
@@ -51716,7 +51812,7 @@ __export(exports_environment_snapshots, {
|
|
|
51716
51812
|
captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
|
|
51717
51813
|
});
|
|
51718
51814
|
import { createHash as createHash12 } from "crypto";
|
|
51719
|
-
import { existsSync as
|
|
51815
|
+
import { existsSync as existsSync19, readFileSync as readFileSync14, statSync as statSync9 } from "fs";
|
|
51720
51816
|
import { hostname as hostname2, platform, arch } from "os";
|
|
51721
51817
|
import { dirname as dirname9, join as join18, resolve as resolve20 } from "path";
|
|
51722
51818
|
import { tmpdir as tmpdir3 } from "os";
|
|
@@ -51725,9 +51821,9 @@ function sha2566(value) {
|
|
|
51725
51821
|
}
|
|
51726
51822
|
function fileRecord(root, relativePath) {
|
|
51727
51823
|
const path = join18(root, relativePath);
|
|
51728
|
-
if (!
|
|
51824
|
+
if (!existsSync19(path))
|
|
51729
51825
|
return null;
|
|
51730
|
-
const stat =
|
|
51826
|
+
const stat = statSync9(path);
|
|
51731
51827
|
if (!stat.isFile())
|
|
51732
51828
|
return null;
|
|
51733
51829
|
const content = readFileSync14(path);
|
|
@@ -52428,7 +52524,7 @@ __export(exports_serve, {
|
|
|
52428
52524
|
SECURITY_HEADERS: () => SECURITY_HEADERS,
|
|
52429
52525
|
MIME_TYPES: () => MIME_TYPES
|
|
52430
52526
|
});
|
|
52431
|
-
import { existsSync as
|
|
52527
|
+
import { existsSync as existsSync20 } from "fs";
|
|
52432
52528
|
import { join as join19, dirname as dirname10, extname } from "path";
|
|
52433
52529
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
52434
52530
|
function resolveDashboardDir() {
|
|
@@ -52445,7 +52541,7 @@ function resolveDashboardDir() {
|
|
|
52445
52541
|
}
|
|
52446
52542
|
candidates.push(join19(process.cwd(), "dashboard", "dist"));
|
|
52447
52543
|
for (const candidate of candidates) {
|
|
52448
|
-
if (
|
|
52544
|
+
if (existsSync20(candidate))
|
|
52449
52545
|
return candidate;
|
|
52450
52546
|
}
|
|
52451
52547
|
return join19(process.cwd(), "dashboard", "dist");
|
|
@@ -52507,7 +52603,7 @@ function json(data, status = 200, headers) {
|
|
|
52507
52603
|
});
|
|
52508
52604
|
}
|
|
52509
52605
|
function serveStaticFile(filePath) {
|
|
52510
|
-
if (!
|
|
52606
|
+
if (!existsSync20(filePath))
|
|
52511
52607
|
return null;
|
|
52512
52608
|
const ext = extname(filePath);
|
|
52513
52609
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
@@ -52588,7 +52684,7 @@ data: ${data}
|
|
|
52588
52684
|
filteredSseClients.delete(client);
|
|
52589
52685
|
}
|
|
52590
52686
|
const dashboardDir = resolveDashboardDir();
|
|
52591
|
-
const dashboardExists =
|
|
52687
|
+
const dashboardExists = existsSync20(dashboardDir);
|
|
52592
52688
|
if (!dashboardExists) {
|
|
52593
52689
|
console.error(`
|
|
52594
52690
|
Dashboard not found at: ${dashboardDir}`);
|
|
@@ -54421,7 +54517,7 @@ __export(exports_config_serve_commands, {
|
|
|
54421
54517
|
registerConfigServeCommands: () => registerConfigServeCommands
|
|
54422
54518
|
});
|
|
54423
54519
|
import chalk6 from "chalk";
|
|
54424
|
-
import { existsSync as
|
|
54520
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
|
|
54425
54521
|
import { dirname as dirname11, join as join20 } from "path";
|
|
54426
54522
|
function registerConfigServeCommands(program2) {
|
|
54427
54523
|
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) => {
|
|
@@ -54463,7 +54559,7 @@ function registerConfigServeCommands(program2) {
|
|
|
54463
54559
|
}
|
|
54464
54560
|
obj[keys[keys.length - 1]] = parsedValue;
|
|
54465
54561
|
const dir = dirname11(configPath);
|
|
54466
|
-
if (!
|
|
54562
|
+
if (!existsSync21(dir))
|
|
54467
54563
|
mkdirSync10(dir, { recursive: true });
|
|
54468
54564
|
writeFileSync8(configPath, JSON.stringify(config2, null, 2));
|
|
54469
54565
|
if (globalOpts.json) {
|
|
@@ -55503,7 +55599,7 @@ __export(exports_task_route_sources, {
|
|
|
55503
55599
|
});
|
|
55504
55600
|
import { Database as Database3 } from "bun:sqlite";
|
|
55505
55601
|
import { createHash as createHash13 } from "crypto";
|
|
55506
|
-
import { existsSync as
|
|
55602
|
+
import { existsSync as existsSync22, readdirSync as readdirSync5, statSync as statSync10 } from "fs";
|
|
55507
55603
|
import { basename as basename9, dirname as dirname12, join as join21, resolve as resolve21 } from "path";
|
|
55508
55604
|
function normalizePath5(input) {
|
|
55509
55605
|
return resolve21(input);
|
|
@@ -55568,7 +55664,7 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
55568
55664
|
const rootPath = normalizePath5(sourceRoot);
|
|
55569
55665
|
const errors2 = [];
|
|
55570
55666
|
const stores = [];
|
|
55571
|
-
if (!
|
|
55667
|
+
if (!existsSync22(rootPath)) {
|
|
55572
55668
|
const ref = createStoreRef(join21(rootPath, TODO_STORE_RELATIVE_PATH));
|
|
55573
55669
|
errors2.push({
|
|
55574
55670
|
...ref,
|
|
@@ -55579,7 +55675,7 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
55579
55675
|
}
|
|
55580
55676
|
let rootStat;
|
|
55581
55677
|
try {
|
|
55582
|
-
rootStat =
|
|
55678
|
+
rootStat = statSync10(rootPath);
|
|
55583
55679
|
} catch (error) {
|
|
55584
55680
|
const ref = createStoreRef(join21(rootPath, TODO_STORE_RELATIVE_PATH));
|
|
55585
55681
|
errors2.push({
|
|
@@ -55595,7 +55691,7 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
55595
55691
|
}
|
|
55596
55692
|
function scanDirectory(dir, depth) {
|
|
55597
55693
|
const candidate = join21(dir, TODO_STORE_RELATIVE_PATH);
|
|
55598
|
-
if (
|
|
55694
|
+
if (existsSync22(candidate)) {
|
|
55599
55695
|
stores.push(createStoreRef(candidate));
|
|
55600
55696
|
}
|
|
55601
55697
|
if (depth >= ROOT_SCAN_MAX_DEPTH)
|
|
@@ -55641,7 +55737,7 @@ function collectStoreRefs(input) {
|
|
|
55641
55737
|
};
|
|
55642
55738
|
}
|
|
55643
55739
|
function openReadonlyStore(ref) {
|
|
55644
|
-
if (!
|
|
55740
|
+
if (!existsSync22(ref.source_db_path)) {
|
|
55645
55741
|
throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
|
|
55646
55742
|
}
|
|
55647
55743
|
return new Database3(ref.source_db_path, { readonly: true, create: false });
|
|
@@ -57001,13 +57097,13 @@ Repairs`));
|
|
|
57001
57097
|
try {
|
|
57002
57098
|
const db = getDatabase();
|
|
57003
57099
|
const row = db.query("SELECT COUNT(*) as count FROM tasks").get();
|
|
57004
|
-
const { statSync:
|
|
57100
|
+
const { statSync: statSync11 } = await import("fs");
|
|
57005
57101
|
const { join: join22 } = await import("path");
|
|
57006
57102
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
57007
57103
|
const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join22(home, ".hasna", "todos", "todos.db");
|
|
57008
57104
|
let size = "unknown";
|
|
57009
57105
|
try {
|
|
57010
|
-
size = `${(
|
|
57106
|
+
size = `${(statSync11(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
|
|
57011
57107
|
} catch {}
|
|
57012
57108
|
checks.push({ name: "Database", ok: true, message: `${row.count} tasks \xB7 ${size} \xB7 ${chalk7.dim(dbPath)}` });
|
|
57013
57109
|
} catch (e) {
|
|
@@ -58854,7 +58950,7 @@ __export(exports_mcp_hooks_commands, {
|
|
|
58854
58950
|
});
|
|
58855
58951
|
import chalk8 from "chalk";
|
|
58856
58952
|
import { execSync as execSync3 } from "child_process";
|
|
58857
|
-
import { existsSync as
|
|
58953
|
+
import { existsSync as existsSync23, readFileSync as readFileSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11, chmodSync as chmodSync2 } from "fs";
|
|
58858
58954
|
import { dirname as dirname13, join as join22 } from "path";
|
|
58859
58955
|
function getMcpBinaryPath() {
|
|
58860
58956
|
try {
|
|
@@ -58863,12 +58959,12 @@ function getMcpBinaryPath() {
|
|
|
58863
58959
|
return p;
|
|
58864
58960
|
} catch {}
|
|
58865
58961
|
const bunBin = join22(HOME2, ".bun", "bin", "todos-mcp");
|
|
58866
|
-
if (
|
|
58962
|
+
if (existsSync23(bunBin))
|
|
58867
58963
|
return bunBin;
|
|
58868
58964
|
return "todos-mcp";
|
|
58869
58965
|
}
|
|
58870
58966
|
function readJsonFile2(path) {
|
|
58871
|
-
if (!
|
|
58967
|
+
if (!existsSync23(path))
|
|
58872
58968
|
return {};
|
|
58873
58969
|
try {
|
|
58874
58970
|
return JSON.parse(readFileSync17(path, "utf-8"));
|
|
@@ -58878,19 +58974,19 @@ function readJsonFile2(path) {
|
|
|
58878
58974
|
}
|
|
58879
58975
|
function writeJsonFile2(path, data) {
|
|
58880
58976
|
const dir = dirname13(path);
|
|
58881
|
-
if (!
|
|
58977
|
+
if (!existsSync23(dir))
|
|
58882
58978
|
mkdirSync11(dir, { recursive: true });
|
|
58883
58979
|
writeFileSync10(path, JSON.stringify(data, null, 2) + `
|
|
58884
58980
|
`);
|
|
58885
58981
|
}
|
|
58886
58982
|
function readTomlFile(path) {
|
|
58887
|
-
if (!
|
|
58983
|
+
if (!existsSync23(path))
|
|
58888
58984
|
return "";
|
|
58889
58985
|
return readFileSync17(path, "utf-8");
|
|
58890
58986
|
}
|
|
58891
58987
|
function writeTomlFile(path, content) {
|
|
58892
58988
|
const dir = dirname13(path);
|
|
58893
|
-
if (!
|
|
58989
|
+
if (!existsSync23(dir))
|
|
58894
58990
|
mkdirSync11(dir, { recursive: true });
|
|
58895
58991
|
writeFileSync10(path, content);
|
|
58896
58992
|
}
|
|
@@ -59054,7 +59150,7 @@ function registerMcpHooksCommands(program2) {
|
|
|
59054
59150
|
todosBin = p;
|
|
59055
59151
|
} catch {}
|
|
59056
59152
|
const hooksDir = join22(process.cwd(), ".claude", "hooks");
|
|
59057
|
-
if (!
|
|
59153
|
+
if (!existsSync23(hooksDir))
|
|
59058
59154
|
mkdirSync11(hooksDir, { recursive: true });
|
|
59059
59155
|
const hookScript = `#!/usr/bin/env bash
|
|
59060
59156
|
# Auto-generated by: todos hooks install
|
|
@@ -59956,7 +60052,7 @@ Artifacts:`));
|
|
|
59956
60052
|
const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
59957
60053
|
const hookPath = `${gitDir}/hooks/post-commit`;
|
|
59958
60054
|
const marker = "# todos-auto-link";
|
|
59959
|
-
if (
|
|
60055
|
+
if (existsSync23(hookPath)) {
|
|
59960
60056
|
const existing = readFileSync17(hookPath, "utf-8");
|
|
59961
60057
|
if (existing.includes(marker)) {
|
|
59962
60058
|
console.log(chalk8.yellow("Hook already installed."));
|
|
@@ -59984,7 +60080,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
|
|
|
59984
60080
|
const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
|
|
59985
60081
|
const hookPath = `${gitDir}/hooks/post-commit`;
|
|
59986
60082
|
const marker = "# todos-auto-link";
|
|
59987
|
-
if (!
|
|
60083
|
+
if (!existsSync23(hookPath)) {
|
|
59988
60084
|
console.log(chalk8.dim("No post-commit hook found."));
|
|
59989
60085
|
return;
|
|
59990
60086
|
}
|