@jmylchreest/aide-plugin 0.0.38 → 0.0.40

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.
@@ -1,17 +1,18 @@
1
1
  /**
2
2
  * Todo continuation checker — platform-agnostic.
3
3
  *
4
- * Reads the agent's todo list and checks for incomplete items.
4
+ * Reads aide tasks (`aide task list`) and checks for incomplete items.
5
5
  * Used to enhance persistence-logic.ts with precise todo-aware blocking:
6
6
  * instead of a generic "verify your work is complete", we list the
7
- * specific incomplete todos.
7
+ * specific incomplete tasks.
8
8
  *
9
- * For Claude Code: reads todos from the transcript (TodoWrite tool outputs)
10
- * or from aide task list.
11
- * For OpenCode: reads todos via client.session.todo() API.
9
+ * Only checks aide tasks (persistent, cross-session). Native todos
10
+ * (Claude Code TodoWrite, OpenCode todowrite) are session-scoped
11
+ * personal tracking and are intentionally not checked here — neither
12
+ * platform exposes an API for reading them from hooks.
12
13
  *
13
14
  * This module provides the platform-agnostic core. Platform hooks
14
- * call it with however they obtained the todo list.
15
+ * call it via persistence-logic.ts.
15
16
  */
16
17
 
17
18
  import { runAide } from "./aide-client.js";
@@ -19,10 +20,24 @@ import { debug } from "../lib/logger.js";
19
20
 
20
21
  const SOURCE = "todo-checker";
21
22
 
23
+ /**
24
+ * Known terminal statuses — tasks in these states are considered "done".
25
+ * Any status NOT in this set is treated as incomplete (including unknown
26
+ * statuses from future aide versions), which is the safe default for
27
+ * persistence enforcement.
28
+ *
29
+ * Covers both aide backend statuses (done) and any legacy/alias statuses
30
+ * (completed, cancelled) for forward/backward compatibility.
31
+ */
32
+ export const TERMINAL_STATUSES = new Set(["done", "completed", "cancelled"]);
33
+
22
34
  export interface TodoItem {
23
35
  id: string;
24
36
  content: string;
25
- status: "pending" | "in_progress" | "completed" | "cancelled";
37
+ /** Raw status string from aide may be any value, not just known ones. */
38
+ status: string;
39
+ /** Agent that claimed this task, if any. */
40
+ claimedBy?: string;
26
41
  priority?: string;
27
42
  }
28
43
 
@@ -41,8 +56,16 @@ export interface TodoCheckResult {
41
56
 
42
57
  /**
43
58
  * Check a list of todos for incomplete items and build a continuation message.
59
+ *
60
+ * When agentId is provided, only tasks relevant to this agent are considered:
61
+ * - Unclaimed tasks (pending, blocked) — everyone's responsibility
62
+ * - Tasks claimed by this agent — this agent's responsibility
63
+ * Tasks claimed by other agents are filtered out.
44
64
  */
45
- export function checkTodos(todos: TodoItem[]): TodoCheckResult {
65
+ export function checkTodos(
66
+ todos: TodoItem[],
67
+ agentId?: string,
68
+ ): TodoCheckResult {
46
69
  if (!todos || todos.length === 0) {
47
70
  return {
48
71
  hasIncomplete: false,
@@ -53,29 +76,34 @@ export function checkTodos(todos: TodoItem[]): TodoCheckResult {
53
76
  };
54
77
  }
55
78
 
56
- const incomplete = todos.filter(
57
- (t) => t.status !== "completed" && t.status !== "cancelled",
58
- );
79
+ // When scoped to a specific agent, filter out tasks claimed by other agents.
80
+ // Unclaimed tasks (no claimedBy) are considered everyone's responsibility.
81
+ const relevant = agentId
82
+ ? todos.filter((t) => !t.claimedBy || t.claimedBy === agentId)
83
+ : todos;
84
+
85
+ const incomplete = relevant.filter((t) => !TERMINAL_STATUSES.has(t.status));
59
86
 
60
87
  if (incomplete.length === 0) {
61
88
  return {
62
89
  hasIncomplete: false,
63
90
  incompleteCount: 0,
64
- totalCount: todos.length,
91
+ totalCount: relevant.length,
65
92
  incompleteItems: [],
66
93
  message: "",
67
94
  };
68
95
  }
69
96
 
70
- const completedCount = todos.length - incomplete.length;
97
+ const completedCount = relevant.length - incomplete.length;
71
98
  const lines: string[] = [
72
- `**TODO CONTINUATION** — ${incomplete.length} of ${todos.length} tasks incomplete (${completedCount} done)`,
99
+ `**TODO CONTINUATION** — ${incomplete.length} of ${relevant.length} tasks incomplete (${completedCount} done)`,
73
100
  "",
74
101
  "Remaining tasks:",
75
102
  ];
76
103
 
77
104
  for (const item of incomplete) {
78
- const statusIcon = item.status === "in_progress" ? ">" : " ";
105
+ const statusIcon =
106
+ item.status === "in_progress" || item.status === "claimed" ? ">" : " ";
79
107
  lines.push(` [${statusIcon}] ${item.content}`);
80
108
  }
81
109
 
@@ -87,7 +115,7 @@ export function checkTodos(todos: TodoItem[]): TodoCheckResult {
87
115
  return {
88
116
  hasIncomplete: true,
89
117
  incompleteCount: incomplete.length,
90
- totalCount: todos.length,
118
+ totalCount: relevant.length,
91
119
  incompleteItems: incomplete,
92
120
  message: lines.join("\n"),
93
121
  };
@@ -99,6 +127,12 @@ export function checkTodos(todos: TodoItem[]): TodoCheckResult {
99
127
  * Output format from `aide task list`:
100
128
  * [status] id: content
101
129
  * e.g.: [pending] abc123: Implement feature X
130
+ * [claimed] task-def: Deploy service
131
+ *
132
+ * The regex accepts any alphanumeric/underscore status to handle
133
+ * current statuses (pending, claimed, done, blocked) and any future
134
+ * additions without code changes. Unknown statuses are treated as
135
+ * incomplete by checkTodos().
102
136
  */
103
137
  export function parseTodosFromAide(output: string): TodoItem[] {
104
138
  const todos: TodoItem[] = [];
@@ -106,13 +140,14 @@ export function parseTodosFromAide(output: string): TodoItem[] {
106
140
 
107
141
  for (const line of lines) {
108
142
  const match = line.match(
109
- /\[(pending|in_progress|completed|cancelled)\]\s+(\S+):\s+(.+)/,
143
+ /\[(\w+)\]\s+(\S+):\s+(.+?)(?:\s+\(agent:(\S+)\))?$/,
110
144
  );
111
145
  if (match) {
112
146
  todos.push({
113
- status: match[1] as TodoItem["status"],
147
+ status: match[1],
114
148
  id: match[2],
115
149
  content: match[3].trim(),
150
+ claimedBy: match[4] || undefined,
116
151
  });
117
152
  }
118
153
  }
package/src/core/types.ts CHANGED
@@ -15,6 +15,27 @@ export interface AideConfig {
15
15
  /** Auto-export on session end (default: false) */
16
16
  autoExport?: boolean;
17
17
  };
18
+ findings?: {
19
+ /** Complexity analyser settings */
20
+ complexity?: {
21
+ /** Cyclomatic complexity threshold (default: 10) */
22
+ threshold?: number;
23
+ };
24
+ /** Import coupling analyser settings */
25
+ coupling?: {
26
+ /** Fan-out threshold — max outgoing imports (default: 15) */
27
+ fanOut?: number;
28
+ /** Fan-in threshold — max incoming imports (default: 20) */
29
+ fanIn?: number;
30
+ };
31
+ /** Code clone detection settings */
32
+ clones?: {
33
+ /** Sliding window size in tokens (default: 50) */
34
+ windowSize?: number;
35
+ /** Minimum clone size in lines (default: 6) */
36
+ minLines?: number;
37
+ };
38
+ };
18
39
  }
19
40
 
20
41
  export const DEFAULT_CONFIG: AideConfig = {};
@@ -427,7 +427,11 @@ async function handleSessionIdle(
427
427
  // Check persistence: if ralph/autopilot mode is active, re-prompt the session
428
428
  if (state.binary) {
429
429
  try {
430
- const persistResult = checkPersistence(state.binary, state.cwd);
430
+ const persistResult = checkPersistence(
431
+ state.binary,
432
+ state.cwd,
433
+ sessionId,
434
+ );
431
435
  if (persistResult) {
432
436
  const activeMode = getActiveMode(state.binary, state.cwd);
433
437
  debug(
@@ -156,8 +156,8 @@ export const AidePlugin: Plugin = async (ctx: PluginInput): Promise<Hooks> => {
156
156
  // Plugin may be called before the client is fully ready
157
157
  }
158
158
 
159
- // Also log to stderr for direct observability
160
- console.error(rawLog);
159
+ // Also log to stderr when debugging
160
+ if (process.env.AIDE_DEBUG === "1") console.error(rawLog);
161
161
 
162
162
  const resolved = resolveProjectRoot(ctx);
163
163
 
@@ -170,7 +170,7 @@ export const AidePlugin: Plugin = async (ctx: PluginInput): Promise<Hooks> => {
170
170
  } catch {
171
171
  // non-fatal
172
172
  }
173
- console.error(resolvedLog);
173
+ if (process.env.AIDE_DEBUG === "1") console.error(resolvedLog);
174
174
 
175
175
  return createHooks(resolved.root, ctx.worktree, ctx.client, pluginRoot, {
176
176
  skipInit: !resolved.hasProjectRoot,