iterate-plugin 2.6.0 → 2.7.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.
@@ -0,0 +1,162 @@
1
+ import { appendFileSync, readFileSync, mkdirSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { defineTool } from '@deepseek-ai/dsh-tools';
4
+ import { resolveProjectRoot } from "../config-loader.js";
5
+ const LOG_DIR = '.iterate';
6
+ const LOG_FILE = 'decision-log.jsonl';
7
+ /**
8
+ * Resolve the log file path, creating the directory if needed.
9
+ */
10
+ function logPath(projectRoot) {
11
+ const dir = join(projectRoot, LOG_DIR);
12
+ if (!existsSync(dir)) {
13
+ mkdirSync(dir, { recursive: true });
14
+ }
15
+ return join(dir, LOG_FILE);
16
+ }
17
+ /**
18
+ * Append one entry to the decision log (JSONL format).
19
+ * Returns the entry count after appending.
20
+ */
21
+ export function appendDecisionEntry(projectRoot, entry) {
22
+ const filePath = logPath(projectRoot);
23
+ const line = JSON.stringify(entry) + '\n';
24
+ appendFileSync(filePath, line, 'utf-8');
25
+ // Count entries
26
+ let count = 0;
27
+ try {
28
+ const content = readFileSync(filePath, 'utf-8');
29
+ count = content.split('\n').filter((l) => l.trim().length > 0).length;
30
+ }
31
+ catch {
32
+ count = 1;
33
+ }
34
+ return { count, path: filePath };
35
+ }
36
+ /**
37
+ * Read all entries from the decision log.
38
+ */
39
+ export function readDecisionEntries(projectRoot) {
40
+ const filePath = join(projectRoot, LOG_DIR, LOG_FILE);
41
+ if (!existsSync(filePath))
42
+ return [];
43
+ try {
44
+ const content = readFileSync(filePath, 'utf-8');
45
+ return content
46
+ .split('\n')
47
+ .filter((l) => l.trim().length > 0)
48
+ .map((l) => JSON.parse(l));
49
+ }
50
+ catch {
51
+ return [];
52
+ }
53
+ }
54
+ /**
55
+ * Register the `iterate_decision_log` tool.
56
+ * Append-only decision log stored in .iterate/decision-log.jsonl.
57
+ * Supports `append` and `read` operations.
58
+ */
59
+ export function registerDecisionLogTool(ctx) {
60
+ ctx.tools.register(defineTool({
61
+ name: 'iterate_decision_log',
62
+ description: 'Append-only decision log for the iterate loop. ' +
63
+ 'Use `append` to record a round start, review finding, fix, validation result, or decision. ' +
64
+ 'Use `read` to retrieve all entries for review. ' +
65
+ 'The log is stored in .iterate/decision-log.jsonl and persists across sessions.',
66
+ parameters: {
67
+ operation: {
68
+ type: 'string',
69
+ required: true,
70
+ description: '"append" to add an entry, "read" to retrieve all entries.',
71
+ enum: ['append', 'read'],
72
+ },
73
+ type: {
74
+ type: 'string',
75
+ description: 'Entry type (required for append): round_start, review_result, atomic_fix, ' +
76
+ 'architectural_fix, revert, validation, decision, report.',
77
+ enum: [
78
+ 'round_start',
79
+ 'review_result',
80
+ 'atomic_fix',
81
+ 'architectural_fix',
82
+ 'revert',
83
+ 'validation',
84
+ 'decision',
85
+ 'report',
86
+ ],
87
+ },
88
+ round: {
89
+ type: 'integer',
90
+ description: 'Current iteration round number (required for append).',
91
+ },
92
+ data: {
93
+ type: 'json',
94
+ description: 'Entry payload as JSON object (required for append).',
95
+ },
96
+ path: {
97
+ type: 'string',
98
+ description: 'Project root directory (default: current working directory).',
99
+ },
100
+ },
101
+ output: {
102
+ schema: {
103
+ type: 'object',
104
+ additionalProperties: false,
105
+ properties: {
106
+ operation: { type: 'string', required: true },
107
+ entryCount: { type: 'integer' },
108
+ logPath: { type: 'string' },
109
+ entries: { type: 'json' },
110
+ success: { type: 'boolean' },
111
+ entry: { type: 'json' },
112
+ error: { type: 'string' },
113
+ },
114
+ },
115
+ render: (_args, value) => [
116
+ { type: 'text', text: JSON.stringify(value, null, 2) },
117
+ ],
118
+ },
119
+ async execute(args) {
120
+ const resolved = resolveProjectRoot(args.path);
121
+ if (!resolved.ok) {
122
+ return { operation: args.operation, error: resolved.reason };
123
+ }
124
+ const projectRoot = resolved.root;
125
+ if (args.operation === 'read') {
126
+ const entries = readDecisionEntries(projectRoot);
127
+ return {
128
+ operation: 'read',
129
+ entryCount: entries.length,
130
+ logPath: join(projectRoot, LOG_DIR, LOG_FILE),
131
+ entries: entries,
132
+ };
133
+ }
134
+ if (args.operation === 'append') {
135
+ if (!args.type || !args.round) {
136
+ return {
137
+ operation: 'append',
138
+ error: 'type and round are required for append operation.',
139
+ };
140
+ }
141
+ const entry = {
142
+ timestamp: new Date().toISOString(),
143
+ round: args.round,
144
+ type: args.type,
145
+ data: args.data ?? {},
146
+ };
147
+ const result = appendDecisionEntry(projectRoot, entry);
148
+ return {
149
+ operation: 'append',
150
+ success: true,
151
+ entryCount: result.count,
152
+ logPath: result.path,
153
+ entry: entry,
154
+ };
155
+ }
156
+ return {
157
+ operation: args.operation,
158
+ error: `Unknown operation "${args.operation}". Use "append" or "read".`,
159
+ };
160
+ },
161
+ }));
162
+ }