@hasna/todos 0.11.37 → 0.11.39

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 (61) hide show
  1. package/README.md +12 -7
  2. package/dist/capabilities.d.ts +32 -0
  3. package/dist/capabilities.d.ts.map +1 -0
  4. package/dist/cli/commands/cloud-commands.d.ts.map +1 -1
  5. package/dist/cli/commands/remote-commands.d.ts +3 -0
  6. package/dist/cli/commands/remote-commands.d.ts.map +1 -0
  7. package/dist/cli/index.js +16854 -28731
  8. package/dist/cli/remote-index.d.ts +3 -0
  9. package/dist/cli/remote-index.d.ts.map +1 -0
  10. package/dist/contracts.d.ts +58 -0
  11. package/dist/contracts.d.ts.map +1 -0
  12. package/dist/contracts.js +823 -0
  13. package/dist/db/builtin-templates.d.ts.map +1 -1
  14. package/dist/index.d.ts +14 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +4724 -13647
  17. package/dist/json-contracts.d.ts +56 -0
  18. package/dist/json-contracts.d.ts.map +1 -0
  19. package/dist/lib/cloud-migration.d.ts +53 -0
  20. package/dist/lib/cloud-migration.d.ts.map +1 -0
  21. package/dist/lib/config.d.ts +17 -0
  22. package/dist/lib/config.d.ts.map +1 -1
  23. package/dist/lib/logger.d.ts +6 -12
  24. package/dist/lib/logger.d.ts.map +1 -1
  25. package/dist/mcp/index.d.ts.map +1 -1
  26. package/dist/mcp/index.js +4969 -16672
  27. package/dist/mcp/token-utils.d.ts +15 -0
  28. package/dist/mcp/token-utils.d.ts.map +1 -0
  29. package/dist/mcp/tools/code-tools.d.ts.map +1 -1
  30. package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
  31. package/dist/mcp/tools/task-crud.d.ts.map +1 -1
  32. package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
  33. package/dist/mcp/tools/task-workflow-tools.d.ts.map +1 -1
  34. package/dist/mcp.d.ts +42 -0
  35. package/dist/mcp.d.ts.map +1 -0
  36. package/dist/mcp.js +359 -0
  37. package/dist/registry.d.ts +33 -0
  38. package/dist/registry.d.ts.map +1 -0
  39. package/dist/registry.js +1382 -0
  40. package/dist/remote-cli/remote-index.js +3054 -0
  41. package/dist/remote.d.ts +5 -0
  42. package/dist/remote.d.ts.map +1 -0
  43. package/dist/remote.js +770 -0
  44. package/dist/sdk/client.d.ts +31 -9
  45. package/dist/sdk/client.d.ts.map +1 -1
  46. package/dist/sdk/index.js +635 -0
  47. package/dist/sdk/types.d.ts +6 -0
  48. package/dist/sdk/types.d.ts.map +1 -1
  49. package/dist/server/index.js +51 -19
  50. package/dist/server/routes.d.ts +3 -3
  51. package/dist/server/routes.d.ts.map +1 -1
  52. package/dist/storage/index.d.ts +4 -0
  53. package/dist/storage/index.d.ts.map +1 -0
  54. package/dist/storage/interfaces.d.ts +185 -0
  55. package/dist/storage/interfaces.d.ts.map +1 -0
  56. package/dist/storage/local-sqlite.d.ts +7 -0
  57. package/dist/storage/local-sqlite.d.ts.map +1 -0
  58. package/dist/storage.d.ts +4 -0
  59. package/dist/storage.d.ts.map +1 -0
  60. package/dist/storage.js +4984 -0
  61. package/package.json +22 -4
@@ -0,0 +1,823 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ function __accessProp(key) {
7
+ return this[key];
8
+ }
9
+ var __toCommonJS = (from) => {
10
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
11
+ if (entry)
12
+ return entry;
13
+ entry = __defProp({}, "__esModule", { value: true });
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (var key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(entry, key))
17
+ __defProp(entry, key, {
18
+ get: __accessProp.bind(from, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ __moduleCache.set(from, entry);
23
+ return entry;
24
+ };
25
+ var __moduleCache;
26
+ var __returnValue = (v) => v;
27
+ function __exportSetter(name, newValue) {
28
+ this[name] = __returnValue.bind(null, newValue);
29
+ }
30
+ var __export = (target, all) => {
31
+ for (var name in all)
32
+ __defProp(target, name, {
33
+ get: all[name],
34
+ enumerable: true,
35
+ configurable: true,
36
+ set: __exportSetter.bind(all, name)
37
+ });
38
+ };
39
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
40
+
41
+ // src/types/index.ts
42
+ var TASK_STATUSES, TASK_PRIORITIES, PLAN_STATUSES, VersionConflictError, TaskNotFoundError, ProjectNotFoundError, PlanNotFoundError, LockError, AgentNotFoundError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
43
+ var init_types = __esm(() => {
44
+ TASK_STATUSES = [
45
+ "pending",
46
+ "in_progress",
47
+ "completed",
48
+ "failed",
49
+ "cancelled"
50
+ ];
51
+ TASK_PRIORITIES = [
52
+ "low",
53
+ "medium",
54
+ "high",
55
+ "critical"
56
+ ];
57
+ PLAN_STATUSES = ["active", "completed", "archived"];
58
+ VersionConflictError = class VersionConflictError extends Error {
59
+ taskId;
60
+ expectedVersion;
61
+ actualVersion;
62
+ static code = "VERSION_CONFLICT";
63
+ static suggestion = "Fetch the task with get_task to get the current version before updating.";
64
+ constructor(taskId, expectedVersion, actualVersion) {
65
+ super(`Version conflict for task ${taskId}: expected ${expectedVersion}, got ${actualVersion}`);
66
+ this.taskId = taskId;
67
+ this.expectedVersion = expectedVersion;
68
+ this.actualVersion = actualVersion;
69
+ this.name = "VersionConflictError";
70
+ }
71
+ };
72
+ TaskNotFoundError = class TaskNotFoundError extends Error {
73
+ taskId;
74
+ static code = "TASK_NOT_FOUND";
75
+ static suggestion = "Verify the task ID. Use list_tasks or search_tasks to find the correct ID.";
76
+ constructor(taskId) {
77
+ super(`Task not found: ${taskId}`);
78
+ this.taskId = taskId;
79
+ this.name = "TaskNotFoundError";
80
+ }
81
+ };
82
+ ProjectNotFoundError = class ProjectNotFoundError extends Error {
83
+ projectId;
84
+ static code = "PROJECT_NOT_FOUND";
85
+ static suggestion = "Use list_projects to see available projects.";
86
+ constructor(projectId) {
87
+ super(`Project not found: ${projectId}`);
88
+ this.projectId = projectId;
89
+ this.name = "ProjectNotFoundError";
90
+ }
91
+ };
92
+ PlanNotFoundError = class PlanNotFoundError extends Error {
93
+ planId;
94
+ static code = "PLAN_NOT_FOUND";
95
+ static suggestion = "Use list_plans to see available plans.";
96
+ constructor(planId) {
97
+ super(`Plan not found: ${planId}`);
98
+ this.planId = planId;
99
+ this.name = "PlanNotFoundError";
100
+ }
101
+ };
102
+ LockError = class LockError extends Error {
103
+ taskId;
104
+ lockedBy;
105
+ static code = "LOCK_ERROR";
106
+ static suggestion = "Wait for the lock to expire (30 min) or contact the lock holder.";
107
+ constructor(taskId, lockedBy) {
108
+ super(`Task ${taskId} is locked by ${lockedBy}`);
109
+ this.taskId = taskId;
110
+ this.lockedBy = lockedBy;
111
+ this.name = "LockError";
112
+ }
113
+ };
114
+ AgentNotFoundError = class AgentNotFoundError extends Error {
115
+ agentId;
116
+ static code = "AGENT_NOT_FOUND";
117
+ static suggestion = "Use register_agent to create the agent first, or list_agents to find existing ones.";
118
+ constructor(agentId) {
119
+ super(`Agent not found: ${agentId}`);
120
+ this.agentId = agentId;
121
+ this.name = "AgentNotFoundError";
122
+ }
123
+ };
124
+ TaskListNotFoundError = class TaskListNotFoundError extends Error {
125
+ taskListId;
126
+ static code = "TASK_LIST_NOT_FOUND";
127
+ static suggestion = "Use list_task_lists to see available lists.";
128
+ constructor(taskListId) {
129
+ super(`Task list not found: ${taskListId}`);
130
+ this.taskListId = taskListId;
131
+ this.name = "TaskListNotFoundError";
132
+ }
133
+ };
134
+ DependencyCycleError = class DependencyCycleError extends Error {
135
+ taskId;
136
+ dependsOn;
137
+ static code = "DEPENDENCY_CYCLE";
138
+ static suggestion = "Check the dependency chain with get_task to avoid circular references.";
139
+ constructor(taskId, dependsOn) {
140
+ super(`Adding dependency ${taskId} -> ${dependsOn} would create a cycle`);
141
+ this.taskId = taskId;
142
+ this.dependsOn = dependsOn;
143
+ this.name = "DependencyCycleError";
144
+ }
145
+ };
146
+ CompletionGuardError = class CompletionGuardError extends Error {
147
+ reason;
148
+ retryAfterSeconds;
149
+ static code = "COMPLETION_BLOCKED";
150
+ static suggestion = "Wait for the cooldown period, then retry.";
151
+ constructor(reason, retryAfterSeconds) {
152
+ super(reason);
153
+ this.reason = reason;
154
+ this.retryAfterSeconds = retryAfterSeconds;
155
+ this.name = "CompletionGuardError";
156
+ }
157
+ };
158
+ DISPATCH_STATUSES = ["pending", "sent", "failed", "cancelled"];
159
+ DispatchNotFoundError = class DispatchNotFoundError extends Error {
160
+ dispatchId;
161
+ static code = "DISPATCH_NOT_FOUND";
162
+ static suggestion = "Check the dispatch ID with list_dispatches.";
163
+ constructor(dispatchId) {
164
+ super(`Dispatch not found: ${dispatchId}`);
165
+ this.dispatchId = dispatchId;
166
+ this.name = "DispatchNotFoundError";
167
+ }
168
+ };
169
+ });
170
+
171
+ // src/lib/package-version.ts
172
+ import { existsSync, readFileSync } from "fs";
173
+ import { dirname, join } from "path";
174
+ import { fileURLToPath } from "url";
175
+ function getPackageVersion(fromUrl = import.meta.url) {
176
+ try {
177
+ let dir = dirname(fileURLToPath(fromUrl));
178
+ for (let i = 0;i < 5; i++) {
179
+ const pkgPath = join(dir, "package.json");
180
+ if (existsSync(pkgPath)) {
181
+ return JSON.parse(readFileSync(pkgPath, "utf-8")).version || "0.0.0";
182
+ }
183
+ const parent = dirname(dir);
184
+ if (parent === dir)
185
+ break;
186
+ dir = parent;
187
+ }
188
+ } catch {
189
+ return "0.0.0";
190
+ }
191
+ return "0.0.0";
192
+ }
193
+
194
+ // src/json-contracts.ts
195
+ function source(version) {
196
+ return {
197
+ packageName: "@hasna/todos",
198
+ repository: "hasna/todos",
199
+ version
200
+ };
201
+ }
202
+ function field(type, description, nullable = false) {
203
+ return { type, description, nullable };
204
+ }
205
+ function contract(input) {
206
+ return {
207
+ ...input,
208
+ additionalProperties: true,
209
+ evolution: {
210
+ additionalFields: "allowed",
211
+ removingRequiredFields: "breaking",
212
+ changingRequiredFieldTypes: "breaking",
213
+ nullableToNonNullable: "breaking"
214
+ }
215
+ };
216
+ }
217
+ var idField = field("string", "Stable UUID or short identifier.");
218
+ var nullableIdField = field(["string", "null"], "Stable identifier when attached to another object.", true);
219
+ var isoDateField = field("string", "ISO-8601 timestamp string.");
220
+ var nullableIsoDateField = field(["string", "null"], "ISO-8601 timestamp string, or null when not set.", true);
221
+ var metadataField = field("object", "JSON object metadata. Unknown keys are extension data.");
222
+ var tagsField = field("array", "Array of string tags.");
223
+ var TODOS_JSON_CONTRACTS = [
224
+ contract({
225
+ id: "task",
226
+ name: "Task",
227
+ description: "Canonical task object returned by CLI JSON, SDK, API summaries, and MCP task tools.",
228
+ surfaces: ["cli", "api", "mcp", "sdk"],
229
+ stability: "stable",
230
+ required: {
231
+ id: idField,
232
+ short_id: nullableIdField,
233
+ title: field("string", "Human-readable task title."),
234
+ description: field(["string", "null"], "Optional task description.", true),
235
+ status: field("string", "Task lifecycle status."),
236
+ priority: field("string", "Task priority."),
237
+ project_id: nullableIdField,
238
+ plan_id: nullableIdField,
239
+ task_list_id: nullableIdField,
240
+ agent_id: nullableIdField,
241
+ assigned_to: field(["string", "null"], "Assigned agent name or id.", true),
242
+ tags: tagsField,
243
+ metadata: metadataField,
244
+ version: field("integer", "Optimistic locking version."),
245
+ created_at: isoDateField,
246
+ updated_at: isoDateField
247
+ },
248
+ optional: {
249
+ locked_by: field(["string", "null"], "Agent currently holding the task lock.", true),
250
+ locked_at: nullableIsoDateField,
251
+ started_at: nullableIsoDateField,
252
+ completed_at: nullableIsoDateField,
253
+ due_at: nullableIsoDateField
254
+ }
255
+ }),
256
+ contract({
257
+ id: "project",
258
+ name: "Project",
259
+ description: "Project object used to scope tasks to a local repository or workspace.",
260
+ surfaces: ["cli", "api", "mcp", "sdk"],
261
+ stability: "stable",
262
+ required: {
263
+ id: idField,
264
+ name: field("string", "Project display name."),
265
+ path: field("string", "Canonical project path."),
266
+ description: field(["string", "null"], "Optional project description.", true),
267
+ task_list_id: nullableIdField,
268
+ task_prefix: field(["string", "null"], "Optional task prefix.", true),
269
+ task_counter: field("integer", "Monotonic project task counter."),
270
+ created_at: isoDateField,
271
+ updated_at: isoDateField
272
+ },
273
+ optional: {
274
+ sources: field("array", "Optional project source records.")
275
+ }
276
+ }),
277
+ contract({
278
+ id: "agent",
279
+ name: "Agent",
280
+ description: "Registered agent identity and coordination metadata.",
281
+ surfaces: ["cli", "api", "mcp", "sdk"],
282
+ stability: "stable",
283
+ required: {
284
+ id: idField,
285
+ name: field("string", "Normalized agent name."),
286
+ description: field(["string", "null"], "Optional agent description.", true),
287
+ role: field(["string", "null"], "Agent role.", true),
288
+ permissions: field("array", "Permission labels."),
289
+ capabilities: field("array", "Capability labels."),
290
+ status: field("string", "Agent lifecycle status."),
291
+ created_at: isoDateField,
292
+ last_seen_at: isoDateField
293
+ },
294
+ optional: {
295
+ session_id: nullableIdField,
296
+ working_dir: field(["string", "null"], "Current working directory.", true),
297
+ active_project_id: nullableIdField
298
+ }
299
+ }),
300
+ contract({
301
+ id: "template",
302
+ name: "Task Template",
303
+ description: "Task template object used to create repeatable task plans.",
304
+ surfaces: ["cli", "api", "mcp", "sdk"],
305
+ stability: "stable",
306
+ required: {
307
+ id: idField,
308
+ name: field("string", "Template name."),
309
+ title_pattern: field("string", "Task title pattern."),
310
+ description: field(["string", "null"], "Optional template description.", true),
311
+ priority: field("string", "Default task priority."),
312
+ tags: tagsField,
313
+ variables: field("array", "Template variable definitions."),
314
+ version: field("integer", "Template version."),
315
+ project_id: nullableIdField,
316
+ plan_id: nullableIdField,
317
+ metadata: metadataField,
318
+ created_at: isoDateField
319
+ },
320
+ optional: {
321
+ tasks: field("array", "Expanded template task steps.")
322
+ }
323
+ }),
324
+ contract({
325
+ id: "task_list",
326
+ name: "Task List",
327
+ description: "Named task list, optionally scoped to a project.",
328
+ surfaces: ["cli", "api", "mcp", "sdk"],
329
+ stability: "stable",
330
+ required: {
331
+ id: idField,
332
+ project_id: nullableIdField,
333
+ slug: field("string", "Stable URL/CLI-safe list slug."),
334
+ name: field("string", "Task list display name."),
335
+ description: field(["string", "null"], "Optional list description.", true),
336
+ metadata: metadataField,
337
+ created_at: isoDateField,
338
+ updated_at: isoDateField
339
+ },
340
+ optional: {}
341
+ }),
342
+ contract({
343
+ id: "comment",
344
+ name: "Task Comment",
345
+ description: "Comment, progress update, or note attached to a task.",
346
+ surfaces: ["cli", "api", "mcp", "sdk"],
347
+ stability: "stable",
348
+ required: {
349
+ id: idField,
350
+ task_id: idField,
351
+ agent_id: nullableIdField,
352
+ session_id: nullableIdField,
353
+ content: field("string", "Comment body."),
354
+ type: field("string", "Comment type: comment, progress, or note."),
355
+ progress_pct: field(["integer", "number", "null"], "Progress percentage for progress entries.", true),
356
+ created_at: isoDateField
357
+ },
358
+ optional: {}
359
+ }),
360
+ contract({
361
+ id: "checkpoint",
362
+ name: "Task Run Checkpoint",
363
+ description: "Task runner checkpoint for a named execution step.",
364
+ surfaces: ["api", "mcp", "sdk"],
365
+ stability: "stable",
366
+ required: {
367
+ id: idField,
368
+ task_id: idField,
369
+ agent_id: nullableIdField,
370
+ step: field("string", "Runner step name."),
371
+ status: field("string", "Checkpoint execution status."),
372
+ data: metadataField,
373
+ error: field(["string", "null"], "Step error message when failed.", true),
374
+ attempt: field("integer", "Current attempt number."),
375
+ max_attempts: field("integer", "Maximum allowed attempts."),
376
+ started_at: nullableIsoDateField,
377
+ completed_at: nullableIsoDateField,
378
+ created_at: isoDateField,
379
+ updated_at: isoDateField
380
+ },
381
+ optional: {}
382
+ }),
383
+ contract({
384
+ id: "dispatch",
385
+ name: "Dispatch",
386
+ description: "Queued or completed tmux dispatch/run request.",
387
+ surfaces: ["cli", "mcp", "sdk"],
388
+ stability: "stable",
389
+ required: {
390
+ id: idField,
391
+ title: field(["string", "null"], "Optional dispatch title.", true),
392
+ target_window: field("string", "tmux target window."),
393
+ task_ids: field("array", "Task IDs included in the dispatch."),
394
+ task_list_id: nullableIdField,
395
+ message: field(["string", "null"], "Preformatted message, or null when generated from tasks.", true),
396
+ delay_ms: field(["integer", "null"], "Delay between send and Enter.", true),
397
+ scheduled_at: nullableIsoDateField,
398
+ status: field("string", "Dispatch status."),
399
+ error: field(["string", "null"], "Dispatch error when failed.", true),
400
+ created_at: isoDateField,
401
+ sent_at: nullableIsoDateField
402
+ },
403
+ optional: {}
404
+ }),
405
+ contract({
406
+ id: "audit_history",
407
+ name: "Audit History",
408
+ description: "Audit log entry for a task mutation.",
409
+ surfaces: ["cli", "api", "mcp", "sdk"],
410
+ stability: "stable",
411
+ required: {
412
+ id: idField,
413
+ task_id: idField,
414
+ action: field("string", "Mutation action name."),
415
+ field: field(["string", "null"], "Changed field name.", true),
416
+ old_value: field(["string", "null"], "Previous value serialized as a string.", true),
417
+ new_value: field(["string", "null"], "New value serialized as a string.", true),
418
+ agent_id: nullableIdField,
419
+ created_at: isoDateField
420
+ },
421
+ optional: {}
422
+ }),
423
+ contract({
424
+ id: "status_summary",
425
+ name: "Status Summary",
426
+ description: "Queue status counts and lightweight next/active work summary.",
427
+ surfaces: ["cli", "api", "mcp", "sdk"],
428
+ stability: "stable",
429
+ required: {
430
+ pending: field("integer", "Pending task count."),
431
+ in_progress: field("integer", "In-progress task count."),
432
+ completed: field("integer", "Completed task count."),
433
+ total: field("integer", "Total task count for the selected scope."),
434
+ active_work: field("array", "Lightweight active work items."),
435
+ next_task: field(["object", "null"], "Next available task, or null when none is available.", true),
436
+ stale_count: field("integer", "Count of stale in-progress tasks."),
437
+ overdue_recurring: field("integer", "Count of overdue recurring tasks.")
438
+ },
439
+ optional: {
440
+ blocked_tasks: field("array", "Optional blocked task explanation list.")
441
+ }
442
+ }),
443
+ contract({
444
+ id: "structured_error",
445
+ name: "Structured Error",
446
+ description: "Structured MCP/SDK-style error response with a stable machine code.",
447
+ surfaces: ["mcp", "sdk"],
448
+ stability: "stable",
449
+ required: {
450
+ code: field("string", "Stable machine-readable error code."),
451
+ message: field("string", "Human-readable error message.")
452
+ },
453
+ optional: {
454
+ suggestion: field("string", "Optional recovery suggestion."),
455
+ retryAfterSeconds: field("integer", "Optional retry delay for cooldown errors.")
456
+ }
457
+ }),
458
+ contract({
459
+ id: "api_error",
460
+ name: "API Error",
461
+ description: "HTTP API and CLI JSON error object for simple error responses.",
462
+ surfaces: ["cli", "api"],
463
+ stability: "stable",
464
+ required: {
465
+ error: field("string", "Human-readable error message.")
466
+ },
467
+ optional: {
468
+ code: field("string", "Optional machine-readable error code."),
469
+ conflict: field("boolean", "Optional conflict flag for registration errors."),
470
+ suggestions: field("array", "Optional recovery suggestions."),
471
+ retry_after: field("integer", "Optional retry delay for rate-limited responses.")
472
+ }
473
+ })
474
+ ];
475
+ function expectedTypes(contract2) {
476
+ const types = Array.isArray(contract2.type) ? [...contract2.type] : [contract2.type];
477
+ if (contract2.nullable && !types.includes("null"))
478
+ return [...types, "null"];
479
+ return types;
480
+ }
481
+ function actualType(value) {
482
+ if (value === null)
483
+ return "null";
484
+ if (Array.isArray(value))
485
+ return "array";
486
+ if (typeof value === "number" && Number.isInteger(value))
487
+ return "integer";
488
+ if (typeof value === "number")
489
+ return "number";
490
+ if (typeof value === "boolean")
491
+ return "boolean";
492
+ if (typeof value === "string")
493
+ return "string";
494
+ if (typeof value === "object")
495
+ return "object";
496
+ return "string";
497
+ }
498
+ function matchesType(value, expected) {
499
+ const actual = actualType(value);
500
+ if (expected.includes(actual))
501
+ return true;
502
+ return actual === "integer" && expected.includes("number");
503
+ }
504
+ function getJsonContract(contractId) {
505
+ return TODOS_JSON_CONTRACTS.find((contractItem) => contractItem.id === contractId) ?? null;
506
+ }
507
+ function validateJsonContract(contractId, value) {
508
+ const contractItem = getJsonContract(contractId);
509
+ if (!contractItem) {
510
+ throw new Error(`Unknown JSON contract: ${contractId}`);
511
+ }
512
+ const record = value && typeof value === "object" && !Array.isArray(value) ? value : {};
513
+ const missingRequired = [];
514
+ const typeMismatches = [];
515
+ for (const [fieldName, fieldContract] of Object.entries(contractItem.required)) {
516
+ if (!(fieldName in record)) {
517
+ missingRequired.push(fieldName);
518
+ continue;
519
+ }
520
+ const expected = expectedTypes(fieldContract);
521
+ const valueType = actualType(record[fieldName]);
522
+ if (!matchesType(record[fieldName], expected)) {
523
+ typeMismatches.push({ field: fieldName, expected, actual: valueType });
524
+ }
525
+ }
526
+ return {
527
+ ok: missingRequired.length === 0 && typeMismatches.length === 0,
528
+ contractId,
529
+ missingRequired,
530
+ typeMismatches
531
+ };
532
+ }
533
+ function createJsonContractsManifest(options = {}) {
534
+ const version = options.version ?? getPackageVersion(import.meta.url);
535
+ return {
536
+ schemaVersion: 1,
537
+ generatedAt: options.generatedAt ?? new Date().toISOString(),
538
+ package: source(version),
539
+ contracts: TODOS_JSON_CONTRACTS
540
+ };
541
+ }
542
+ var TODOS_JSON_CONTRACTS_MANIFEST = createJsonContractsManifest({
543
+ generatedAt: "1970-01-01T00:00:00.000Z"
544
+ });
545
+
546
+ // src/contracts.ts
547
+ init_types();
548
+ var objectSchema = {
549
+ type: "object",
550
+ additionalProperties: true
551
+ };
552
+ var taskSchema = {
553
+ type: "object",
554
+ properties: {
555
+ id: { type: "string" },
556
+ title: { type: "string" },
557
+ status: { type: "string", enum: TASK_STATUSES },
558
+ priority: { type: "string", enum: TASK_PRIORITIES }
559
+ },
560
+ required: ["id", "title", "status", "priority"],
561
+ additionalProperties: true
562
+ };
563
+ var taskArraySchema = {
564
+ type: "array",
565
+ items: taskSchema
566
+ };
567
+ var TODOS_API_ROUTES = [
568
+ {
569
+ id: "health.read",
570
+ method: "GET",
571
+ path: "/api/health",
572
+ description: "Read local server health and stale task counts.",
573
+ auth: "optional-api-key",
574
+ requestSchema: null,
575
+ responseSchema: objectSchema,
576
+ tags: ["server", "health"],
577
+ stability: "stable"
578
+ },
579
+ {
580
+ id: "tasks.list",
581
+ method: "GET",
582
+ path: "/api/tasks",
583
+ description: "List tasks with query filters for status, project, session, agent, limit, and offset.",
584
+ auth: "optional-api-key",
585
+ requestSchema: null,
586
+ responseSchema: taskArraySchema,
587
+ tags: ["tasks", "query"],
588
+ stability: "stable"
589
+ },
590
+ {
591
+ id: "tasks.create",
592
+ method: "POST",
593
+ path: "/api/tasks",
594
+ description: "Create a task with a title, optional description, priority, and project.",
595
+ auth: "optional-api-key",
596
+ requestSchema: {
597
+ type: "object",
598
+ properties: {
599
+ title: { type: "string" },
600
+ description: { type: "string" },
601
+ priority: { type: "string", enum: TASK_PRIORITIES },
602
+ project_id: { type: "string" }
603
+ },
604
+ required: ["title"],
605
+ additionalProperties: false
606
+ },
607
+ responseSchema: taskSchema,
608
+ tags: ["tasks", "mutation"],
609
+ stability: "stable"
610
+ },
611
+ {
612
+ id: "tasks.read",
613
+ method: "GET",
614
+ path: "/api/tasks/:id",
615
+ description: "Read a single task by full or partial id.",
616
+ auth: "optional-api-key",
617
+ requestSchema: null,
618
+ responseSchema: taskSchema,
619
+ tags: ["tasks", "query"],
620
+ stability: "stable"
621
+ },
622
+ {
623
+ id: "tasks.update",
624
+ method: "PATCH",
625
+ path: "/api/tasks/:id",
626
+ description: "Update a task with optimistic locking through the current version.",
627
+ auth: "optional-api-key",
628
+ requestSchema: objectSchema,
629
+ responseSchema: taskSchema,
630
+ tags: ["tasks", "mutation"],
631
+ stability: "stable"
632
+ },
633
+ {
634
+ id: "tasks.complete",
635
+ method: "POST",
636
+ path: "/api/tasks/:id/complete",
637
+ description: "Mark a task complete and return its updated summary.",
638
+ auth: "optional-api-key",
639
+ requestSchema: null,
640
+ responseSchema: taskSchema,
641
+ tags: ["tasks", "workflow"],
642
+ stability: "stable"
643
+ },
644
+ {
645
+ id: "tasks.claim",
646
+ method: "POST",
647
+ path: "/api/tasks/claim",
648
+ description: "Atomically claim the next available task for an agent.",
649
+ auth: "optional-api-key",
650
+ requestSchema: {
651
+ type: "object",
652
+ properties: {
653
+ agent_id: { type: "string" },
654
+ project_id: { type: "string" }
655
+ },
656
+ additionalProperties: false
657
+ },
658
+ responseSchema: objectSchema,
659
+ tags: ["tasks", "agents", "workflow"],
660
+ stability: "stable"
661
+ },
662
+ {
663
+ id: "agents.register",
664
+ method: "POST",
665
+ path: "/api/agents",
666
+ description: "Register an agent for local task coordination.",
667
+ auth: "optional-api-key",
668
+ requestSchema: {
669
+ type: "object",
670
+ properties: {
671
+ name: { type: "string" },
672
+ description: { type: "string" }
673
+ },
674
+ required: ["name"],
675
+ additionalProperties: true
676
+ },
677
+ responseSchema: objectSchema,
678
+ tags: ["agents"],
679
+ stability: "stable"
680
+ },
681
+ {
682
+ id: "plans.create",
683
+ method: "POST",
684
+ path: "/api/plans",
685
+ description: "Create a plan for grouping related task work.",
686
+ auth: "optional-api-key",
687
+ requestSchema: {
688
+ type: "object",
689
+ properties: {
690
+ name: { type: "string" },
691
+ description: { type: "string" },
692
+ project_id: { type: "string" },
693
+ task_list_id: { type: "string" },
694
+ agent_id: { type: "string" },
695
+ status: { type: "string", enum: PLAN_STATUSES }
696
+ },
697
+ required: ["name"],
698
+ additionalProperties: false
699
+ },
700
+ responseSchema: objectSchema,
701
+ tags: ["plans"],
702
+ stability: "stable"
703
+ },
704
+ {
705
+ id: "streams.tasks",
706
+ method: "GET",
707
+ path: "/api/tasks/stream",
708
+ description: "Subscribe to server-sent task events with optional agent, project, and event filters.",
709
+ auth: "optional-api-key",
710
+ requestSchema: null,
711
+ responseSchema: {
712
+ type: "object",
713
+ properties: {
714
+ type: { type: "string" },
715
+ timestamp: { type: "string" }
716
+ },
717
+ additionalProperties: true
718
+ },
719
+ tags: ["tasks", "events"],
720
+ stability: "experimental"
721
+ }
722
+ ];
723
+ var TODOS_ERROR_CODES = [
724
+ {
725
+ code: VersionConflictError.code,
726
+ name: "VersionConflictError",
727
+ suggestion: VersionConflictError.suggestion,
728
+ httpStatus: 409
729
+ },
730
+ {
731
+ code: TaskNotFoundError.code,
732
+ name: "TaskNotFoundError",
733
+ suggestion: TaskNotFoundError.suggestion,
734
+ httpStatus: 404
735
+ },
736
+ {
737
+ code: ProjectNotFoundError.code,
738
+ name: "ProjectNotFoundError",
739
+ suggestion: ProjectNotFoundError.suggestion,
740
+ httpStatus: 404
741
+ },
742
+ {
743
+ code: PlanNotFoundError.code,
744
+ name: "PlanNotFoundError",
745
+ suggestion: PlanNotFoundError.suggestion,
746
+ httpStatus: 404
747
+ },
748
+ {
749
+ code: LockError.code,
750
+ name: "LockError",
751
+ suggestion: LockError.suggestion,
752
+ httpStatus: 409
753
+ },
754
+ {
755
+ code: AgentNotFoundError.code,
756
+ name: "AgentNotFoundError",
757
+ suggestion: AgentNotFoundError.suggestion,
758
+ httpStatus: 404
759
+ },
760
+ {
761
+ code: TaskListNotFoundError.code,
762
+ name: "TaskListNotFoundError",
763
+ suggestion: TaskListNotFoundError.suggestion,
764
+ httpStatus: 404
765
+ },
766
+ {
767
+ code: DependencyCycleError.code,
768
+ name: "DependencyCycleError",
769
+ suggestion: DependencyCycleError.suggestion,
770
+ httpStatus: 409
771
+ },
772
+ {
773
+ code: CompletionGuardError.code,
774
+ name: "CompletionGuardError",
775
+ suggestion: CompletionGuardError.suggestion,
776
+ httpStatus: 409
777
+ },
778
+ {
779
+ code: DispatchNotFoundError.code,
780
+ name: "DispatchNotFoundError",
781
+ suggestion: DispatchNotFoundError.suggestion,
782
+ httpStatus: 404
783
+ }
784
+ ];
785
+ function source2(version) {
786
+ return {
787
+ packageName: "@hasna/todos",
788
+ repository: "hasna/todos",
789
+ version
790
+ };
791
+ }
792
+ function createContractsManifest(options = {}) {
793
+ const version = options.version ?? getPackageVersion(import.meta.url);
794
+ const generatedAt = options.generatedAt ?? new Date().toISOString();
795
+ return {
796
+ schemaVersion: 1,
797
+ generatedAt,
798
+ package: source2(version),
799
+ values: {
800
+ taskStatuses: TASK_STATUSES,
801
+ taskPriorities: TASK_PRIORITIES,
802
+ planStatuses: PLAN_STATUSES,
803
+ dispatchStatuses: DISPATCH_STATUSES
804
+ },
805
+ apiRoutes: TODOS_API_ROUTES,
806
+ errorCodes: TODOS_ERROR_CODES,
807
+ jsonOutputs: createJsonContractsManifest({ version, generatedAt })
808
+ };
809
+ }
810
+ var TODOS_CONTRACTS = createContractsManifest({
811
+ generatedAt: "1970-01-01T00:00:00.000Z"
812
+ });
813
+ export {
814
+ validateJsonContract,
815
+ getJsonContract,
816
+ createJsonContractsManifest,
817
+ createContractsManifest,
818
+ TODOS_JSON_CONTRACTS_MANIFEST,
819
+ TODOS_JSON_CONTRACTS,
820
+ TODOS_ERROR_CODES,
821
+ TODOS_CONTRACTS,
822
+ TODOS_API_ROUTES
823
+ };