@qelos/aidev 0.5.0 → 0.5.2
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/.env.aidev.example +8 -0
- package/README.md +137 -2
- package/dist/cli.js +14 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/accepted.d.ts +7 -2
- package/dist/commands/accepted.d.ts.map +1 -1
- package/dist/commands/accepted.js +17 -2
- package/dist/commands/accepted.js.map +1 -1
- package/dist/commands/help.d.ts.map +1 -1
- package/dist/commands/help.js +13 -0
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/run.d.ts +16 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +408 -20
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/tasks.d.ts +1 -0
- package/dist/commands/tasks.d.ts.map +1 -1
- package/dist/commands/tasks.js +29 -0
- package/dist/commands/tasks.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/github.d.ts +2 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +28 -0
- package/dist/github.js.map +1 -1
- package/dist/hooks.d.ts +16 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +1 -0
- package/dist/hooks.js.map +1 -1
- package/dist/providers/linear.js +1 -1
- package/dist/providers/linear.js.map +1 -1
- package/dist/sessions.d.ts +20 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +171 -0
- package/dist/sessions.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/__tests__/ai-runners.test.d.ts +0 -2
- package/dist/__tests__/ai-runners.test.d.ts.map +0 -1
- package/dist/__tests__/ai-runners.test.js +0 -367
- package/dist/__tests__/ai-runners.test.js.map +0 -1
- package/dist/__tests__/clickup-format.test.d.ts +0 -2
- package/dist/__tests__/clickup-format.test.d.ts.map +0 -1
- package/dist/__tests__/clickup-format.test.js +0 -256
- package/dist/__tests__/clickup-format.test.js.map +0 -1
- package/dist/__tests__/config.test.d.ts +0 -2
- package/dist/__tests__/config.test.d.ts.map +0 -1
- package/dist/__tests__/config.test.js +0 -418
- package/dist/__tests__/config.test.js.map +0 -1
- package/dist/__tests__/git.test.d.ts +0 -2
- package/dist/__tests__/git.test.d.ts.map +0 -1
- package/dist/__tests__/git.test.js +0 -697
- package/dist/__tests__/git.test.js.map +0 -1
- package/dist/__tests__/github.test.d.ts +0 -2
- package/dist/__tests__/github.test.d.ts.map +0 -1
- package/dist/__tests__/github.test.js +0 -209
- package/dist/__tests__/github.test.js.map +0 -1
- package/dist/__tests__/help.test.d.ts +0 -2
- package/dist/__tests__/help.test.d.ts.map +0 -1
- package/dist/__tests__/help.test.js +0 -34
- package/dist/__tests__/help.test.js.map +0 -1
- package/dist/__tests__/hooks.test.d.ts +0 -2
- package/dist/__tests__/hooks.test.d.ts.map +0 -1
- package/dist/__tests__/hooks.test.js +0 -333
- package/dist/__tests__/hooks.test.js.map +0 -1
- package/dist/__tests__/init.test.d.ts +0 -2
- package/dist/__tests__/init.test.d.ts.map +0 -1
- package/dist/__tests__/init.test.js +0 -596
- package/dist/__tests__/init.test.js.map +0 -1
- package/dist/__tests__/local-provider.test.d.ts +0 -2
- package/dist/__tests__/local-provider.test.d.ts.map +0 -1
- package/dist/__tests__/local-provider.test.js +0 -631
- package/dist/__tests__/local-provider.test.js.map +0 -1
- package/dist/__tests__/lockfile.test.d.ts +0 -2
- package/dist/__tests__/lockfile.test.d.ts.map +0 -1
- package/dist/__tests__/lockfile.test.js +0 -144
- package/dist/__tests__/lockfile.test.js.map +0 -1
- package/dist/__tests__/permissions.test.d.ts +0 -2
- package/dist/__tests__/permissions.test.d.ts.map +0 -1
- package/dist/__tests__/permissions.test.js +0 -151
- package/dist/__tests__/permissions.test.js.map +0 -1
- package/dist/__tests__/platform.test.d.ts +0 -2
- package/dist/__tests__/platform.test.d.ts.map +0 -1
- package/dist/__tests__/platform.test.js +0 -329
- package/dist/__tests__/platform.test.js.map +0 -1
- package/dist/__tests__/providers.test.d.ts +0 -2
- package/dist/__tests__/providers.test.d.ts.map +0 -1
- package/dist/__tests__/providers.test.js +0 -925
- package/dist/__tests__/providers.test.js.map +0 -1
- package/dist/__tests__/run.test.d.ts +0 -2
- package/dist/__tests__/run.test.d.ts.map +0 -1
- package/dist/__tests__/run.test.js +0 -529
- package/dist/__tests__/run.test.js.map +0 -1
- package/dist/__tests__/schedule.test.d.ts +0 -2
- package/dist/__tests__/schedule.test.d.ts.map +0 -1
- package/dist/__tests__/schedule.test.js +0 -258
- package/dist/__tests__/schedule.test.js.map +0 -1
|
@@ -1,631 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const node_test_1 = require("node:test");
|
|
40
|
-
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
41
|
-
const fs = __importStar(require("node:fs"));
|
|
42
|
-
const os = __importStar(require("node:os"));
|
|
43
|
-
const path = __importStar(require("node:path"));
|
|
44
|
-
const local_1 = require("../providers/local");
|
|
45
|
-
function withTmpDir(fn) {
|
|
46
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-local-test-'));
|
|
47
|
-
try {
|
|
48
|
-
fn(dir);
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function writeTask(baseDir, folder, filename, content) {
|
|
55
|
-
const dir = path.join((0, local_1.tasksRoot)(baseDir), folder);
|
|
56
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
57
|
-
fs.writeFileSync(path.join(dir, filename), content, 'utf8');
|
|
58
|
-
}
|
|
59
|
-
function writeSession(baseDir, folder, filename, content) {
|
|
60
|
-
const dir = path.join((0, local_1.tasksRoot)(baseDir), folder);
|
|
61
|
-
fs.writeFileSync(path.join(dir, filename), content, 'utf8');
|
|
62
|
-
}
|
|
63
|
-
const sampleTask = `---
|
|
64
|
-
title: Fix login bug
|
|
65
|
-
priority: 2
|
|
66
|
-
assignee: david
|
|
67
|
-
tags: frontend, auth
|
|
68
|
-
created: 2026-03-12T10:00:00.000Z
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
The login form should redirect to the dashboard after authentication.
|
|
72
|
-
`;
|
|
73
|
-
const sampleSession = `<!-- aidev session log -->
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
## aidev — 2026-03-12T10:05:00.000Z
|
|
78
|
-
|
|
79
|
-
[aidev] Starting implementation on branch \`a1b2c3d4/fix-login-bug\`
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## david — 2026-03-12T10:10:00.000Z
|
|
84
|
-
|
|
85
|
-
Please use the new auth API endpoint.
|
|
86
|
-
`;
|
|
87
|
-
// ─── ensureTaskFolders ───────────────────────────────────────────────────────
|
|
88
|
-
(0, node_test_1.describe)('ensureTaskFolders', () => {
|
|
89
|
-
(0, node_test_1.it)('creates all status folders under .aidev/tasks/', () => {
|
|
90
|
-
withTmpDir((dir) => {
|
|
91
|
-
(0, local_1.ensureTaskFolders)(dir);
|
|
92
|
-
for (const folder of ['open', 'pending', 'progress', 'review', 'done']) {
|
|
93
|
-
const p = path.join(dir, '.aidev', 'tasks', folder);
|
|
94
|
-
strict_1.default.ok(fs.existsSync(p), `Missing folder: ${folder}`);
|
|
95
|
-
strict_1.default.ok(fs.statSync(p).isDirectory());
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
(0, node_test_1.it)('is idempotent — does not fail if folders already exist', () => {
|
|
100
|
-
withTmpDir((dir) => {
|
|
101
|
-
(0, local_1.ensureTaskFolders)(dir);
|
|
102
|
-
strict_1.default.doesNotThrow(() => (0, local_1.ensureTaskFolders)(dir));
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
// ─── parseFrontmatter ────────────────────────────────────────────────────────
|
|
107
|
-
(0, node_test_1.describe)('parseFrontmatter', () => {
|
|
108
|
-
(0, node_test_1.it)('parses YAML frontmatter and body from task content', () => {
|
|
109
|
-
const { meta, body } = (0, local_1.parseFrontmatter)(sampleTask);
|
|
110
|
-
strict_1.default.equal(meta.title, 'Fix login bug');
|
|
111
|
-
strict_1.default.equal(meta.priority, '2');
|
|
112
|
-
strict_1.default.equal(meta.assignee, 'david');
|
|
113
|
-
strict_1.default.equal(meta.tags, 'frontend, auth');
|
|
114
|
-
strict_1.default.ok(body.includes('login form'));
|
|
115
|
-
});
|
|
116
|
-
(0, node_test_1.it)('returns empty meta and full body when no frontmatter present', () => {
|
|
117
|
-
const { meta, body } = (0, local_1.parseFrontmatter)('Just a plain description.');
|
|
118
|
-
strict_1.default.deepEqual(meta, {});
|
|
119
|
-
strict_1.default.equal(body, 'Just a plain description.');
|
|
120
|
-
});
|
|
121
|
-
(0, node_test_1.it)('handles frontmatter with no body', () => {
|
|
122
|
-
const { meta, body } = (0, local_1.parseFrontmatter)('---\ntitle: Test\n---\n');
|
|
123
|
-
strict_1.default.equal(meta.title, 'Test');
|
|
124
|
-
strict_1.default.equal(body, '');
|
|
125
|
-
});
|
|
126
|
-
(0, node_test_1.it)('handles values containing colons', () => {
|
|
127
|
-
const { meta } = (0, local_1.parseFrontmatter)('---\nurl: https://example.com\n---\n');
|
|
128
|
-
strict_1.default.equal(meta.url, 'https://example.com');
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
// ─── renderFrontmatter ──────────────────────────────────────────────────────
|
|
132
|
-
(0, node_test_1.describe)('renderFrontmatter', () => {
|
|
133
|
-
(0, node_test_1.it)('renders frontmatter and body', () => {
|
|
134
|
-
const result = (0, local_1.renderFrontmatter)({ title: 'Test', priority: '1' }, 'Description here.');
|
|
135
|
-
strict_1.default.ok(result.startsWith('---\n'));
|
|
136
|
-
strict_1.default.ok(result.includes('title: Test'));
|
|
137
|
-
strict_1.default.ok(result.includes('priority: 1'));
|
|
138
|
-
strict_1.default.ok(result.includes('Description here.'));
|
|
139
|
-
});
|
|
140
|
-
(0, node_test_1.it)('omits empty-value keys', () => {
|
|
141
|
-
const result = (0, local_1.renderFrontmatter)({ title: 'Test', empty: '' }, 'Body');
|
|
142
|
-
strict_1.default.ok(!result.includes('empty'));
|
|
143
|
-
});
|
|
144
|
-
(0, node_test_1.it)('round-trips through parseFrontmatter', () => {
|
|
145
|
-
const original = { title: 'Round trip', priority: '3' };
|
|
146
|
-
const body = 'Some body text.';
|
|
147
|
-
const rendered = (0, local_1.renderFrontmatter)(original, body);
|
|
148
|
-
const { meta, body: parsedBody } = (0, local_1.parseFrontmatter)(rendered);
|
|
149
|
-
strict_1.default.equal(meta.title, 'Round trip');
|
|
150
|
-
strict_1.default.equal(meta.priority, '3');
|
|
151
|
-
strict_1.default.equal(parsedBody, body);
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
// ─── parseSession ────────────────────────────────────────────────────────────
|
|
155
|
-
(0, node_test_1.describe)('parseSession', () => {
|
|
156
|
-
(0, node_test_1.it)('parses session entries with author and timestamp', () => {
|
|
157
|
-
const comments = (0, local_1.parseSession)(sampleSession);
|
|
158
|
-
strict_1.default.equal(comments.length, 2);
|
|
159
|
-
strict_1.default.equal(comments[0].author, 'aidev');
|
|
160
|
-
strict_1.default.ok(comments[0].text.includes('[aidev] Starting implementation'));
|
|
161
|
-
strict_1.default.equal(comments[1].author, 'david');
|
|
162
|
-
strict_1.default.ok(comments[1].text.includes('new auth API'));
|
|
163
|
-
});
|
|
164
|
-
(0, node_test_1.it)('returns empty array for empty content', () => {
|
|
165
|
-
strict_1.default.deepEqual((0, local_1.parseSession)(''), []);
|
|
166
|
-
strict_1.default.deepEqual((0, local_1.parseSession)(' '), []);
|
|
167
|
-
});
|
|
168
|
-
(0, node_test_1.it)('parses entries without timestamp', () => {
|
|
169
|
-
const content = '---\n\n## david\n\nSome comment text.\n';
|
|
170
|
-
const comments = (0, local_1.parseSession)(content);
|
|
171
|
-
strict_1.default.equal(comments.length, 1);
|
|
172
|
-
strict_1.default.equal(comments[0].author, 'david');
|
|
173
|
-
strict_1.default.equal(comments[0].text, 'Some comment text.');
|
|
174
|
-
});
|
|
175
|
-
(0, node_test_1.it)('parses entries with ISO timestamp', () => {
|
|
176
|
-
const content = '---\n\n## bot — 2026-01-15T08:30:00.000Z\n\nHello world\n';
|
|
177
|
-
const comments = (0, local_1.parseSession)(content);
|
|
178
|
-
strict_1.default.equal(comments.length, 1);
|
|
179
|
-
strict_1.default.equal(comments[0].author, 'bot');
|
|
180
|
-
strict_1.default.equal(comments[0].date, new Date('2026-01-15T08:30:00.000Z').getTime());
|
|
181
|
-
});
|
|
182
|
-
(0, node_test_1.it)('handles multiple entries separated by ---', () => {
|
|
183
|
-
const content = [
|
|
184
|
-
'---',
|
|
185
|
-
'',
|
|
186
|
-
'## alice — 2026-01-01T00:00:00.000Z',
|
|
187
|
-
'',
|
|
188
|
-
'First comment.',
|
|
189
|
-
'',
|
|
190
|
-
'---',
|
|
191
|
-
'',
|
|
192
|
-
'## bob — 2026-01-02T00:00:00.000Z',
|
|
193
|
-
'',
|
|
194
|
-
'Second comment.',
|
|
195
|
-
].join('\n');
|
|
196
|
-
const comments = (0, local_1.parseSession)(content);
|
|
197
|
-
strict_1.default.equal(comments.length, 2);
|
|
198
|
-
strict_1.default.equal(comments[0].author, 'alice');
|
|
199
|
-
strict_1.default.equal(comments[1].author, 'bob');
|
|
200
|
-
});
|
|
201
|
-
(0, node_test_1.it)('parses CRLF line endings correctly', () => {
|
|
202
|
-
const content = '---\r\n\r\n## aidev — 2026-03-12T10:05:00.000Z\r\n\r\nStarting implementation\r\n\r\n---\r\n\r\n## david — 2026-03-12T10:10:00.000Z\r\n\r\nPlease use the new API.\r\n';
|
|
203
|
-
const comments = (0, local_1.parseSession)(content);
|
|
204
|
-
strict_1.default.equal(comments.length, 2);
|
|
205
|
-
strict_1.default.equal(comments[0].author, 'aidev');
|
|
206
|
-
strict_1.default.ok(comments[0].text.includes('Starting implementation'));
|
|
207
|
-
strict_1.default.equal(comments[1].author, 'david');
|
|
208
|
-
strict_1.default.ok(comments[1].text.includes('new API'));
|
|
209
|
-
});
|
|
210
|
-
(0, node_test_1.it)('parses mixed LF and CRLF line endings', () => {
|
|
211
|
-
const content = '---\r\n\r\n## bot — 2026-01-15T08:30:00.000Z\r\n\nMixed line endings\n';
|
|
212
|
-
const comments = (0, local_1.parseSession)(content);
|
|
213
|
-
strict_1.default.equal(comments.length, 1);
|
|
214
|
-
strict_1.default.equal(comments[0].author, 'bot');
|
|
215
|
-
strict_1.default.equal(comments[0].text, 'Mixed line endings');
|
|
216
|
-
});
|
|
217
|
-
(0, node_test_1.it)('parses CRLF entry without timestamp', () => {
|
|
218
|
-
const content = '---\r\n\r\n## david\r\n\r\nSome comment.\r\n';
|
|
219
|
-
const comments = (0, local_1.parseSession)(content);
|
|
220
|
-
strict_1.default.equal(comments.length, 1);
|
|
221
|
-
strict_1.default.equal(comments[0].author, 'david');
|
|
222
|
-
strict_1.default.equal(comments[0].text, 'Some comment.');
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
// ─── renderSessionEntry ─────────────────────────────────────────────────────
|
|
226
|
-
(0, node_test_1.describe)('renderSessionEntry', () => {
|
|
227
|
-
(0, node_test_1.it)('produces a parseable session entry', () => {
|
|
228
|
-
const entry = (0, local_1.renderSessionEntry)('aidev', '[aidev] Test comment');
|
|
229
|
-
strict_1.default.ok(entry.includes('## aidev —'));
|
|
230
|
-
strict_1.default.ok(entry.includes('[aidev] Test comment'));
|
|
231
|
-
const comments = (0, local_1.parseSession)(entry);
|
|
232
|
-
strict_1.default.equal(comments.length, 1);
|
|
233
|
-
strict_1.default.equal(comments[0].author, 'aidev');
|
|
234
|
-
strict_1.default.equal(comments[0].text, '[aidev] Test comment');
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
// ─── LocalProvider.fetchTasks ────────────────────────────────────────────────
|
|
238
|
-
(0, node_test_1.describe)('LocalProvider.fetchTasks', () => {
|
|
239
|
-
let tmpDir;
|
|
240
|
-
(0, node_test_1.beforeEach)(() => {
|
|
241
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-local-fetch-'));
|
|
242
|
-
});
|
|
243
|
-
(0, node_test_1.afterEach)(() => {
|
|
244
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
245
|
-
});
|
|
246
|
-
(0, node_test_1.it)('returns empty array when no tasks exist', async () => {
|
|
247
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
248
|
-
const tasks = await provider.fetchTasks();
|
|
249
|
-
strict_1.default.equal(tasks.length, 0);
|
|
250
|
-
});
|
|
251
|
-
(0, node_test_1.it)('reads a task from the open folder', async () => {
|
|
252
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-fix-login-bug.md', sampleTask);
|
|
253
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
254
|
-
const tasks = await provider.fetchTasks();
|
|
255
|
-
strict_1.default.equal(tasks.length, 1);
|
|
256
|
-
strict_1.default.equal(tasks[0].id, 'a1b2c3d4');
|
|
257
|
-
strict_1.default.equal(tasks[0].name, 'Fix login bug');
|
|
258
|
-
strict_1.default.equal(tasks[0].status, 'open');
|
|
259
|
-
strict_1.default.deepEqual(tasks[0].tags, ['frontend', 'auth']);
|
|
260
|
-
strict_1.default.ok(tasks[0].description.includes('login form'));
|
|
261
|
-
});
|
|
262
|
-
(0, node_test_1.it)('maps folder names to correct status strings', async () => {
|
|
263
|
-
writeTask(tmpDir, 'open', 'aaaa0001-t1.md', '---\ntitle: T1\n---\n');
|
|
264
|
-
writeTask(tmpDir, 'pending', 'aaaa0002-t2.md', '---\ntitle: T2\n---\n');
|
|
265
|
-
writeTask(tmpDir, 'progress', 'aaaa0003-t3.md', '---\ntitle: T3\n---\n');
|
|
266
|
-
writeTask(tmpDir, 'review', 'aaaa0004-t4.md', '---\ntitle: T4\n---\n');
|
|
267
|
-
writeTask(tmpDir, 'done', 'aaaa0005-t5.md', '---\ntitle: T5\n---\n');
|
|
268
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
269
|
-
const tasks = await provider.fetchTasks();
|
|
270
|
-
const statusMap = new Map(tasks.map((t) => [t.id, t.status]));
|
|
271
|
-
strict_1.default.equal(statusMap.get('aaaa0001'), 'open');
|
|
272
|
-
strict_1.default.equal(statusMap.get('aaaa0002'), 'pending');
|
|
273
|
-
strict_1.default.equal(statusMap.get('aaaa0003'), 'in progress');
|
|
274
|
-
strict_1.default.equal(statusMap.get('aaaa0004'), 'review');
|
|
275
|
-
strict_1.default.equal(statusMap.get('aaaa0005'), 'done');
|
|
276
|
-
});
|
|
277
|
-
(0, node_test_1.it)('ignores session files when listing tasks', async () => {
|
|
278
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-task.md', '---\ntitle: Task\n---\n');
|
|
279
|
-
writeSession(tmpDir, 'open', 'a1b2c3d4-task.session.md', sampleSession);
|
|
280
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
281
|
-
const tasks = await provider.fetchTasks();
|
|
282
|
-
strict_1.default.equal(tasks.length, 1);
|
|
283
|
-
});
|
|
284
|
-
(0, node_test_1.it)('uses filename as fallback name when title is missing', async () => {
|
|
285
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-some-task.md', 'No frontmatter here.');
|
|
286
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
287
|
-
const tasks = await provider.fetchTasks();
|
|
288
|
-
strict_1.default.equal(tasks[0].name, 'a1b2c3d4-some-task');
|
|
289
|
-
});
|
|
290
|
-
(0, node_test_1.it)('treats task without type field as code task', async () => {
|
|
291
|
-
writeTask(tmpDir, 'open', 'aaaa0001-no-type.md', '---\ntitle: No type field\npriority: 1\n---\n\nTask body.\n');
|
|
292
|
-
const codeProvider = new local_1.LocalProvider(tmpDir, 'code');
|
|
293
|
-
const codeTasks = await codeProvider.fetchTasks();
|
|
294
|
-
strict_1.default.equal(codeTasks.length, 1);
|
|
295
|
-
strict_1.default.equal(codeTasks[0].name, 'No type field');
|
|
296
|
-
const nonCodeProvider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
297
|
-
const nonCodeTasks = await nonCodeProvider.fetchTasks();
|
|
298
|
-
strict_1.default.equal(nonCodeTasks.length, 0);
|
|
299
|
-
});
|
|
300
|
-
(0, node_test_1.it)('treats task with no frontmatter at all as code task', async () => {
|
|
301
|
-
writeTask(tmpDir, 'open', 'aaaa0002-bare.md', 'Just a plain description, no frontmatter.');
|
|
302
|
-
const codeProvider = new local_1.LocalProvider(tmpDir, 'code');
|
|
303
|
-
const codeTasks = await codeProvider.fetchTasks();
|
|
304
|
-
strict_1.default.equal(codeTasks.length, 1);
|
|
305
|
-
strict_1.default.equal(codeTasks[0].description, 'Just a plain description, no frontmatter.');
|
|
306
|
-
const nonCodeProvider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
307
|
-
const nonCodeTasks = await nonCodeProvider.fetchTasks();
|
|
308
|
-
strict_1.default.equal(nonCodeTasks.length, 0);
|
|
309
|
-
});
|
|
310
|
-
(0, node_test_1.it)('treats task with empty type value as code task', async () => {
|
|
311
|
-
writeTask(tmpDir, 'open', 'aaaa0003-empty-type.md', '---\ntitle: Empty type\ntype:\n---\n\nBody.\n');
|
|
312
|
-
const codeProvider = new local_1.LocalProvider(tmpDir, 'code');
|
|
313
|
-
const codeTasks = await codeProvider.fetchTasks();
|
|
314
|
-
strict_1.default.equal(codeTasks.length, 1);
|
|
315
|
-
const nonCodeProvider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
316
|
-
const nonCodeTasks = await nonCodeProvider.fetchTasks();
|
|
317
|
-
strict_1.default.equal(nonCodeTasks.length, 0);
|
|
318
|
-
});
|
|
319
|
-
(0, node_test_1.it)('treats task with type: code explicitly as code task', async () => {
|
|
320
|
-
writeTask(tmpDir, 'open', 'aaaa0004-explicit-code.md', '---\ntitle: Explicit code\ntype: code\n---\n\nCode task.\n');
|
|
321
|
-
const codeProvider = new local_1.LocalProvider(tmpDir, 'code');
|
|
322
|
-
const codeTasks = await codeProvider.fetchTasks();
|
|
323
|
-
strict_1.default.equal(codeTasks.length, 1);
|
|
324
|
-
const nonCodeProvider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
325
|
-
const nonCodeTasks = await nonCodeProvider.fetchTasks();
|
|
326
|
-
strict_1.default.equal(nonCodeTasks.length, 0);
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
// ─── LocalProvider.postComment ───────────────────────────────────────────────
|
|
330
|
-
(0, node_test_1.describe)('LocalProvider.postComment', () => {
|
|
331
|
-
let tmpDir;
|
|
332
|
-
(0, node_test_1.beforeEach)(() => {
|
|
333
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-local-comment-'));
|
|
334
|
-
});
|
|
335
|
-
(0, node_test_1.afterEach)(() => {
|
|
336
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
337
|
-
});
|
|
338
|
-
(0, node_test_1.it)('creates a session file when none exists', async () => {
|
|
339
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-task.md', '---\ntitle: Task\n---\n');
|
|
340
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
341
|
-
await provider.postComment('a1b2c3d4', '[aidev] Starting');
|
|
342
|
-
const sessionPath = path.join((0, local_1.tasksRoot)(tmpDir), 'open', 'a1b2c3d4-task.session.md');
|
|
343
|
-
strict_1.default.ok(fs.existsSync(sessionPath));
|
|
344
|
-
const content = fs.readFileSync(sessionPath, 'utf8');
|
|
345
|
-
strict_1.default.ok(content.includes('[aidev] Starting'));
|
|
346
|
-
strict_1.default.ok(content.includes('## aidev'));
|
|
347
|
-
});
|
|
348
|
-
(0, node_test_1.it)('appends to an existing session file', async () => {
|
|
349
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-task.md', '---\ntitle: Task\n---\n');
|
|
350
|
-
writeSession(tmpDir, 'open', 'a1b2c3d4-task.session.md', sampleSession);
|
|
351
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
352
|
-
await provider.postComment('a1b2c3d4', '[aidev] Done!');
|
|
353
|
-
const content = fs.readFileSync(path.join((0, local_1.tasksRoot)(tmpDir), 'open', 'a1b2c3d4-task.session.md'), 'utf8');
|
|
354
|
-
strict_1.default.ok(content.includes('[aidev] Done!'));
|
|
355
|
-
strict_1.default.ok(content.includes('new auth API')); // original content preserved
|
|
356
|
-
});
|
|
357
|
-
(0, node_test_1.it)('throws when task ID is not found', async () => {
|
|
358
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
359
|
-
await strict_1.default.rejects(() => provider.postComment('nonexistent', 'Hello'), /Local task not found/);
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
// ─── LocalProvider.getComments ───────────────────────────────────────────────
|
|
363
|
-
(0, node_test_1.describe)('LocalProvider.getComments', () => {
|
|
364
|
-
let tmpDir;
|
|
365
|
-
(0, node_test_1.beforeEach)(() => {
|
|
366
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-local-getcomments-'));
|
|
367
|
-
});
|
|
368
|
-
(0, node_test_1.afterEach)(() => {
|
|
369
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
370
|
-
});
|
|
371
|
-
(0, node_test_1.it)('returns empty array when no session file exists', async () => {
|
|
372
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-task.md', '---\ntitle: Task\n---\n');
|
|
373
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
374
|
-
const comments = await provider.getComments('a1b2c3d4');
|
|
375
|
-
strict_1.default.deepEqual(comments, []);
|
|
376
|
-
});
|
|
377
|
-
(0, node_test_1.it)('parses comments from session file', async () => {
|
|
378
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-task.md', '---\ntitle: Task\n---\n');
|
|
379
|
-
writeSession(tmpDir, 'open', 'a1b2c3d4-task.session.md', sampleSession);
|
|
380
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
381
|
-
const comments = await provider.getComments('a1b2c3d4');
|
|
382
|
-
strict_1.default.equal(comments.length, 2);
|
|
383
|
-
strict_1.default.equal(comments[0].author, 'aidev');
|
|
384
|
-
strict_1.default.equal(comments[1].author, 'david');
|
|
385
|
-
});
|
|
386
|
-
(0, node_test_1.it)('returns empty array when task ID is not found', async () => {
|
|
387
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
388
|
-
const comments = await provider.getComments('nonexistent');
|
|
389
|
-
strict_1.default.deepEqual(comments, []);
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
// ─── LocalProvider.updateStatus ──────────────────────────────────────────────
|
|
393
|
-
(0, node_test_1.describe)('LocalProvider.updateStatus', () => {
|
|
394
|
-
let tmpDir;
|
|
395
|
-
(0, node_test_1.beforeEach)(() => {
|
|
396
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-local-status-'));
|
|
397
|
-
});
|
|
398
|
-
(0, node_test_1.afterEach)(() => {
|
|
399
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
400
|
-
});
|
|
401
|
-
(0, node_test_1.it)('moves task file to the target status folder', async () => {
|
|
402
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-task.md', '---\ntitle: Task\n---\n');
|
|
403
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
404
|
-
await provider.updateStatus('a1b2c3d4', 'in progress');
|
|
405
|
-
strict_1.default.ok(!fs.existsSync(path.join((0, local_1.tasksRoot)(tmpDir), 'open', 'a1b2c3d4-task.md')));
|
|
406
|
-
strict_1.default.ok(fs.existsSync(path.join((0, local_1.tasksRoot)(tmpDir), 'progress', 'a1b2c3d4-task.md')));
|
|
407
|
-
});
|
|
408
|
-
(0, node_test_1.it)('also moves the session file', async () => {
|
|
409
|
-
writeTask(tmpDir, 'open', 'a1b2c3d4-task.md', '---\ntitle: Task\n---\n');
|
|
410
|
-
writeSession(tmpDir, 'open', 'a1b2c3d4-task.session.md', sampleSession);
|
|
411
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
412
|
-
await provider.updateStatus('a1b2c3d4', 'review');
|
|
413
|
-
strict_1.default.ok(!fs.existsSync(path.join((0, local_1.tasksRoot)(tmpDir), 'open', 'a1b2c3d4-task.session.md')));
|
|
414
|
-
strict_1.default.ok(fs.existsSync(path.join((0, local_1.tasksRoot)(tmpDir), 'review', 'a1b2c3d4-task.session.md')));
|
|
415
|
-
});
|
|
416
|
-
(0, node_test_1.it)('is a no-op when task is already in the target folder', async () => {
|
|
417
|
-
writeTask(tmpDir, 'review', 'a1b2c3d4-task.md', '---\ntitle: Task\n---\n');
|
|
418
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
419
|
-
await provider.updateStatus('a1b2c3d4', 'review');
|
|
420
|
-
strict_1.default.ok(fs.existsSync(path.join((0, local_1.tasksRoot)(tmpDir), 'review', 'a1b2c3d4-task.md')));
|
|
421
|
-
});
|
|
422
|
-
(0, node_test_1.it)('maps "done", "closed", "cancelled", "complete" to done folder', async () => {
|
|
423
|
-
for (const status of ['done', 'closed', 'cancelled', 'complete']) {
|
|
424
|
-
const id = `aa00${status.slice(0, 4)}`;
|
|
425
|
-
writeTask(tmpDir, 'open', `${id}-task.md`, `---\ntitle: ${status} test\n---\n`);
|
|
426
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
427
|
-
await provider.updateStatus(id, status);
|
|
428
|
-
strict_1.default.ok(fs.existsSync(path.join((0, local_1.tasksRoot)(tmpDir), 'done', `${id}-task.md`)), `Status "${status}" should map to done folder`);
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
(0, node_test_1.it)('throws when task ID is not found', async () => {
|
|
432
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
433
|
-
await strict_1.default.rejects(() => provider.updateStatus('nonexistent', 'done'), /Local task not found/);
|
|
434
|
-
});
|
|
435
|
-
});
|
|
436
|
-
// ─── LocalProvider.createTask ────────────────────────────────────────────────
|
|
437
|
-
(0, node_test_1.describe)('LocalProvider.createTask', () => {
|
|
438
|
-
let tmpDir;
|
|
439
|
-
(0, node_test_1.beforeEach)(() => {
|
|
440
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-local-create-'));
|
|
441
|
-
});
|
|
442
|
-
(0, node_test_1.afterEach)(() => {
|
|
443
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
444
|
-
});
|
|
445
|
-
(0, node_test_1.it)('creates a task file in the open folder', async () => {
|
|
446
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
447
|
-
const result = await provider.createTask({
|
|
448
|
-
title: 'New feature',
|
|
449
|
-
description: 'Build the new feature.',
|
|
450
|
-
tags: ['backend'],
|
|
451
|
-
});
|
|
452
|
-
strict_1.default.ok(result.id);
|
|
453
|
-
strict_1.default.ok(result.url.includes('.aidev'));
|
|
454
|
-
strict_1.default.ok(fs.existsSync(result.url));
|
|
455
|
-
const content = fs.readFileSync(result.url, 'utf8');
|
|
456
|
-
const { meta, body } = (0, local_1.parseFrontmatter)(content);
|
|
457
|
-
strict_1.default.equal(meta.title, 'New feature');
|
|
458
|
-
strict_1.default.equal(meta.tags, 'backend');
|
|
459
|
-
strict_1.default.ok(meta.created);
|
|
460
|
-
strict_1.default.ok(body.includes('Build the new feature.'));
|
|
461
|
-
});
|
|
462
|
-
(0, node_test_1.it)('slugifies the title for the filename', async () => {
|
|
463
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
464
|
-
const result = await provider.createTask({
|
|
465
|
-
title: 'Fix Bug #123: Login Page',
|
|
466
|
-
description: '',
|
|
467
|
-
tags: [],
|
|
468
|
-
});
|
|
469
|
-
strict_1.default.ok(result.url.includes('fix-bug-123-login-page'));
|
|
470
|
-
});
|
|
471
|
-
(0, node_test_1.it)('includes priority and due_date when provided', async () => {
|
|
472
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
473
|
-
const result = await provider.createTask({
|
|
474
|
-
title: 'Urgent task',
|
|
475
|
-
description: '',
|
|
476
|
-
tags: [],
|
|
477
|
-
priority: 1,
|
|
478
|
-
dueDate: new Date('2026-04-01').getTime(),
|
|
479
|
-
});
|
|
480
|
-
const content = fs.readFileSync(result.url, 'utf8');
|
|
481
|
-
const { meta } = (0, local_1.parseFrontmatter)(content);
|
|
482
|
-
strict_1.default.equal(meta.priority, '1');
|
|
483
|
-
strict_1.default.equal(meta.due_date, '2026-04-01');
|
|
484
|
-
});
|
|
485
|
-
(0, node_test_1.it)('new task is discoverable via fetchTasks', async () => {
|
|
486
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
487
|
-
await provider.createTask({
|
|
488
|
-
title: 'Discoverable task',
|
|
489
|
-
description: 'Test discovery.',
|
|
490
|
-
tags: ['test'],
|
|
491
|
-
});
|
|
492
|
-
const tasks = await provider.fetchTasks();
|
|
493
|
-
strict_1.default.equal(tasks.length, 1);
|
|
494
|
-
strict_1.default.equal(tasks[0].name, 'Discoverable task');
|
|
495
|
-
strict_1.default.equal(tasks[0].status, 'open');
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
// ─── Code / non-code mode filtering ──────────────────────────────────────────
|
|
499
|
-
const nonCodeTask = `---
|
|
500
|
-
title: Research auth providers
|
|
501
|
-
type: non-code
|
|
502
|
-
priority: 1
|
|
503
|
-
tags: research
|
|
504
|
-
created: 2026-03-12T10:00:00.000Z
|
|
505
|
-
---
|
|
506
|
-
|
|
507
|
-
Compare OAuth2 providers and write a recommendation.
|
|
508
|
-
`;
|
|
509
|
-
(0, node_test_1.describe)('LocalProvider mode filtering', () => {
|
|
510
|
-
let tmpDir;
|
|
511
|
-
(0, node_test_1.beforeEach)(() => {
|
|
512
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-local-mode-'));
|
|
513
|
-
});
|
|
514
|
-
(0, node_test_1.afterEach)(() => {
|
|
515
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
516
|
-
});
|
|
517
|
-
(0, node_test_1.it)('code-mode provider returns only code tasks (no type or type=code)', async () => {
|
|
518
|
-
writeTask(tmpDir, 'open', 'aaaa0001-code-task.md', sampleTask);
|
|
519
|
-
writeTask(tmpDir, 'open', 'aaaa0002-noncode-task.md', nonCodeTask);
|
|
520
|
-
const provider = new local_1.LocalProvider(tmpDir, 'code');
|
|
521
|
-
const tasks = await provider.fetchTasks();
|
|
522
|
-
strict_1.default.equal(tasks.length, 1);
|
|
523
|
-
strict_1.default.equal(tasks[0].id, 'aaaa0001');
|
|
524
|
-
});
|
|
525
|
-
(0, node_test_1.it)('non-code-mode provider returns only non-code tasks', async () => {
|
|
526
|
-
writeTask(tmpDir, 'open', 'aaaa0001-code-task.md', sampleTask);
|
|
527
|
-
writeTask(tmpDir, 'open', 'aaaa0002-noncode-task.md', nonCodeTask);
|
|
528
|
-
const provider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
529
|
-
const tasks = await provider.fetchTasks();
|
|
530
|
-
strict_1.default.equal(tasks.length, 1);
|
|
531
|
-
strict_1.default.equal(tasks[0].id, 'aaaa0002');
|
|
532
|
-
strict_1.default.equal(tasks[0].name, 'Research auth providers');
|
|
533
|
-
});
|
|
534
|
-
(0, node_test_1.it)('default mode is code — tasks without type field are included', async () => {
|
|
535
|
-
writeTask(tmpDir, 'open', 'aaaa0001-task.md', '---\ntitle: Default task\n---\nBody.\n');
|
|
536
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
537
|
-
const tasks = await provider.fetchTasks();
|
|
538
|
-
strict_1.default.equal(tasks.length, 1);
|
|
539
|
-
});
|
|
540
|
-
(0, node_test_1.it)('non-code mode excludes tasks without type field', async () => {
|
|
541
|
-
writeTask(tmpDir, 'open', 'aaaa0001-task.md', '---\ntitle: Default task\n---\nBody.\n');
|
|
542
|
-
const provider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
543
|
-
const tasks = await provider.fetchTasks();
|
|
544
|
-
strict_1.default.equal(tasks.length, 0);
|
|
545
|
-
});
|
|
546
|
-
(0, node_test_1.it)('createTask in non-code mode sets type: non-code in frontmatter', async () => {
|
|
547
|
-
const provider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
548
|
-
const result = await provider.createTask({
|
|
549
|
-
title: 'Research task',
|
|
550
|
-
description: 'Do some research.',
|
|
551
|
-
tags: ['research'],
|
|
552
|
-
});
|
|
553
|
-
const content = fs.readFileSync(result.url, 'utf8');
|
|
554
|
-
const { meta } = (0, local_1.parseFrontmatter)(content);
|
|
555
|
-
strict_1.default.equal(meta.type, 'non-code');
|
|
556
|
-
});
|
|
557
|
-
(0, node_test_1.it)('createTask in code mode does not set type field', async () => {
|
|
558
|
-
const provider = new local_1.LocalProvider(tmpDir, 'code');
|
|
559
|
-
const result = await provider.createTask({
|
|
560
|
-
title: 'Code task',
|
|
561
|
-
description: 'Fix something.',
|
|
562
|
-
tags: [],
|
|
563
|
-
});
|
|
564
|
-
const content = fs.readFileSync(result.url, 'utf8');
|
|
565
|
-
const { meta } = (0, local_1.parseFrontmatter)(content);
|
|
566
|
-
strict_1.default.ok(!('type' in meta), 'code tasks should not have a type field');
|
|
567
|
-
});
|
|
568
|
-
(0, node_test_1.it)('non-code task created by non-code provider is discoverable by non-code provider only', async () => {
|
|
569
|
-
const codeProvider = new local_1.LocalProvider(tmpDir, 'code');
|
|
570
|
-
const nonCodeProvider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
571
|
-
await nonCodeProvider.createTask({
|
|
572
|
-
title: 'Research task',
|
|
573
|
-
description: 'Research only.',
|
|
574
|
-
tags: [],
|
|
575
|
-
});
|
|
576
|
-
const codeTasks = await codeProvider.fetchTasks();
|
|
577
|
-
const nonCodeTasks = await nonCodeProvider.fetchTasks();
|
|
578
|
-
strict_1.default.equal(codeTasks.length, 0);
|
|
579
|
-
strict_1.default.equal(nonCodeTasks.length, 1);
|
|
580
|
-
strict_1.default.equal(nonCodeTasks[0].name, 'Research task');
|
|
581
|
-
});
|
|
582
|
-
(0, node_test_1.it)('mode filtering works across all status folders', async () => {
|
|
583
|
-
writeTask(tmpDir, 'open', 'aaaa0001-code.md', '---\ntitle: Code open\n---\n');
|
|
584
|
-
writeTask(tmpDir, 'open', 'aaaa0002-noncode.md', '---\ntitle: NC open\ntype: non-code\n---\n');
|
|
585
|
-
writeTask(tmpDir, 'pending', 'aaaa0003-code.md', '---\ntitle: Code pending\n---\n');
|
|
586
|
-
writeTask(tmpDir, 'progress', 'aaaa0004-noncode.md', '---\ntitle: NC progress\ntype: non-code\n---\n');
|
|
587
|
-
const codeProvider = new local_1.LocalProvider(tmpDir, 'code');
|
|
588
|
-
const nonCodeProvider = new local_1.LocalProvider(tmpDir, 'non-code');
|
|
589
|
-
const codeTasks = await codeProvider.fetchTasks();
|
|
590
|
-
const nonCodeTasks = await nonCodeProvider.fetchTasks();
|
|
591
|
-
strict_1.default.equal(codeTasks.length, 2);
|
|
592
|
-
strict_1.default.equal(nonCodeTasks.length, 2);
|
|
593
|
-
strict_1.default.ok(codeTasks.every((t) => !t.name.startsWith('NC')));
|
|
594
|
-
strict_1.default.ok(nonCodeTasks.every((t) => t.name.startsWith('NC')));
|
|
595
|
-
});
|
|
596
|
-
});
|
|
597
|
-
// ─── Full lifecycle ──────────────────────────────────────────────────────────
|
|
598
|
-
(0, node_test_1.describe)('LocalProvider full lifecycle', () => {
|
|
599
|
-
let tmpDir;
|
|
600
|
-
(0, node_test_1.beforeEach)(() => {
|
|
601
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-local-lifecycle-'));
|
|
602
|
-
});
|
|
603
|
-
(0, node_test_1.afterEach)(() => {
|
|
604
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
605
|
-
});
|
|
606
|
-
(0, node_test_1.it)('create → comment → updateStatus → getComments round-trip', async () => {
|
|
607
|
-
const provider = new local_1.LocalProvider(tmpDir);
|
|
608
|
-
const { id } = await provider.createTask({
|
|
609
|
-
title: 'Lifecycle test',
|
|
610
|
-
description: 'Test the full lifecycle.',
|
|
611
|
-
tags: ['test'],
|
|
612
|
-
});
|
|
613
|
-
await provider.postComment(id, '[aidev] Starting implementation');
|
|
614
|
-
await provider.updateStatus(id, 'in progress');
|
|
615
|
-
let tasks = await provider.fetchTasks();
|
|
616
|
-
const task = tasks.find((t) => t.id === id);
|
|
617
|
-
strict_1.default.ok(task);
|
|
618
|
-
strict_1.default.equal(task.status, 'in progress');
|
|
619
|
-
const comments = await provider.getComments(id);
|
|
620
|
-
strict_1.default.equal(comments.length, 1);
|
|
621
|
-
strict_1.default.ok(comments[0].text.includes('[aidev] Starting'));
|
|
622
|
-
await provider.postComment(id, '[aidev] Implementation complete!');
|
|
623
|
-
await provider.updateStatus(id, 'review');
|
|
624
|
-
tasks = await provider.fetchTasks();
|
|
625
|
-
const updated = tasks.find((t) => t.id === id);
|
|
626
|
-
strict_1.default.equal(updated.status, 'review');
|
|
627
|
-
const allComments = await provider.getComments(id);
|
|
628
|
-
strict_1.default.equal(allComments.length, 2);
|
|
629
|
-
});
|
|
630
|
-
});
|
|
631
|
-
//# sourceMappingURL=local-provider.test.js.map
|