Skip to content

Commit 79a5dd6

Browse files
CSCSoftwareclaude
andcommitted
feat: add Global Task Scheduler — due dates, recurring intervals, cross-project triggers
Tasks can now have due dates (relative like "3d" or ISO), repeat intervals, actions, and auto-execute flags. Overdue tasks are reported at every aidex_session call across ALL projects via a fast global mirror table. - New task fields: due, interval, action, auto_go - Global mirror: scheduled_tasks table in ~/.aidex/global.db - Session hook: checkScheduledTasks() runs on every session start - Auto-migration: existing project DBs get new columns automatically - 26 tests covering scheduler logic, task CRUD, global sync - Docs: README, CHANGELOG, MCP-API-REFERENCE, CLAUDE.md updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ce5c2c0 commit 79a5dd6

18 files changed

Lines changed: 1129 additions & 24 deletions

.claude/CLAUDE.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
MCP Server für persistentes Code-Indexing. Ermöglicht Claude Code schnelle, präzise Suchen statt Grep/Glob.
44

5-
**Version:** 1.16.3 | **Sprachen:** 11 | **Repo:** https://github.com/CSCSoftware/AiDex
5+
**Version:** 1.17.0 | **Sprachen:** 11 | **Repo:** https://github.com/CSCSoftware/AiDex
66

77
## Build & Run
88

@@ -77,8 +77,8 @@ Registriert als MCP Server `aidex` (Prefix: `mcp__aidex__aidex_*`).
7777
### Task Backlog (v1.8+)
7878
| Tool | Beschreibung |
7979
|------|--------------|
80-
| `aidex_task` | Task CRUD + Log (create/read/update/delete/log) |
81-
| `aidex_tasks` | Tasks auflisten, filtern nach Status/Priority/Tag |
80+
| `aidex_task` | Task CRUD + Log + Scheduler (due/interval/action/auto_go) |
81+
| `aidex_tasks` | Tasks auflisten, filtern nach Status/Priority/Tag. Zeigt due-Daten + Intervalle |
8282

8383
Status: `backlog → active → done | cancelled`
8484

@@ -158,8 +158,9 @@ src/
158158
| `signatures` | Header-Kommentare |
159159
| `project_files` | Alle Dateien mit Typ |
160160
| `metadata` | Key-Value (Sessions, Notizen) |
161-
| `tasks` | Backlog-Tasks (Priority, Status, Tags) |
161+
| `tasks` | Backlog-Tasks (Priority, Status, Tags, Scheduling: due/interval/action/auto_go) |
162162
| `task_log` | Task-Historie (Auto-Log bei Änderungen) |
163+
| `scheduled_tasks` | Global Scheduler Mirror in ~/.aidex/global.db (project_path, task_id, due) |
163164

164165
## Wichtige Features
165166

@@ -204,6 +205,17 @@ aidex_tasks({ path: ".", status: "active", tag: "bug" }) # Gefiltert
204205
- Auto-Log bei Status-Änderungen und Task-Erstellung
205206
- Viewer: Tasks-Tab mit Priority-Farben, Done-Toggle, Cancelled-Sektion (durchgestrichen)
206207

208+
### Task Scheduler (v1.17)
209+
```
210+
aidex_task({ path: ".", action: "create", title: "Check PR", due: "3d", interval: "3d", task_action: "gh pr list" })
211+
aidex_task({ path: ".", action: "create", title: "One-shot", due: "1w" })
212+
```
213+
- Due: `"30m"`, `"2h"`, `"3d"`, `"1w"` oder ISO-Datum
214+
- Intervall: Automatisch weitergesetzt nach Trigger
215+
- One-Shot: `due` wird nach Trigger gelöscht
216+
- Cross-Project: Bei jedem `aidex_session` werden fällige Tasks aus ALLEN Projekten gemeldet
217+
- `auto_go: true`: Aktion wird automatisch ausgeführt
218+
207219
### Screenshots (v1.9, Optimierung v1.13)
208220
```
209221
aidex_screenshot() # Ganzer Bildschirm

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to AiDex will be documented in this file.
44

55
## [Unreleased]
66

7+
## [1.17.0] - 2026-04-07
8+
9+
### Added
10+
- **Task Scheduler**: Tasks can now have due dates (`due`), repeat intervals (`interval`), actions (`task_action`), and auto-execute flag (`auto_go`)
11+
- **Due dates**: Relative (`"3d"`, `"1w"`) or ISO date (`"2026-04-10"`)
12+
- **Recurring tasks**: Automatically advance due date by interval after each trigger
13+
- **One-shot tasks**: Due date cleared after trigger
14+
- **Cross-project**: Overdue tasks reported at every `aidex_session` call — even from other projects
15+
- **Global mirror**: `scheduled_tasks` table in `~/.aidex/global.db` for fast cross-project lookups
16+
- **Auto-migration**: Existing databases get new columns automatically
17+
- **Test suite**: First tests for AiDex — 26 tests covering scheduler logic, task CRUD, global sync
18+
719
## [1.16.3] - 2026-03-26
820

921
### Fixed

MCP-API-REFERENCE.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,10 @@ Manage a single task in the project backlog. Tasks persist in the AiDex database
679679
| `source` | string | - | Where the task came from (e.g., `"code review of parser.ts:142"`) |
680680
| `sort_order` | number | - | Sort order within same priority (lower = first, default: 0) |
681681
| `note` | string | for log | Log note text |
682+
| `due` | string | - | Due date: ISO date (`"2026-04-10"`) or relative from now (`"3d"`, `"1w"`, `"12h"`). Set to `""` to clear. |
683+
| `interval` | string | - | Repeat interval after trigger: `"30m"`, `"2h"`, `"3d"`, `"1w"`. Omit or `""` for one-shot. |
684+
| `task_action` | string | - | What to do when triggered (description of the action to perform) |
685+
| `auto_go` | boolean | - | If `true`, auto-execute the action on trigger. Default: `false`. |
682686

683687
**Actions:**
684688

@@ -692,6 +696,8 @@ Manage a single task in the project backlog. Tasks persist in the AiDex database
692696

693697
**Auto-logging:** Status changes and task creation are automatically recorded in the task history.
694698

699+
**Task Scheduler:** Tasks with `due` dates are tracked globally in `~/.aidex/global.db`. At every `aidex_session` call, overdue tasks from ALL projects are reported. Recurring tasks (`interval` set) automatically advance their due date. Setting a task to `done`/`cancelled` or clearing `due` removes it from the scheduler.
700+
695701
**Examples:**
696702
```json
697703
// Create a high-priority bug task with summary
@@ -713,6 +719,32 @@ Manage a single task in the project backlog. Tasks persist in the AiDex database
713719

714720
// Add a log note
715721
{ "path": ".", "action": "log", "id": 1, "note": "Root cause found: unbounded buffer" }
722+
723+
// Create a recurring task — check every 3 days
724+
{
725+
"path": ".", "action": "create",
726+
"title": "Check PR status",
727+
"due": "3d", "interval": "3d",
728+
"task_action": "gh pr list --state open"
729+
}
730+
731+
// One-shot reminder in 1 week
732+
{
733+
"path": ".", "action": "create",
734+
"title": "Follow up on review",
735+
"due": "1w"
736+
}
737+
738+
// Auto-execute task
739+
{
740+
"path": ".", "action": "create",
741+
"title": "Refresh project stats",
742+
"due": "1d", "interval": "1d",
743+
"auto_go": true
744+
}
745+
746+
// Clear a task's schedule
747+
{ "path": ".", "action": "update", "id": 3, "due": "" }
716748
```
717749

718750
---
@@ -735,6 +767,8 @@ List and filter tasks in the project backlog. Returns tasks grouped by status an
735767
- Priority icons: 🔴 high, 🟡 medium, ⚪ low
736768
- Task summaries shown inline (one-sentence table-of-contents)
737769
- Tags displayed inline
770+
- Due dates shown with ⏰ indicator (OVERDUE if past due)
771+
- Recurring intervals displayed inline
738772

739773
**Examples:**
740774
```json

README.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ AiDex is an MCP server that gives AI coding assistants instant access to your en
3030
| **Global Search** | `global_init`, `global_query`, `global_signatures`, `global_status`, `global_refresh` | Search across ALL your projects at once — "Have I ever written X?" |
3131
| **Guidelines** | `global_guideline` | Persistent AI instructions & coding conventions — shared across all projects |
3232
| **Sessions** | `session`, `note` | Track sessions, detect external changes, leave notes for next session (with searchable history) |
33-
| **Task Backlog** | `task`, `tasks` | Built-in task management with priorities, tags, and auto-logged history |
33+
| **Task Backlog** | `task`, `tasks` | Built-in task management with priorities, tags, auto-logged history, and **scheduled/recurring tasks** |
3434
| **Log Hub** | `log` | Universal log receiver — any program sends logs via HTTP, queryable by the AI, live in Viewer |
3535
| **Screenshots** | `screenshot`, `windows` | Cross-platform screen capture with LLM optimization — scale + color reduction saves up to 95% tokens |
3636
| **Viewer** | `viewer` | Interactive browser UI with file tree, signatures, tasks, logs, and live reload |
@@ -314,7 +314,7 @@ Do I want to search code?
314314
| Global Search | `aidex_global_init`, `aidex_global_query`, `aidex_global_signatures`, `aidex_global_status`, `aidex_global_refresh` | Search across ALL projects |
315315
| Guidelines | `aidex_global_guideline` | Persistent AI instructions & conventions (key-value, global) |
316316
| Sessions | `aidex_session`, `aidex_note` | Track sessions, leave notes (with searchable history) |
317-
| Tasks | `aidex_task`, `aidex_tasks` | Built-in backlog with priorities, tags, summaries, auto-logged history |
317+
| Tasks | `aidex_task`, `aidex_tasks` | Built-in backlog with priorities, tags, summaries, auto-logged history, scheduled/recurring tasks |
318318
| Log Hub | `aidex_log` | Universal log receiver — any program sends logs via HTTP, AI queries them, live in Viewer |
319319
| Screenshots | `aidex_screenshot`, `aidex_windows` | Screen capture with LLM optimization (scale + color reduction, no index needed) |
320320
| Viewer | `aidex_viewer` | Interactive browser UI with file tree, signatures, tasks, and live logs |
@@ -342,6 +342,9 @@ aidex_task({ path: ".", action: "create", title: "Fix bug", priority: 1, tags: "
342342
aidex_task({ path: ".", action: "update", id: 1, status: "done" })
343343
aidex_task({ path: ".", action: "log", id: 1, note: "Root cause found" })
344344
aidex_tasks({ path: ".", status: "active" })
345+
346+
# Scheduled & recurring tasks
347+
aidex_task({ path: ".", action: "create", title: "Check PR status", due: "3d", interval: "3d", task_action: "gh pr list" })
345348
```
346349
Priority: 1=high, 2=medium, 3=low | Status: `backlog → active → done | cancelled`
347350
@@ -492,12 +495,32 @@ aidex_task({ path: ".", action: "log", id: 1, note: "Root cause: unbounded buffe
492495
aidex_tasks({ path: ".", status: "active" })
493496
```
494497

498+
### Scheduled & Recurring Tasks
499+
500+
Tasks can have due dates and repeat intervals. Overdue tasks are reported at every session start across ALL projects:
501+
502+
```
503+
# One-shot: remind in 3 days
504+
aidex_task({ path: ".", action: "create", title: "Review PR", due: "3d", task_action: "Check if PR was submitted" })
505+
506+
# Recurring: check every week
507+
aidex_task({ path: ".", action: "create", title: "Check dependencies", due: "1w", interval: "1w", task_action: "npm outdated" })
508+
509+
# Auto-execute: runs the action automatically when due
510+
aidex_task({ path: ".", action: "create", title: "Refresh stats", due: "1d", interval: "1d", auto_go: true })
511+
```
512+
513+
**Due formats:** Relative (`"30m"`, `"2h"`, `"3d"`, `"1w"`) or ISO date (`"2026-04-10"`)
514+
515+
At every `aidex_session` call, the **Task Scheduler** checks `~/.aidex/global.db` for due tasks across all projects — even if you're working on a different project. Recurring tasks automatically advance their due date after each trigger.
516+
495517
**Features:**
496518
- **Summaries**: One-sentence table-of-contents per task — scan the backlog without reading full details
497519
- **Priorities**: 🔴 high, 🟡 medium, ⚪ low
498520
- **Statuses**: `backlog → active → done | cancelled`
499521
- **Tags**: Categorize tasks (`bug`, `feature`, `docs`, etc.)
500522
- **History log**: Every status change is auto-logged, plus manual notes
523+
- **Scheduling**: Due dates, recurring intervals, actions, auto-execute across all projects
501524
- **Viewer integration**: Tasks tab in the browser viewer with live updates
502525
- **Persistent**: Tasks survive between sessions, stored in `.aidex/index.db`
503526

jest.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** @type {import('jest').Config} */
2+
export default {
3+
testMatch: ['**/tests/**/*.test.js'],
4+
testEnvironment: 'node',
5+
transform: {},
6+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "aidex-mcp",
3-
"version": "1.16.3",
3+
"version": "1.17.0",
44
"mcpName": "io.github.CSCSoftware/aidex",
55
"description": "MCP Server for persistent code indexing. Gives AI assistants (Claude, Gemini, Copilot, Cursor) instant access to your codebase. 50x less context than grep.",
66
"main": "build/index.js",
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* Global Task Scheduler
3+
*
4+
* Checks for due tasks across all projects at session start.
5+
* Processes due tasks: reports them, advances recurring intervals.
6+
*
7+
* Architecture:
8+
* - Tasks with `due` dates are mirrored in global.db/scheduled_tasks
9+
* - On every aidex_session call, this module scans for due tasks
10+
* - Recurring tasks get their `due` advanced by `interval`
11+
* - One-shot tasks get `due` cleared after trigger
12+
*/
13+
14+
import { existsSync } from 'fs';
15+
import { join, resolve } from 'path';
16+
import { openDatabase, createQueries } from '../../db/index.js';
17+
import { GlobalDatabase, globalDbExists, type ScheduledTaskRow } from '../../db/global-database.js';
18+
import { INDEX_DIR } from '../../constants.js';
19+
import type { TaskRow } from '../../db/queries.js';
20+
import { parseIntervalToMs } from '../shared.js';
21+
import { ensureTaskTables } from '../task.js';
22+
23+
// ============================================================
24+
// Types
25+
// ============================================================
26+
27+
export interface DueTask {
28+
projectPath: string;
29+
projectName: string;
30+
task: TaskRow;
31+
autoGo: boolean;
32+
}
33+
34+
export interface SchedulerResult {
35+
active: boolean;
36+
dueTasks: DueTask[];
37+
processed: number;
38+
errors: string[];
39+
}
40+
41+
// ============================================================
42+
// Scheduler Check
43+
// ============================================================
44+
45+
/**
46+
* Check for due scheduled tasks across all projects.
47+
* Called from session.ts at every session start/continue.
48+
*/
49+
export function checkScheduledTasks(): SchedulerResult {
50+
if (!globalDbExists()) {
51+
return { active: false, dueTasks: [], processed: 0, errors: [] };
52+
}
53+
54+
let globalDb: GlobalDatabase | null = null;
55+
try {
56+
globalDb = new GlobalDatabase();
57+
const now = Date.now();
58+
const dueRows = globalDb.getDueScheduledTasks(now);
59+
60+
if (dueRows.length === 0) {
61+
return { active: true, dueTasks: [], processed: 0, errors: [] };
62+
}
63+
64+
const dueTasks: DueTask[] = [];
65+
const errors: string[] = [];
66+
67+
for (const row of dueRows) {
68+
try {
69+
const projectPath = resolve(row.project_path);
70+
const dbPath = join(projectPath, INDEX_DIR, 'index.db');
71+
72+
if (!existsSync(dbPath)) {
73+
// Project DB missing — remove stale entry
74+
globalDb.removeScheduledTask(row.project_path, row.task_id);
75+
errors.push(`Stale schedule removed: DB missing at ${row.project_path}`);
76+
continue;
77+
}
78+
79+
const db = openDatabase(dbPath, false);
80+
try {
81+
// Ensure scheduler columns exist (migration for older DBs)
82+
ensureTaskTables(db);
83+
const queries = createQueries(db);
84+
const task = queries.getTaskById(row.task_id);
85+
86+
if (!task) {
87+
// Task deleted externally
88+
globalDb.removeScheduledTask(row.project_path, row.task_id);
89+
continue;
90+
}
91+
92+
if (task.status === 'done' || task.status === 'cancelled') {
93+
// Task completed/cancelled externally
94+
globalDb.removeScheduledTask(row.project_path, row.task_id);
95+
continue;
96+
}
97+
98+
// Get project name
99+
const projectName = row.project_path.split('/').pop() || row.project_path;
100+
101+
dueTasks.push({
102+
projectPath: row.project_path,
103+
projectName,
104+
task,
105+
autoGo: row.auto_go === 1,
106+
});
107+
108+
// Advance the schedule
109+
if (row.interval) {
110+
const intervalMs = parseIntervalToMs(row.interval);
111+
if (intervalMs) {
112+
const nextDue = now + intervalMs;
113+
// Update project DB
114+
queries.updateTask(row.task_id, { due: nextDue });
115+
// Update global mirror
116+
globalDb.updateScheduledTaskDue(row.project_path, row.task_id, nextDue);
117+
}
118+
} else {
119+
// One-shot: clear due in project DB, remove from global
120+
queries.updateTask(row.task_id, { due: null });
121+
globalDb.removeScheduledTask(row.project_path, row.task_id);
122+
}
123+
} finally {
124+
db.close();
125+
}
126+
} catch (err) {
127+
errors.push(`Error: task #${row.task_id} in ${row.project_path}: ${err instanceof Error ? err.message : String(err)}`);
128+
}
129+
}
130+
131+
return { active: true, dueTasks, processed: dueTasks.length, errors };
132+
} catch {
133+
// Don't let scheduler errors break anything
134+
return { active: false, dueTasks: [], processed: 0, errors: [] };
135+
} finally {
136+
globalDb?.close();
137+
}
138+
}

src/commands/global/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ export { globalQuery, invalidateGlobalCache, type GlobalQueryParams, type Global
88
export { globalSignatures, type GlobalSignaturesParams, type GlobalSignaturesResult, type GlobalSignaturesProjectResult, type GlobalMethodMatch, type GlobalTypeMatch, type SignatureKind } from './global-signatures.js';
99
export { globalRefresh, type GlobalRefreshParams, type GlobalRefreshResult } from './global-refresh.js';
1010
export { globalGuideline, type GlobalGuidelineParams, type GlobalGuidelineResult, type GuidelineAction } from './global-guideline.js';
11+
export { checkScheduledTasks, type SchedulerResult, type DueTask } from './global-scheduler.js';
12+
export { parseDueDate, parseIntervalToMs } from '../shared.js';

0 commit comments

Comments
 (0)