@hasnaxyz/hook-checktasks 1.0.8 → 1.0.9

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.
Files changed (3) hide show
  1. package/dist/cli.js +156 -31
  2. package/dist/hook.js +84 -21
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -93,19 +93,79 @@ function getAllTaskLists() {
93
93
  return [];
94
94
  }
95
95
  }
96
+ function getProjectIdentifiers(cwd) {
97
+ const genericSegments = new Set([
98
+ "users",
99
+ "home",
100
+ "workspace",
101
+ "workspaces",
102
+ "projects",
103
+ "repos",
104
+ "src",
105
+ "lib",
106
+ "app",
107
+ "apps",
108
+ "packages",
109
+ "platform",
110
+ "service",
111
+ "services",
112
+ "web",
113
+ "api",
114
+ "server",
115
+ "client",
116
+ "frontend",
117
+ "backend",
118
+ "dev",
119
+ "development",
120
+ "prod",
121
+ "staging",
122
+ "tmp",
123
+ "temp",
124
+ "var",
125
+ "opt",
126
+ "usr",
127
+ "volumes"
128
+ ]);
129
+ const segments = cwd.split("/").filter(Boolean);
130
+ const identifiers = [];
131
+ for (let i = segments.length - 1;i >= 0; i--) {
132
+ const seg = segments[i];
133
+ if (seg.length < 3)
134
+ continue;
135
+ if (genericSegments.has(seg.toLowerCase()))
136
+ continue;
137
+ if (i <= 2)
138
+ continue;
139
+ identifiers.push(seg);
140
+ }
141
+ return identifiers;
142
+ }
96
143
  function getProjectTaskLists(cwd) {
97
144
  const allLists = getAllTaskLists();
98
- const dirName = cwd.split("/").filter(Boolean).pop() || "";
99
- const projectLists = allLists.filter((list) => {
145
+ const identifiers = getProjectIdentifiers(cwd);
146
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
147
+ const namedLists = allLists.filter((list) => !uuidRegex.test(list));
148
+ if (namedLists.length === 0 || identifiers.length === 0)
149
+ return [];
150
+ const scored = [];
151
+ for (const list of namedLists) {
100
152
  const listLower = list.toLowerCase();
101
- const dirLower = dirName.toLowerCase();
102
- if (listLower.startsWith(dirLower + "-"))
103
- return true;
104
- if (listLower === dirLower)
105
- return true;
106
- return false;
107
- });
108
- return projectLists;
153
+ let bestScore = 0;
154
+ for (let i = 0;i < identifiers.length; i++) {
155
+ const idLower = identifiers[i].toLowerCase();
156
+ const priorityWeight = identifiers.length - i;
157
+ if (listLower === idLower) {
158
+ bestScore = Math.max(bestScore, priorityWeight * 100);
159
+ } else if (listLower.startsWith(idLower + "-")) {
160
+ bestScore = Math.max(bestScore, priorityWeight * 10);
161
+ }
162
+ }
163
+ if (bestScore > 0) {
164
+ scored.push({ list, score: bestScore });
165
+ }
166
+ }
167
+ scored.sort((a, b) => b.score - a.score);
168
+ return scored.map((s) => s.list);
109
169
  }
110
170
  function getTasksFromList(listId) {
111
171
  const tasksDir = join(homedir(), ".claude", "tasks", listId);
@@ -136,16 +196,7 @@ function run() {
136
196
  if (config.enabled === false) {
137
197
  approve();
138
198
  }
139
- let sessionName = null;
140
- if (hookInput?.transcript_path) {
141
- sessionName = getSessionName(hookInput.transcript_path);
142
- }
143
- const nameToCheck = sessionName || config.taskListId || "";
144
199
  const keywords = config.keywords || ["dev"];
145
- const matchesKeyword = keywords.some((keyword) => nameToCheck.toLowerCase().includes(keyword.toLowerCase()));
146
- if (!matchesKeyword && keywords.length > 0 && nameToCheck) {
147
- approve();
148
- }
149
200
  let listsToCheck = [];
150
201
  if (config.taskListId) {
151
202
  listsToCheck = [config.taskListId];
@@ -153,7 +204,8 @@ function run() {
153
204
  const projectLists = getProjectTaskLists(cwd);
154
205
  if (projectLists.length > 0) {
155
206
  if (keywords.length > 0) {
156
- listsToCheck = projectLists.filter((list) => keywords.some((keyword) => list.toLowerCase().includes(keyword.toLowerCase())));
207
+ const keywordMatched = projectLists.filter((list) => keywords.some((keyword) => list.toLowerCase().includes(keyword.toLowerCase())));
208
+ listsToCheck = keywordMatched.length > 0 ? keywordMatched : projectLists;
157
209
  } else {
158
210
  listsToCheck = projectLists;
159
211
  }
@@ -162,6 +214,17 @@ function run() {
162
214
  if (listsToCheck.length === 0) {
163
215
  approve();
164
216
  }
217
+ let sessionName = null;
218
+ if (hookInput?.transcript_path) {
219
+ sessionName = getSessionName(hookInput.transcript_path);
220
+ }
221
+ if (sessionName && keywords.length > 0) {
222
+ const sessionMatchesKeyword = keywords.some((keyword) => sessionName.toLowerCase().includes(keyword.toLowerCase()));
223
+ const listMatchesKeyword = config.taskListId ? keywords.some((keyword) => config.taskListId.toLowerCase().includes(keyword.toLowerCase())) : true;
224
+ if (!sessionMatchesKeyword && !listMatchesKeyword) {
225
+ approve();
226
+ }
227
+ }
165
228
  let allPending = [];
166
229
  let allInProgress = [];
167
230
  let allCompleted = [];
@@ -186,7 +249,7 @@ function run() {
186
249
  const prompt = `
187
250
  STOP BLOCKED: You have ${remainingCount} tasks remaining${listInfo} (${allPending.length} pending, ${allInProgress.length} in progress, ${allCompleted.length} completed).
188
251
 
189
- DO NOT STOP. DO NOT ASK QUESTIONS. DO NOT WAIT FOR USER INPUT.
252
+ DO NOT STOP. DO NOT ASK QUESTIONS. DO NOT WAIT FOR USER INPUT.
190
253
 
191
254
  You MUST continue working AUTONOMOUSLY until ALL tasks are completed.
192
255
 
@@ -365,19 +428,81 @@ function getAllTaskLists2() {
365
428
  return [];
366
429
  }
367
430
  }
431
+ function getProjectIdentifiers2(projectPath) {
432
+ const genericSegments = new Set([
433
+ "users",
434
+ "home",
435
+ "workspace",
436
+ "workspaces",
437
+ "projects",
438
+ "repos",
439
+ "src",
440
+ "lib",
441
+ "app",
442
+ "apps",
443
+ "packages",
444
+ "platform",
445
+ "service",
446
+ "services",
447
+ "web",
448
+ "api",
449
+ "server",
450
+ "client",
451
+ "frontend",
452
+ "backend",
453
+ "dev",
454
+ "development",
455
+ "prod",
456
+ "staging",
457
+ "tmp",
458
+ "temp",
459
+ "var",
460
+ "opt",
461
+ "usr",
462
+ "volumes"
463
+ ]);
464
+ const segments = projectPath.split("/").filter(Boolean);
465
+ const identifiers = [];
466
+ for (let i = segments.length - 1;i >= 0; i--) {
467
+ const seg = segments[i];
468
+ if (seg.length < 3)
469
+ continue;
470
+ if (genericSegments.has(seg.toLowerCase()))
471
+ continue;
472
+ if (i <= 2)
473
+ continue;
474
+ identifiers.push(seg);
475
+ }
476
+ return identifiers;
477
+ }
368
478
  function getProjectTaskLists2(projectPath) {
369
479
  const allLists = getAllTaskLists2();
370
- const dirName = projectPath.split("/").filter(Boolean).pop() || "";
371
- const projectLists = allLists.filter((list) => {
480
+ const identifiers = getProjectIdentifiers2(projectPath);
481
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
482
+ const namedLists = allLists.filter((list) => !uuidRegex.test(list));
483
+ if (namedLists.length === 0 || identifiers.length === 0)
484
+ return [];
485
+ const scored = [];
486
+ for (const list of namedLists) {
372
487
  const listLower = list.toLowerCase();
373
- const dirLower = dirName.toLowerCase();
374
- if (listLower.startsWith(dirLower + "-"))
375
- return true;
376
- if (listLower.includes(dirLower))
377
- return true;
378
- return false;
379
- });
380
- return projectLists;
488
+ let bestScore = 0;
489
+ for (let i = 0;i < identifiers.length; i++) {
490
+ const idLower = identifiers[i].toLowerCase();
491
+ const priorityWeight = identifiers.length - i;
492
+ if (listLower === idLower) {
493
+ bestScore = Math.max(bestScore, priorityWeight * 100);
494
+ } else if (listLower.startsWith(idLower + "-")) {
495
+ bestScore = Math.max(bestScore, priorityWeight * 10);
496
+ } else if (listLower.includes(idLower)) {
497
+ bestScore = Math.max(bestScore, priorityWeight * 1);
498
+ }
499
+ }
500
+ if (bestScore > 0) {
501
+ scored.push({ list, score: bestScore });
502
+ }
503
+ }
504
+ scored.sort((a, b) => b.score - a.score);
505
+ return scored.map((s) => s.list);
381
506
  }
382
507
  function parseInstallArgs(args) {
383
508
  const options = {};
package/dist/hook.js CHANGED
@@ -89,19 +89,79 @@ function getAllTaskLists() {
89
89
  return [];
90
90
  }
91
91
  }
92
+ function getProjectIdentifiers(cwd) {
93
+ const genericSegments = new Set([
94
+ "users",
95
+ "home",
96
+ "workspace",
97
+ "workspaces",
98
+ "projects",
99
+ "repos",
100
+ "src",
101
+ "lib",
102
+ "app",
103
+ "apps",
104
+ "packages",
105
+ "platform",
106
+ "service",
107
+ "services",
108
+ "web",
109
+ "api",
110
+ "server",
111
+ "client",
112
+ "frontend",
113
+ "backend",
114
+ "dev",
115
+ "development",
116
+ "prod",
117
+ "staging",
118
+ "tmp",
119
+ "temp",
120
+ "var",
121
+ "opt",
122
+ "usr",
123
+ "volumes"
124
+ ]);
125
+ const segments = cwd.split("/").filter(Boolean);
126
+ const identifiers = [];
127
+ for (let i = segments.length - 1;i >= 0; i--) {
128
+ const seg = segments[i];
129
+ if (seg.length < 3)
130
+ continue;
131
+ if (genericSegments.has(seg.toLowerCase()))
132
+ continue;
133
+ if (i <= 2)
134
+ continue;
135
+ identifiers.push(seg);
136
+ }
137
+ return identifiers;
138
+ }
92
139
  function getProjectTaskLists(cwd) {
93
140
  const allLists = getAllTaskLists();
94
- const dirName = cwd.split("/").filter(Boolean).pop() || "";
95
- const projectLists = allLists.filter((list) => {
141
+ const identifiers = getProjectIdentifiers(cwd);
142
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
143
+ const namedLists = allLists.filter((list) => !uuidRegex.test(list));
144
+ if (namedLists.length === 0 || identifiers.length === 0)
145
+ return [];
146
+ const scored = [];
147
+ for (const list of namedLists) {
96
148
  const listLower = list.toLowerCase();
97
- const dirLower = dirName.toLowerCase();
98
- if (listLower.startsWith(dirLower + "-"))
99
- return true;
100
- if (listLower === dirLower)
101
- return true;
102
- return false;
103
- });
104
- return projectLists;
149
+ let bestScore = 0;
150
+ for (let i = 0;i < identifiers.length; i++) {
151
+ const idLower = identifiers[i].toLowerCase();
152
+ const priorityWeight = identifiers.length - i;
153
+ if (listLower === idLower) {
154
+ bestScore = Math.max(bestScore, priorityWeight * 100);
155
+ } else if (listLower.startsWith(idLower + "-")) {
156
+ bestScore = Math.max(bestScore, priorityWeight * 10);
157
+ }
158
+ }
159
+ if (bestScore > 0) {
160
+ scored.push({ list, score: bestScore });
161
+ }
162
+ }
163
+ scored.sort((a, b) => b.score - a.score);
164
+ return scored.map((s) => s.list);
105
165
  }
106
166
  function getTasksFromList(listId) {
107
167
  const tasksDir = join(homedir(), ".claude", "tasks", listId);
@@ -132,16 +192,7 @@ function run() {
132
192
  if (config.enabled === false) {
133
193
  approve();
134
194
  }
135
- let sessionName = null;
136
- if (hookInput?.transcript_path) {
137
- sessionName = getSessionName(hookInput.transcript_path);
138
- }
139
- const nameToCheck = sessionName || config.taskListId || "";
140
195
  const keywords = config.keywords || ["dev"];
141
- const matchesKeyword = keywords.some((keyword) => nameToCheck.toLowerCase().includes(keyword.toLowerCase()));
142
- if (!matchesKeyword && keywords.length > 0 && nameToCheck) {
143
- approve();
144
- }
145
196
  let listsToCheck = [];
146
197
  if (config.taskListId) {
147
198
  listsToCheck = [config.taskListId];
@@ -149,7 +200,8 @@ function run() {
149
200
  const projectLists = getProjectTaskLists(cwd);
150
201
  if (projectLists.length > 0) {
151
202
  if (keywords.length > 0) {
152
- listsToCheck = projectLists.filter((list) => keywords.some((keyword) => list.toLowerCase().includes(keyword.toLowerCase())));
203
+ const keywordMatched = projectLists.filter((list) => keywords.some((keyword) => list.toLowerCase().includes(keyword.toLowerCase())));
204
+ listsToCheck = keywordMatched.length > 0 ? keywordMatched : projectLists;
153
205
  } else {
154
206
  listsToCheck = projectLists;
155
207
  }
@@ -158,6 +210,17 @@ function run() {
158
210
  if (listsToCheck.length === 0) {
159
211
  approve();
160
212
  }
213
+ let sessionName = null;
214
+ if (hookInput?.transcript_path) {
215
+ sessionName = getSessionName(hookInput.transcript_path);
216
+ }
217
+ if (sessionName && keywords.length > 0) {
218
+ const sessionMatchesKeyword = keywords.some((keyword) => sessionName.toLowerCase().includes(keyword.toLowerCase()));
219
+ const listMatchesKeyword = config.taskListId ? keywords.some((keyword) => config.taskListId.toLowerCase().includes(keyword.toLowerCase())) : true;
220
+ if (!sessionMatchesKeyword && !listMatchesKeyword) {
221
+ approve();
222
+ }
223
+ }
161
224
  let allPending = [];
162
225
  let allInProgress = [];
163
226
  let allCompleted = [];
@@ -182,7 +245,7 @@ function run() {
182
245
  const prompt = `
183
246
  STOP BLOCKED: You have ${remainingCount} tasks remaining${listInfo} (${allPending.length} pending, ${allInProgress.length} in progress, ${allCompleted.length} completed).
184
247
 
185
- DO NOT STOP. DO NOT ASK QUESTIONS. DO NOT WAIT FOR USER INPUT.
248
+ DO NOT STOP. DO NOT ASK QUESTIONS. DO NOT WAIT FOR USER INPUT.
186
249
 
187
250
  You MUST continue working AUTONOMOUSLY until ALL tasks are completed.
188
251
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasnaxyz/hook-checktasks",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Claude Code hook that prevents stopping when there are pending tasks",
5
5
  "type": "module",
6
6
  "bin": {