@hasna/todos 0.7.0 → 0.8.0

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
@@ -2966,7 +2966,7 @@ var init_search = __esm(() => {
2966
2966
  });
2967
2967
 
2968
2968
  // src/lib/sync-utils.ts
2969
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync as readdirSync2, statSync, writeFileSync } from "fs";
2969
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
2970
2970
  import { join as join2 } from "path";
2971
2971
  function ensureDir2(dir) {
2972
2972
  if (!existsSync2(dir))
@@ -2975,7 +2975,7 @@ function ensureDir2(dir) {
2975
2975
  function listJsonFiles(dir) {
2976
2976
  if (!existsSync2(dir))
2977
2977
  return [];
2978
- return readdirSync2(dir).filter((f) => f.endsWith(".json"));
2978
+ return readdirSync(dir).filter((f) => f.endsWith(".json"));
2979
2979
  }
2980
2980
  function readJsonFile(path) {
2981
2981
  try {
@@ -3021,17 +3021,64 @@ var init_sync_utils = __esm(() => {
3021
3021
  HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
3022
3022
  });
3023
3023
 
3024
- // src/lib/claude-tasks.ts
3024
+ // src/lib/config.ts
3025
3025
  import { existsSync as existsSync3 } from "fs";
3026
3026
  import { join as join3 } from "path";
3027
+ function normalizeAgent(agent) {
3028
+ return agent.trim().toLowerCase();
3029
+ }
3030
+ function loadConfig() {
3031
+ if (cached)
3032
+ return cached;
3033
+ if (!existsSync3(CONFIG_PATH)) {
3034
+ cached = {};
3035
+ return cached;
3036
+ }
3037
+ const config = readJsonFile(CONFIG_PATH) || {};
3038
+ if (typeof config.sync_agents === "string") {
3039
+ config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
3040
+ }
3041
+ cached = config;
3042
+ return cached;
3043
+ }
3044
+ function getSyncAgentsFromConfig() {
3045
+ const config = loadConfig();
3046
+ const agents = config.sync_agents;
3047
+ if (Array.isArray(agents) && agents.length > 0)
3048
+ return agents.map(normalizeAgent);
3049
+ return null;
3050
+ }
3051
+ function getAgentTaskListId(agent) {
3052
+ const config = loadConfig();
3053
+ const key = normalizeAgent(agent);
3054
+ return config.agents?.[key]?.task_list_id || config.task_list_id || null;
3055
+ }
3056
+ function getAgentTasksDir(agent) {
3057
+ const config = loadConfig();
3058
+ const key = normalizeAgent(agent);
3059
+ return config.agents?.[key]?.tasks_dir || config.agent_tasks_dir || null;
3060
+ }
3061
+ function getTaskPrefixConfig() {
3062
+ const config = loadConfig();
3063
+ return config.task_prefix || null;
3064
+ }
3065
+ var CONFIG_PATH, cached = null;
3066
+ var init_config = __esm(() => {
3067
+ init_sync_utils();
3068
+ CONFIG_PATH = join3(HOME, ".todos", "config.json");
3069
+ });
3070
+
3071
+ // src/lib/claude-tasks.ts
3072
+ import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
3073
+ import { join as join4 } from "path";
3027
3074
  function getTaskListDir(taskListId) {
3028
- return join3(HOME, ".claude", "tasks", taskListId);
3075
+ return join4(HOME, ".claude", "tasks", taskListId);
3029
3076
  }
3030
3077
  function readClaudeTask(dir, filename) {
3031
- return readJsonFile(join3(dir, filename));
3078
+ return readJsonFile(join4(dir, filename));
3032
3079
  }
3033
3080
  function writeClaudeTask(dir, task) {
3034
- writeJsonFile(join3(dir, `${task.id}.json`), task);
3081
+ writeJsonFile(join4(dir, `${task.id}.json`), task);
3035
3082
  }
3036
3083
  function toClaudeStatus(status) {
3037
3084
  if (status === "pending" || status === "in_progress" || status === "completed") {
@@ -3042,6 +3089,20 @@ function toClaudeStatus(status) {
3042
3089
  function toSqliteStatus(status) {
3043
3090
  return status;
3044
3091
  }
3092
+ function readPrefixCounter(dir) {
3093
+ const path = join4(dir, ".prefix-counter");
3094
+ if (!existsSync4(path))
3095
+ return 0;
3096
+ const val = parseInt(readFileSync2(path, "utf-8").trim(), 10);
3097
+ return isNaN(val) ? 0 : val;
3098
+ }
3099
+ function writePrefixCounter(dir, value) {
3100
+ writeFileSync2(join4(dir, ".prefix-counter"), String(value));
3101
+ }
3102
+ function formatPrefixedSubject(title, prefix, counter) {
3103
+ const padded = String(counter).padStart(5, "0");
3104
+ return `${prefix}-${padded}: ${title}`;
3105
+ }
3045
3106
  function taskToClaudeTask(task, claudeTaskId, existingMeta) {
3046
3107
  return {
3047
3108
  id: claudeTaskId,
@@ -3063,7 +3124,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
3063
3124
  }
3064
3125
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
3065
3126
  const dir = getTaskListDir(taskListId);
3066
- if (!existsSync3(dir))
3127
+ if (!existsSync4(dir))
3067
3128
  ensureDir2(dir);
3068
3129
  const filter = {};
3069
3130
  if (projectId)
@@ -3072,7 +3133,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
3072
3133
  const existingByTodosId = new Map;
3073
3134
  const files = listJsonFiles(dir);
3074
3135
  for (const f of files) {
3075
- const path = join3(dir, f);
3136
+ const path = join4(dir, f);
3076
3137
  const ct = readClaudeTask(dir, f);
3077
3138
  if (ct?.metadata?.["todos_id"]) {
3078
3139
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -3082,6 +3143,11 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
3082
3143
  let pushed = 0;
3083
3144
  const errors = [];
3084
3145
  const prefer = options.prefer || "remote";
3146
+ const prefixConfig = getTaskPrefixConfig();
3147
+ let prefixCounter = prefixConfig ? readPrefixCounter(dir) : 0;
3148
+ if (prefixConfig?.start_from && prefixCounter < prefixConfig.start_from) {
3149
+ prefixCounter = prefixConfig.start_from - 1;
3150
+ }
3085
3151
  for (const task of tasks) {
3086
3152
  try {
3087
3153
  const existing = existingByTodosId.get(task.id);
@@ -3131,6 +3197,10 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
3131
3197
  const claudeId = String(hwm);
3132
3198
  hwm++;
3133
3199
  const ct = taskToClaudeTask(task, claudeId);
3200
+ if (prefixConfig) {
3201
+ prefixCounter++;
3202
+ ct.subject = formatPrefixedSubject(task.title, prefixConfig.prefix, prefixCounter);
3203
+ }
3134
3204
  writeClaudeTask(dir, ct);
3135
3205
  const current = getTask(task.id);
3136
3206
  if (current) {
@@ -3144,14 +3214,16 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
3144
3214
  }
3145
3215
  }
3146
3216
  writeHighWaterMark(dir, hwm);
3217
+ if (prefixConfig)
3218
+ writePrefixCounter(dir, prefixCounter);
3147
3219
  return { pushed, pulled: 0, errors };
3148
3220
  }
3149
3221
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
3150
3222
  const dir = getTaskListDir(taskListId);
3151
- if (!existsSync3(dir)) {
3223
+ if (!existsSync4(dir)) {
3152
3224
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
3153
3225
  }
3154
- const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
3226
+ const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
3155
3227
  let pulled = 0;
3156
3228
  const errors = [];
3157
3229
  const prefer = options.prefer || "remote";
@@ -3168,7 +3240,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
3168
3240
  }
3169
3241
  for (const f of files) {
3170
3242
  try {
3171
- const filePath = join3(dir, f);
3243
+ const filePath = join4(dir, f);
3172
3244
  const ct = readClaudeTask(dir, f);
3173
3245
  if (!ct)
3174
3246
  continue;
@@ -3236,52 +3308,10 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
3236
3308
  }
3237
3309
  var init_claude_tasks = __esm(() => {
3238
3310
  init_tasks();
3311
+ init_config();
3239
3312
  init_sync_utils();
3240
3313
  });
3241
3314
 
3242
- // src/lib/config.ts
3243
- import { existsSync as existsSync4 } from "fs";
3244
- import { join as join4 } from "path";
3245
- function normalizeAgent(agent) {
3246
- return agent.trim().toLowerCase();
3247
- }
3248
- function loadConfig() {
3249
- if (cached)
3250
- return cached;
3251
- if (!existsSync4(CONFIG_PATH)) {
3252
- cached = {};
3253
- return cached;
3254
- }
3255
- const config = readJsonFile(CONFIG_PATH) || {};
3256
- if (typeof config.sync_agents === "string") {
3257
- config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
3258
- }
3259
- cached = config;
3260
- return cached;
3261
- }
3262
- function getSyncAgentsFromConfig() {
3263
- const config = loadConfig();
3264
- const agents = config.sync_agents;
3265
- if (Array.isArray(agents) && agents.length > 0)
3266
- return agents.map(normalizeAgent);
3267
- return null;
3268
- }
3269
- function getAgentTaskListId(agent) {
3270
- const config = loadConfig();
3271
- const key = normalizeAgent(agent);
3272
- return config.agents?.[key]?.task_list_id || config.task_list_id || null;
3273
- }
3274
- function getAgentTasksDir(agent) {
3275
- const config = loadConfig();
3276
- const key = normalizeAgent(agent);
3277
- return config.agents?.[key]?.tasks_dir || config.agent_tasks_dir || null;
3278
- }
3279
- var CONFIG_PATH, cached = null;
3280
- var init_config = __esm(() => {
3281
- init_sync_utils();
3282
- CONFIG_PATH = join4(HOME, ".todos", "config.json");
3283
- });
3284
-
3285
3315
  // src/lib/agent-tasks.ts
3286
3316
  import { existsSync as existsSync5 } from "fs";
3287
3317
  import { join as join5 } from "path";
@@ -8267,7 +8297,7 @@ var init_rate_limit = __esm(() => {
8267
8297
  });
8268
8298
 
8269
8299
  // src/lib/env.ts
8270
- import { existsSync as existsSync6, readFileSync as readFileSync2 } from "fs";
8300
+ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
8271
8301
  import { join as join6 } from "path";
8272
8302
  function loadEnv() {
8273
8303
  const paths = [
@@ -8277,7 +8307,7 @@ function loadEnv() {
8277
8307
  for (const path of paths) {
8278
8308
  if (!existsSync6(path))
8279
8309
  continue;
8280
- const content = readFileSync2(path, "utf-8");
8310
+ const content = readFileSync3(path, "utf-8");
8281
8311
  for (const line of content.split(`
8282
8312
  `)) {
8283
8313
  const trimmed = line.trim();
@@ -14230,7 +14260,7 @@ __export(exports_serve, {
14230
14260
  createFetchHandler: () => createFetchHandler
14231
14261
  });
14232
14262
  import { execSync } from "child_process";
14233
- import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
14263
+ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
14234
14264
  import { join as join7, dirname as dirname2, extname } from "path";
14235
14265
  import { fileURLToPath } from "url";
14236
14266
  function resolveDashboardDir() {
@@ -14268,7 +14298,7 @@ function json(data, status = 200, port) {
14268
14298
  function getPackageVersion() {
14269
14299
  try {
14270
14300
  const pkgPath = join7(dirname2(fileURLToPath(import.meta.url)), "..", "..", "package.json");
14271
- return JSON.parse(readFileSync3(pkgPath, "utf-8")).version || "0.0.0";
14301
+ return JSON.parse(readFileSync4(pkgPath, "utf-8")).version || "0.0.0";
14272
14302
  } catch {
14273
14303
  return "0.0.0";
14274
14304
  }
@@ -16186,13 +16216,13 @@ init_sync();
16186
16216
  init_config();
16187
16217
  import chalk from "chalk";
16188
16218
  import { execSync as execSync2 } from "child_process";
16189
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
16219
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
16190
16220
  import { basename, dirname as dirname3, join as join8, resolve as resolve2 } from "path";
16191
16221
  import { fileURLToPath as fileURLToPath2 } from "url";
16192
16222
  function getPackageVersion2() {
16193
16223
  try {
16194
16224
  const pkgPath = join8(dirname3(fileURLToPath2(import.meta.url)), "..", "..", "package.json");
16195
- return JSON.parse(readFileSync4(pkgPath, "utf-8")).version || "0.0.0";
16225
+ return JSON.parse(readFileSync5(pkgPath, "utf-8")).version || "0.0.0";
16196
16226
  } catch {
16197
16227
  return "0.0.0";
16198
16228
  }
@@ -16835,7 +16865,7 @@ esac
16835
16865
  exit 0
16836
16866
  `;
16837
16867
  const hookPath = join8(hooksDir, "todos-sync.sh");
16838
- writeFileSync2(hookPath, hookScript);
16868
+ writeFileSync3(hookPath, hookScript);
16839
16869
  execSync2(`chmod +x "${hookPath}"`);
16840
16870
  console.log(chalk.green(`Hook script created: ${hookPath}`));
16841
16871
  const settingsPath = join8(process.cwd(), ".claude", "settings.json");
@@ -16894,7 +16924,7 @@ function readJsonFile2(path) {
16894
16924
  if (!existsSync8(path))
16895
16925
  return {};
16896
16926
  try {
16897
- return JSON.parse(readFileSync4(path, "utf-8"));
16927
+ return JSON.parse(readFileSync5(path, "utf-8"));
16898
16928
  } catch {
16899
16929
  return {};
16900
16930
  }
@@ -16903,19 +16933,19 @@ function writeJsonFile2(path, data) {
16903
16933
  const dir = dirname3(path);
16904
16934
  if (!existsSync8(dir))
16905
16935
  mkdirSync3(dir, { recursive: true });
16906
- writeFileSync2(path, JSON.stringify(data, null, 2) + `
16936
+ writeFileSync3(path, JSON.stringify(data, null, 2) + `
16907
16937
  `);
16908
16938
  }
16909
16939
  function readTomlFile(path) {
16910
16940
  if (!existsSync8(path))
16911
16941
  return "";
16912
- return readFileSync4(path, "utf-8");
16942
+ return readFileSync5(path, "utf-8");
16913
16943
  }
16914
16944
  function writeTomlFile(path, content) {
16915
16945
  const dir = dirname3(path);
16916
16946
  if (!existsSync8(dir))
16917
16947
  mkdirSync3(dir, { recursive: true });
16918
- writeFileSync2(path, content);
16948
+ writeFileSync3(path, content);
16919
16949
  }
16920
16950
  function registerClaude(binPath, global) {
16921
16951
  const configPath = global ? join8(HOME2, ".claude", ".mcp.json") : join8(process.cwd(), ".mcp.json");
package/dist/index.js CHANGED
@@ -1008,11 +1008,15 @@ function searchTasks(query, projectId, db) {
1008
1008
  return rows.map(rowToTask2);
1009
1009
  }
1010
1010
  // src/lib/claude-tasks.ts
1011
+ import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
1012
+ import { join as join4 } from "path";
1013
+
1014
+ // src/lib/config.ts
1011
1015
  import { existsSync as existsSync3 } from "fs";
1012
1016
  import { join as join3 } from "path";
1013
1017
 
1014
1018
  // src/lib/sync-utils.ts
1015
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync as readdirSync2, statSync, writeFileSync } from "fs";
1019
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
1016
1020
  import { join as join2 } from "path";
1017
1021
  var HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
1018
1022
  function ensureDir2(dir) {
@@ -1022,7 +1026,7 @@ function ensureDir2(dir) {
1022
1026
  function listJsonFiles(dir) {
1023
1027
  if (!existsSync2(dir))
1024
1028
  return [];
1025
- return readdirSync2(dir).filter((f) => f.endsWith(".json"));
1029
+ return readdirSync(dir).filter((f) => f.endsWith(".json"));
1026
1030
  }
1027
1031
  function readJsonFile(path) {
1028
1032
  try {
@@ -1064,15 +1068,52 @@ function appendSyncConflict(metadata, conflict, limit = 5) {
1064
1068
  return { ...metadata, sync_conflicts: next };
1065
1069
  }
1066
1070
 
1071
+ // src/lib/config.ts
1072
+ var CONFIG_PATH = join3(HOME, ".todos", "config.json");
1073
+ var cached = null;
1074
+ function normalizeAgent(agent) {
1075
+ return agent.trim().toLowerCase();
1076
+ }
1077
+ function loadConfig() {
1078
+ if (cached)
1079
+ return cached;
1080
+ if (!existsSync3(CONFIG_PATH)) {
1081
+ cached = {};
1082
+ return cached;
1083
+ }
1084
+ const config = readJsonFile(CONFIG_PATH) || {};
1085
+ if (typeof config.sync_agents === "string") {
1086
+ config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
1087
+ }
1088
+ cached = config;
1089
+ return cached;
1090
+ }
1091
+ function getSyncAgentsFromConfig() {
1092
+ const config = loadConfig();
1093
+ const agents = config.sync_agents;
1094
+ if (Array.isArray(agents) && agents.length > 0)
1095
+ return agents.map(normalizeAgent);
1096
+ return null;
1097
+ }
1098
+ function getAgentTasksDir(agent) {
1099
+ const config = loadConfig();
1100
+ const key = normalizeAgent(agent);
1101
+ return config.agents?.[key]?.tasks_dir || config.agent_tasks_dir || null;
1102
+ }
1103
+ function getTaskPrefixConfig() {
1104
+ const config = loadConfig();
1105
+ return config.task_prefix || null;
1106
+ }
1107
+
1067
1108
  // src/lib/claude-tasks.ts
1068
1109
  function getTaskListDir(taskListId) {
1069
- return join3(HOME, ".claude", "tasks", taskListId);
1110
+ return join4(HOME, ".claude", "tasks", taskListId);
1070
1111
  }
1071
1112
  function readClaudeTask(dir, filename) {
1072
- return readJsonFile(join3(dir, filename));
1113
+ return readJsonFile(join4(dir, filename));
1073
1114
  }
1074
1115
  function writeClaudeTask(dir, task) {
1075
- writeJsonFile(join3(dir, `${task.id}.json`), task);
1116
+ writeJsonFile(join4(dir, `${task.id}.json`), task);
1076
1117
  }
1077
1118
  function toClaudeStatus(status) {
1078
1119
  if (status === "pending" || status === "in_progress" || status === "completed") {
@@ -1083,6 +1124,20 @@ function toClaudeStatus(status) {
1083
1124
  function toSqliteStatus(status) {
1084
1125
  return status;
1085
1126
  }
1127
+ function readPrefixCounter(dir) {
1128
+ const path = join4(dir, ".prefix-counter");
1129
+ if (!existsSync4(path))
1130
+ return 0;
1131
+ const val = parseInt(readFileSync2(path, "utf-8").trim(), 10);
1132
+ return isNaN(val) ? 0 : val;
1133
+ }
1134
+ function writePrefixCounter(dir, value) {
1135
+ writeFileSync2(join4(dir, ".prefix-counter"), String(value));
1136
+ }
1137
+ function formatPrefixedSubject(title, prefix, counter) {
1138
+ const padded = String(counter).padStart(5, "0");
1139
+ return `${prefix}-${padded}: ${title}`;
1140
+ }
1086
1141
  function taskToClaudeTask(task, claudeTaskId, existingMeta) {
1087
1142
  return {
1088
1143
  id: claudeTaskId,
@@ -1104,7 +1159,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
1104
1159
  }
1105
1160
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
1106
1161
  const dir = getTaskListDir(taskListId);
1107
- if (!existsSync3(dir))
1162
+ if (!existsSync4(dir))
1108
1163
  ensureDir2(dir);
1109
1164
  const filter = {};
1110
1165
  if (projectId)
@@ -1113,7 +1168,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
1113
1168
  const existingByTodosId = new Map;
1114
1169
  const files = listJsonFiles(dir);
1115
1170
  for (const f of files) {
1116
- const path = join3(dir, f);
1171
+ const path = join4(dir, f);
1117
1172
  const ct = readClaudeTask(dir, f);
1118
1173
  if (ct?.metadata?.["todos_id"]) {
1119
1174
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -1123,6 +1178,11 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
1123
1178
  let pushed = 0;
1124
1179
  const errors = [];
1125
1180
  const prefer = options.prefer || "remote";
1181
+ const prefixConfig = getTaskPrefixConfig();
1182
+ let prefixCounter = prefixConfig ? readPrefixCounter(dir) : 0;
1183
+ if (prefixConfig?.start_from && prefixCounter < prefixConfig.start_from) {
1184
+ prefixCounter = prefixConfig.start_from - 1;
1185
+ }
1126
1186
  for (const task of tasks) {
1127
1187
  try {
1128
1188
  const existing = existingByTodosId.get(task.id);
@@ -1172,6 +1232,10 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
1172
1232
  const claudeId = String(hwm);
1173
1233
  hwm++;
1174
1234
  const ct = taskToClaudeTask(task, claudeId);
1235
+ if (prefixConfig) {
1236
+ prefixCounter++;
1237
+ ct.subject = formatPrefixedSubject(task.title, prefixConfig.prefix, prefixCounter);
1238
+ }
1175
1239
  writeClaudeTask(dir, ct);
1176
1240
  const current = getTask(task.id);
1177
1241
  if (current) {
@@ -1185,14 +1249,16 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
1185
1249
  }
1186
1250
  }
1187
1251
  writeHighWaterMark(dir, hwm);
1252
+ if (prefixConfig)
1253
+ writePrefixCounter(dir, prefixCounter);
1188
1254
  return { pushed, pulled: 0, errors };
1189
1255
  }
1190
1256
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
1191
1257
  const dir = getTaskListDir(taskListId);
1192
- if (!existsSync3(dir)) {
1258
+ if (!existsSync4(dir)) {
1193
1259
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
1194
1260
  }
1195
- const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
1261
+ const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
1196
1262
  let pulled = 0;
1197
1263
  const errors = [];
1198
1264
  const prefer = options.prefer || "remote";
@@ -1209,7 +1275,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
1209
1275
  }
1210
1276
  for (const f of files) {
1211
1277
  try {
1212
- const filePath = join3(dir, f);
1278
+ const filePath = join4(dir, f);
1213
1279
  const ct = readClaudeTask(dir, f);
1214
1280
  if (!ct)
1215
1281
  continue;
@@ -1279,43 +1345,6 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
1279
1345
  // src/lib/agent-tasks.ts
1280
1346
  import { existsSync as existsSync5 } from "fs";
1281
1347
  import { join as join5 } from "path";
1282
-
1283
- // src/lib/config.ts
1284
- import { existsSync as existsSync4 } from "fs";
1285
- import { join as join4 } from "path";
1286
- var CONFIG_PATH = join4(HOME, ".todos", "config.json");
1287
- var cached = null;
1288
- function normalizeAgent(agent) {
1289
- return agent.trim().toLowerCase();
1290
- }
1291
- function loadConfig() {
1292
- if (cached)
1293
- return cached;
1294
- if (!existsSync4(CONFIG_PATH)) {
1295
- cached = {};
1296
- return cached;
1297
- }
1298
- const config = readJsonFile(CONFIG_PATH) || {};
1299
- if (typeof config.sync_agents === "string") {
1300
- config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
1301
- }
1302
- cached = config;
1303
- return cached;
1304
- }
1305
- function getSyncAgentsFromConfig() {
1306
- const config = loadConfig();
1307
- const agents = config.sync_agents;
1308
- if (Array.isArray(agents) && agents.length > 0)
1309
- return agents.map(normalizeAgent);
1310
- return null;
1311
- }
1312
- function getAgentTasksDir(agent) {
1313
- const config = loadConfig();
1314
- const key = normalizeAgent(agent);
1315
- return config.agents?.[key]?.tasks_dir || config.agent_tasks_dir || null;
1316
- }
1317
-
1318
- // src/lib/agent-tasks.ts
1319
1348
  function agentBaseDir(agent) {
1320
1349
  const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
1321
1350
  return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join5(HOME, ".todos", "agents");
package/dist/mcp/index.js CHANGED
@@ -4818,11 +4818,15 @@ function searchTasks(query, projectId, db) {
4818
4818
  }
4819
4819
 
4820
4820
  // src/lib/claude-tasks.ts
4821
+ import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
4822
+ import { join as join4 } from "path";
4823
+
4824
+ // src/lib/config.ts
4821
4825
  import { existsSync as existsSync3 } from "fs";
4822
4826
  import { join as join3 } from "path";
4823
4827
 
4824
4828
  // src/lib/sync-utils.ts
4825
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync as readdirSync2, statSync, writeFileSync } from "fs";
4829
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
4826
4830
  import { join as join2 } from "path";
4827
4831
  var HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
4828
4832
  function ensureDir2(dir) {
@@ -4832,7 +4836,7 @@ function ensureDir2(dir) {
4832
4836
  function listJsonFiles(dir) {
4833
4837
  if (!existsSync2(dir))
4834
4838
  return [];
4835
- return readdirSync2(dir).filter((f) => f.endsWith(".json"));
4839
+ return readdirSync(dir).filter((f) => f.endsWith(".json"));
4836
4840
  }
4837
4841
  function readJsonFile(path) {
4838
4842
  try {
@@ -4874,15 +4878,57 @@ function appendSyncConflict(metadata, conflict, limit = 5) {
4874
4878
  return { ...metadata, sync_conflicts: next };
4875
4879
  }
4876
4880
 
4881
+ // src/lib/config.ts
4882
+ var CONFIG_PATH = join3(HOME, ".todos", "config.json");
4883
+ var cached = null;
4884
+ function normalizeAgent(agent) {
4885
+ return agent.trim().toLowerCase();
4886
+ }
4887
+ function loadConfig() {
4888
+ if (cached)
4889
+ return cached;
4890
+ if (!existsSync3(CONFIG_PATH)) {
4891
+ cached = {};
4892
+ return cached;
4893
+ }
4894
+ const config = readJsonFile(CONFIG_PATH) || {};
4895
+ if (typeof config.sync_agents === "string") {
4896
+ config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
4897
+ }
4898
+ cached = config;
4899
+ return cached;
4900
+ }
4901
+ function getSyncAgentsFromConfig() {
4902
+ const config = loadConfig();
4903
+ const agents = config.sync_agents;
4904
+ if (Array.isArray(agents) && agents.length > 0)
4905
+ return agents.map(normalizeAgent);
4906
+ return null;
4907
+ }
4908
+ function getAgentTaskListId(agent) {
4909
+ const config = loadConfig();
4910
+ const key = normalizeAgent(agent);
4911
+ return config.agents?.[key]?.task_list_id || config.task_list_id || null;
4912
+ }
4913
+ function getAgentTasksDir(agent) {
4914
+ const config = loadConfig();
4915
+ const key = normalizeAgent(agent);
4916
+ return config.agents?.[key]?.tasks_dir || config.agent_tasks_dir || null;
4917
+ }
4918
+ function getTaskPrefixConfig() {
4919
+ const config = loadConfig();
4920
+ return config.task_prefix || null;
4921
+ }
4922
+
4877
4923
  // src/lib/claude-tasks.ts
4878
4924
  function getTaskListDir(taskListId) {
4879
- return join3(HOME, ".claude", "tasks", taskListId);
4925
+ return join4(HOME, ".claude", "tasks", taskListId);
4880
4926
  }
4881
4927
  function readClaudeTask(dir, filename) {
4882
- return readJsonFile(join3(dir, filename));
4928
+ return readJsonFile(join4(dir, filename));
4883
4929
  }
4884
4930
  function writeClaudeTask(dir, task) {
4885
- writeJsonFile(join3(dir, `${task.id}.json`), task);
4931
+ writeJsonFile(join4(dir, `${task.id}.json`), task);
4886
4932
  }
4887
4933
  function toClaudeStatus(status) {
4888
4934
  if (status === "pending" || status === "in_progress" || status === "completed") {
@@ -4893,6 +4939,20 @@ function toClaudeStatus(status) {
4893
4939
  function toSqliteStatus(status) {
4894
4940
  return status;
4895
4941
  }
4942
+ function readPrefixCounter(dir) {
4943
+ const path = join4(dir, ".prefix-counter");
4944
+ if (!existsSync4(path))
4945
+ return 0;
4946
+ const val = parseInt(readFileSync2(path, "utf-8").trim(), 10);
4947
+ return isNaN(val) ? 0 : val;
4948
+ }
4949
+ function writePrefixCounter(dir, value) {
4950
+ writeFileSync2(join4(dir, ".prefix-counter"), String(value));
4951
+ }
4952
+ function formatPrefixedSubject(title, prefix, counter) {
4953
+ const padded = String(counter).padStart(5, "0");
4954
+ return `${prefix}-${padded}: ${title}`;
4955
+ }
4896
4956
  function taskToClaudeTask(task, claudeTaskId, existingMeta) {
4897
4957
  return {
4898
4958
  id: claudeTaskId,
@@ -4914,7 +4974,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
4914
4974
  }
4915
4975
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
4916
4976
  const dir = getTaskListDir(taskListId);
4917
- if (!existsSync3(dir))
4977
+ if (!existsSync4(dir))
4918
4978
  ensureDir2(dir);
4919
4979
  const filter = {};
4920
4980
  if (projectId)
@@ -4923,7 +4983,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
4923
4983
  const existingByTodosId = new Map;
4924
4984
  const files = listJsonFiles(dir);
4925
4985
  for (const f of files) {
4926
- const path = join3(dir, f);
4986
+ const path = join4(dir, f);
4927
4987
  const ct = readClaudeTask(dir, f);
4928
4988
  if (ct?.metadata?.["todos_id"]) {
4929
4989
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -4933,6 +4993,11 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
4933
4993
  let pushed = 0;
4934
4994
  const errors2 = [];
4935
4995
  const prefer = options.prefer || "remote";
4996
+ const prefixConfig = getTaskPrefixConfig();
4997
+ let prefixCounter = prefixConfig ? readPrefixCounter(dir) : 0;
4998
+ if (prefixConfig?.start_from && prefixCounter < prefixConfig.start_from) {
4999
+ prefixCounter = prefixConfig.start_from - 1;
5000
+ }
4936
5001
  for (const task of tasks) {
4937
5002
  try {
4938
5003
  const existing = existingByTodosId.get(task.id);
@@ -4982,6 +5047,10 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
4982
5047
  const claudeId = String(hwm);
4983
5048
  hwm++;
4984
5049
  const ct = taskToClaudeTask(task, claudeId);
5050
+ if (prefixConfig) {
5051
+ prefixCounter++;
5052
+ ct.subject = formatPrefixedSubject(task.title, prefixConfig.prefix, prefixCounter);
5053
+ }
4985
5054
  writeClaudeTask(dir, ct);
4986
5055
  const current = getTask(task.id);
4987
5056
  if (current) {
@@ -4995,14 +5064,16 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
4995
5064
  }
4996
5065
  }
4997
5066
  writeHighWaterMark(dir, hwm);
5067
+ if (prefixConfig)
5068
+ writePrefixCounter(dir, prefixCounter);
4998
5069
  return { pushed, pulled: 0, errors: errors2 };
4999
5070
  }
5000
5071
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
5001
5072
  const dir = getTaskListDir(taskListId);
5002
- if (!existsSync3(dir)) {
5073
+ if (!existsSync4(dir)) {
5003
5074
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
5004
5075
  }
5005
- const files = readdirSync(dir).filter((f) => f.endsWith(".json"));
5076
+ const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
5006
5077
  let pulled = 0;
5007
5078
  const errors2 = [];
5008
5079
  const prefer = options.prefer || "remote";
@@ -5019,7 +5090,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
5019
5090
  }
5020
5091
  for (const f of files) {
5021
5092
  try {
5022
- const filePath = join3(dir, f);
5093
+ const filePath = join4(dir, f);
5023
5094
  const ct = readClaudeTask(dir, f);
5024
5095
  if (!ct)
5025
5096
  continue;
@@ -5089,48 +5160,6 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
5089
5160
  // src/lib/agent-tasks.ts
5090
5161
  import { existsSync as existsSync5 } from "fs";
5091
5162
  import { join as join5 } from "path";
5092
-
5093
- // src/lib/config.ts
5094
- import { existsSync as existsSync4 } from "fs";
5095
- import { join as join4 } from "path";
5096
- var CONFIG_PATH = join4(HOME, ".todos", "config.json");
5097
- var cached = null;
5098
- function normalizeAgent(agent) {
5099
- return agent.trim().toLowerCase();
5100
- }
5101
- function loadConfig() {
5102
- if (cached)
5103
- return cached;
5104
- if (!existsSync4(CONFIG_PATH)) {
5105
- cached = {};
5106
- return cached;
5107
- }
5108
- const config = readJsonFile(CONFIG_PATH) || {};
5109
- if (typeof config.sync_agents === "string") {
5110
- config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
5111
- }
5112
- cached = config;
5113
- return cached;
5114
- }
5115
- function getSyncAgentsFromConfig() {
5116
- const config = loadConfig();
5117
- const agents = config.sync_agents;
5118
- if (Array.isArray(agents) && agents.length > 0)
5119
- return agents.map(normalizeAgent);
5120
- return null;
5121
- }
5122
- function getAgentTaskListId(agent) {
5123
- const config = loadConfig();
5124
- const key = normalizeAgent(agent);
5125
- return config.agents?.[key]?.task_list_id || config.task_list_id || null;
5126
- }
5127
- function getAgentTasksDir(agent) {
5128
- const config = loadConfig();
5129
- const key = normalizeAgent(agent);
5130
- return config.agents?.[key]?.tasks_dir || config.agent_tasks_dir || null;
5131
- }
5132
-
5133
- // src/lib/agent-tasks.ts
5134
5163
  function agentBaseDir(agent) {
5135
5164
  const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
5136
5165
  return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join5(HOME, ".todos", "agents");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",