zigrix 0.1.1 → 0.2.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.
Files changed (48) hide show
  1. package/dist/config/defaults.d.ts +8 -0
  2. package/dist/config/defaults.js +8 -1
  3. package/dist/config/schema.d.ts +112 -0
  4. package/dist/config/schema.js +186 -12
  5. package/dist/dashboard/.next/BUILD_ID +1 -1
  6. package/dist/dashboard/.next/app-build-manifest.json +10 -10
  7. package/dist/dashboard/.next/app-path-routes-manifest.json +2 -2
  8. package/dist/dashboard/.next/build-manifest.json +2 -2
  9. package/dist/dashboard/.next/prerender-manifest.json +6 -6
  10. package/dist/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/dist/dashboard/.next/server/app/_not-found.html +1 -1
  12. package/dist/dashboard/.next/server/app/_not-found.rsc +1 -1
  13. package/dist/dashboard/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  14. package/dist/dashboard/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  15. package/dist/dashboard/.next/server/app/api/auth/session/route_client-reference-manifest.js +1 -1
  16. package/dist/dashboard/.next/server/app/api/auth/setup/route_client-reference-manifest.js +1 -1
  17. package/dist/dashboard/.next/server/app/api/overview/route_client-reference-manifest.js +1 -1
  18. package/dist/dashboard/.next/server/app/api/stream/route_client-reference-manifest.js +1 -1
  19. package/dist/dashboard/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  20. package/dist/dashboard/.next/server/app/api/tasks/[taskId]/conversation/route_client-reference-manifest.js +1 -1
  21. package/dist/dashboard/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  22. package/dist/dashboard/.next/server/app/login/page_client-reference-manifest.js +1 -1
  23. package/dist/dashboard/.next/server/app/login.html +1 -1
  24. package/dist/dashboard/.next/server/app/login.rsc +1 -1
  25. package/dist/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  26. package/dist/dashboard/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  27. package/dist/dashboard/.next/server/app/setup.html +1 -1
  28. package/dist/dashboard/.next/server/app/setup.rsc +1 -1
  29. package/dist/dashboard/.next/server/app-paths-manifest.json +2 -2
  30. package/dist/dashboard/.next/server/functions-config-manifest.json +2 -2
  31. package/dist/dashboard/.next/server/pages/404.html +1 -1
  32. package/dist/dashboard/.next/server/pages/500.html +1 -1
  33. package/dist/doctor.d.ts +3 -0
  34. package/dist/doctor.js +233 -60
  35. package/dist/index.js +262 -32
  36. package/dist/migrate/import-orchestration.d.ts +31 -0
  37. package/dist/migrate/import-orchestration.js +638 -0
  38. package/dist/onboard.js +130 -35
  39. package/dist/orchestration/evidence.d.ts +7 -0
  40. package/dist/orchestration/evidence.js +79 -4
  41. package/dist/orchestration/pipeline.d.ts +1 -0
  42. package/dist/orchestration/pipeline.js +26 -1
  43. package/dist/state/tasks.d.ts +37 -2
  44. package/dist/state/tasks.js +242 -10
  45. package/dist/state/verify.js +89 -11
  46. package/package.json +1 -1
  47. /package/dist/dashboard/.next/static/{iKGx5hWe1zbwJZWchF9kg → EZjkAnODdTglaMXuBw76E}/_buildManifest.js +0 -0
  48. /package/dist/dashboard/.next/static/{iKGx5hWe1zbwJZWchF9kg → EZjkAnODdTglaMXuBw76E}/_ssgManifest.js +0 -0
@@ -189,15 +189,226 @@ export function recordTaskProgress(paths, params) {
189
189
  rebuildIndex(paths);
190
190
  return event;
191
191
  }
192
- // ─── Stale policy ───────────────────────────────────────────────────────────
193
- export function findStaleTasks(paths, hours = 24) {
192
+ function toNonEmptyString(value) {
193
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
194
+ }
195
+ function parseAgentSubagentSessionKey(sessionKey) {
196
+ const matched = sessionKey.match(/^agent:([^:]+):subagent:([^:\s]+)$/);
197
+ if (!matched)
198
+ return null;
199
+ return { agentId: matched[1], sessionId: matched[2] };
200
+ }
201
+ function resolveSessionIdFromSessionsJson(agentsStateDir, agentId, sessionKey) {
202
+ try {
203
+ const sessionsJsonPath = path.join(agentsStateDir, agentId, 'sessions', 'sessions.json');
204
+ const raw = JSON.parse(fs.readFileSync(sessionsJsonPath, 'utf8'));
205
+ const entry = raw[sessionKey];
206
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
207
+ return null;
208
+ return toNonEmptyString(entry.sessionId);
209
+ }
210
+ catch {
211
+ return null;
212
+ }
213
+ }
214
+ function listDeletedSessionPaths(agentsStateDir, agentId, sessionId) {
215
+ const sessionsDir = path.join(agentsStateDir, agentId, 'sessions');
216
+ if (!fs.existsSync(sessionsDir))
217
+ return [];
218
+ const prefix = `${sessionId}.jsonl.deleted.`;
219
+ try {
220
+ return fs.readdirSync(sessionsDir)
221
+ .filter((name) => name.startsWith(prefix))
222
+ .sort((a, b) => b.localeCompare(a))
223
+ .map((name) => path.join(sessionsDir, name));
224
+ }
225
+ catch {
226
+ return [];
227
+ }
228
+ }
229
+ function collectTaskSessionRefs(task) {
230
+ const refs = [];
231
+ if (task.orchestratorSessionKey && task.orchestratorId) {
232
+ refs.push({
233
+ scope: 'orchestrator',
234
+ agentId: task.orchestratorId,
235
+ sessionKey: task.orchestratorSessionKey,
236
+ sessionId: task.orchestratorSessionId ?? null,
237
+ });
238
+ }
239
+ for (const [agentId, raw] of Object.entries(task.workerSessions ?? {})) {
240
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
241
+ continue;
242
+ const data = raw;
243
+ const sessionKey = toNonEmptyString(data.sessionKey);
244
+ if (!sessionKey)
245
+ continue;
246
+ refs.push({
247
+ scope: 'worker',
248
+ agentId,
249
+ sessionKey,
250
+ sessionId: toNonEmptyString(data.sessionId),
251
+ });
252
+ }
253
+ return refs;
254
+ }
255
+ function diagnoseTaskSessions(task, agentsStateDir) {
256
+ if (!agentsStateDir)
257
+ return [];
258
+ return collectTaskSessionRefs(task).map((ref) => {
259
+ const parsed = parseAgentSubagentSessionKey(ref.sessionKey);
260
+ const agentId = parsed?.agentId ?? ref.agentId;
261
+ let resolvedSessionId = ref.sessionId;
262
+ let mappingSource = ref.sessionId ? 'explicit' : 'none';
263
+ if (!resolvedSessionId && parsed) {
264
+ resolvedSessionId = parsed.sessionId;
265
+ mappingSource = 'parsed';
266
+ }
267
+ if (!resolvedSessionId) {
268
+ const mappedSessionId = resolveSessionIdFromSessionsJson(agentsStateDir, agentId, ref.sessionKey);
269
+ if (mappedSessionId) {
270
+ resolvedSessionId = mappedSessionId;
271
+ mappingSource = 'sessions_json';
272
+ }
273
+ }
274
+ if (!resolvedSessionId) {
275
+ return {
276
+ scope: ref.scope,
277
+ agentId,
278
+ sessionKey: ref.sessionKey,
279
+ sessionId: null,
280
+ mappingSource,
281
+ state: 'missing',
282
+ reason: 'missing_session_mapping',
283
+ activePath: null,
284
+ deletedPath: null,
285
+ };
286
+ }
287
+ const activePath = path.join(agentsStateDir, agentId, 'sessions', `${resolvedSessionId}.jsonl`);
288
+ if (fs.existsSync(activePath)) {
289
+ return {
290
+ scope: ref.scope,
291
+ agentId,
292
+ sessionKey: ref.sessionKey,
293
+ sessionId: resolvedSessionId,
294
+ mappingSource,
295
+ state: 'active',
296
+ reason: null,
297
+ activePath,
298
+ deletedPath: null,
299
+ };
300
+ }
301
+ const deletedPaths = listDeletedSessionPaths(agentsStateDir, agentId, resolvedSessionId);
302
+ if (deletedPaths.length > 0) {
303
+ return {
304
+ scope: ref.scope,
305
+ agentId,
306
+ sessionKey: ref.sessionKey,
307
+ sessionId: resolvedSessionId,
308
+ mappingSource,
309
+ state: 'deleted',
310
+ reason: 'session_dead',
311
+ activePath: null,
312
+ deletedPath: deletedPaths[0],
313
+ };
314
+ }
315
+ return {
316
+ scope: ref.scope,
317
+ agentId,
318
+ sessionKey: ref.sessionKey,
319
+ sessionId: resolvedSessionId,
320
+ mappingSource,
321
+ state: 'missing',
322
+ reason: null,
323
+ activePath: null,
324
+ deletedPath: null,
325
+ };
326
+ });
327
+ }
328
+ function joinUniqueAgentIds(diagnoses, reason) {
329
+ const agentIds = Array.from(new Set(diagnoses.filter((item) => item.reason === reason).map((item) => item.agentId)));
330
+ return agentIds.join(', ') || 'unknown-agent';
331
+ }
332
+ function buildStaleGuidance(task, reasonCode, hours, diagnoses) {
333
+ if (reasonCode === 'session_dead') {
334
+ const agents = joinUniqueAgentIds(diagnoses, 'session_dead');
335
+ return {
336
+ reason: 'session_dead',
337
+ nextAction: `respawn the deleted OpenClaw session for ${agents} and re-register it before resuming ${task.taskId}`,
338
+ resumeHint: `start a fresh session for ${agents}, then update zigrix with the new session key/sessionId before continuing the blocked task`,
339
+ reportLine: `${task.taskId}: BLOCKED session_dead (${agents})`,
340
+ };
341
+ }
342
+ if (reasonCode === 'missing_session_mapping') {
343
+ const agents = joinUniqueAgentIds(diagnoses, 'missing_session_mapping');
344
+ return {
345
+ reason: 'missing_session_mapping',
346
+ nextAction: `repair or re-register the missing session mapping for ${agents} before resuming ${task.taskId}`,
347
+ resumeHint: `re-run worker/orchestrator registration with --session-key and --session-id so zigrix can resolve the backing OpenClaw session`,
348
+ reportLine: `${task.taskId}: BLOCKED missing_session_mapping (${agents})`,
349
+ };
350
+ }
351
+ return {
352
+ reason: 'stale_timeout',
353
+ nextAction: `inspect the latest progress for ${task.taskId} and either report, refresh progress, or respawn the worker after ${hours}h of inactivity`,
354
+ resumeHint: 'check task status/events/evidence, then continue with a fresh worker registration if the original session is no longer active',
355
+ reportLine: `${task.taskId}: BLOCKED stale_timeout (> ${hours}h)`,
356
+ };
357
+ }
358
+ function buildStaleTaskSummary(task, hours, agentsStateDir, fallbackReason = 'stale_timeout') {
194
359
  const cutoff = Date.now() - hours * 3600 * 1000;
195
- return listTasks(paths).filter((task) => task.status === 'IN_PROGRESS' && Date.parse(task.updatedAt) < cutoff);
360
+ const timedOut = Date.parse(task.updatedAt) < cutoff;
361
+ const diagnoses = diagnoseTaskSessions(task, agentsStateDir);
362
+ const hasDeletedSession = diagnoses.some((item) => item.reason === 'session_dead');
363
+ const hasMissingMapping = diagnoses.some((item) => item.reason === 'missing_session_mapping');
364
+ if (!hasDeletedSession && !timedOut)
365
+ return null;
366
+ const reasons = new Set();
367
+ if (timedOut)
368
+ reasons.add('stale_timeout');
369
+ if (hasDeletedSession)
370
+ reasons.add('session_dead');
371
+ if (hasMissingMapping)
372
+ reasons.add('missing_session_mapping');
373
+ const reasonCode = hasDeletedSession
374
+ ? 'session_dead'
375
+ : hasMissingMapping && timedOut
376
+ ? 'missing_session_mapping'
377
+ : 'stale_timeout';
378
+ const guidance = buildStaleGuidance(task, reasonCode, hours, diagnoses);
379
+ const reason = reasonCode === 'stale_timeout' ? fallbackReason : guidance.reason;
380
+ return {
381
+ taskId: task.taskId,
382
+ title: task.title,
383
+ updatedAt: task.updatedAt,
384
+ hoursThreshold: hours,
385
+ timedOut,
386
+ reason,
387
+ reasonCode,
388
+ reasons: Array.from(reasons),
389
+ nextAction: guidance.nextAction,
390
+ resumeHint: guidance.resumeHint,
391
+ reportLine: guidance.reportLine,
392
+ sessions: diagnoses,
393
+ };
394
+ }
395
+ export function findStaleTasks(paths, hours = 24, options = {}) {
396
+ return listTasks(paths)
397
+ .filter((task) => task.status === 'IN_PROGRESS')
398
+ .map((task) => buildStaleTaskSummary(task, hours, options.agentsStateDir, options.fallbackReason ?? 'stale_timeout'))
399
+ .filter((task) => task !== null);
196
400
  }
197
- export function applyStalePolicy(paths, hours = 24, reason = 'stale_timeout') {
198
- const staleTasks = findStaleTasks(paths, hours);
199
- const changed = staleTasks.map((task) => {
401
+ export function applyStalePolicy(paths, hours = 24, reason = 'stale_timeout', options = {}) {
402
+ const staleTasks = findStaleTasks(paths, hours, { ...options, fallbackReason: reason });
403
+ const changed = staleTasks.map((summary) => {
404
+ const task = loadTask(paths, summary.taskId);
405
+ if (!task)
406
+ return null;
200
407
  task.status = 'BLOCKED';
408
+ task.nextAction = summary.nextAction;
409
+ task.resumeHint = summary.resumeHint;
410
+ task.staleReason = summary.reasonCode;
411
+ task.staleReasons = summary.reasons;
201
412
  saveTask(paths, task);
202
413
  const event = appendEvent(paths.eventsFile, {
203
414
  event: 'task_blocked',
@@ -205,12 +416,33 @@ export function applyStalePolicy(paths, hours = 24, reason = 'stale_timeout') {
205
416
  phase: 'recovery',
206
417
  actor: 'zigrix',
207
418
  status: 'BLOCKED',
208
- payload: { reason, previousStatus: 'IN_PROGRESS', hoursThreshold: hours },
419
+ payload: {
420
+ reason: summary.reason,
421
+ reasonCode: summary.reasonCode,
422
+ reasons: summary.reasons,
423
+ previousStatus: 'IN_PROGRESS',
424
+ hoursThreshold: hours,
425
+ timedOut: summary.timedOut,
426
+ nextAction: summary.nextAction,
427
+ resumeHint: summary.resumeHint,
428
+ reportLine: summary.reportLine,
429
+ sessions: summary.sessions,
430
+ },
209
431
  });
210
- return { taskId: task.taskId, event };
211
- });
432
+ return {
433
+ taskId: task.taskId,
434
+ reason: summary.reason,
435
+ reasonCode: summary.reasonCode,
436
+ reasons: summary.reasons,
437
+ nextAction: summary.nextAction,
438
+ resumeHint: summary.resumeHint,
439
+ reportLine: summary.reportLine,
440
+ sessions: summary.sessions,
441
+ event,
442
+ };
443
+ }).filter((item) => item !== null);
212
444
  rebuildIndex(paths);
213
- return { ok: true, hours, reason, count: changed.length, changed };
445
+ return { ok: true, hours, requestedReason: reason, count: changed.length, changed };
214
446
  }
215
447
  // ─── Index ──────────────────────────────────────────────────────────────────
216
448
  export function rebuildIndex(paths) {
@@ -9,6 +9,12 @@ function safeReadJson(filePath) {
9
9
  return null;
10
10
  }
11
11
  }
12
+ function sortedStrings(value) {
13
+ return Array.isArray(value) ? value.map(String).sort() : [];
14
+ }
15
+ function sameStringArray(left, right) {
16
+ return JSON.stringify([...left].sort()) === JSON.stringify([...right].sort());
17
+ }
12
18
  function verifyTask(paths, task) {
13
19
  const issues = [];
14
20
  const evidenceDir = path.join(paths.evidenceDir, task.taskId);
@@ -17,34 +23,58 @@ function verifyTask(paths, task) {
17
23
  const presentEvidenceAgents = fs.existsSync(evidenceDir)
18
24
  ? fs.readdirSync(evidenceDir).filter((name) => name.endsWith('.json') && name !== '_merged.json').map((name) => path.basename(name, '.json')).sort()
19
25
  : [];
20
- const requiredAgents = Array.isArray(task.requiredAgents) ? task.requiredAgents.map(String) : [];
26
+ const requiredAgents = Array.isArray(task.requiredAgents) ? task.requiredAgents.map(String).sort() : [];
21
27
  const workerAgents = Object.keys(task.workerSessions ?? {}).sort();
28
+ const requiredRoles = Array.isArray(task.requiredRoles) ? task.requiredRoles.map(String).sort() : [];
29
+ const roleAgentMap = task.roleAgentMap && typeof task.roleAgentMap === 'object'
30
+ ? task.roleAgentMap
31
+ : {};
22
32
  for (const agentId of requiredAgents) {
23
33
  if (!workerAgents.includes(agentId) && !presentEvidenceAgents.includes(agentId)) {
24
34
  issues.push(`required agent '${agentId}' has neither worker session nor evidence`);
25
35
  }
26
36
  }
37
+ for (const role of requiredRoles) {
38
+ const mappedAgents = Array.isArray(roleAgentMap[role]) ? roleAgentMap[role].map(String).filter(Boolean) : [];
39
+ if (mappedAgents.length === 0) {
40
+ issues.push(`required role '${role}' has no mapped agent in roleAgentMap`);
41
+ continue;
42
+ }
43
+ const hasActiveArtifact = mappedAgents.some((agentId) => requiredAgents.includes(agentId) || workerAgents.includes(agentId) || presentEvidenceAgents.includes(agentId));
44
+ if (!hasActiveArtifact) {
45
+ issues.push(`required role '${role}' has no required agent, worker session, or evidence`);
46
+ }
47
+ }
27
48
  if (merged) {
28
- const mergedPresent = Array.isArray(merged.presentAgents) ? merged.presentAgents.map(String).sort() : [];
29
- const mergedMissing = Array.isArray(merged.missingAgents) ? merged.missingAgents.map(String).sort() : [];
49
+ const mergedPresent = sortedStrings(merged.presentAgents);
50
+ const mergedMissing = sortedStrings(merged.missingAgents);
30
51
  const computedMissing = requiredAgents.filter((agentId) => !presentEvidenceAgents.includes(agentId)).sort();
31
- if (JSON.stringify(mergedPresent) !== JSON.stringify(presentEvidenceAgents)) {
52
+ if (!sameStringArray(mergedPresent, presentEvidenceAgents)) {
32
53
  issues.push('merged presentAgents does not match evidence files');
33
54
  }
34
- if (JSON.stringify(mergedMissing) !== JSON.stringify(computedMissing)) {
55
+ if (!sameStringArray(mergedMissing, computedMissing)) {
35
56
  issues.push('merged missingAgents does not match requiredAgents/evidence files');
36
57
  }
37
- if (task.status === 'REPORTED' && merged.complete !== true) {
38
- issues.push('task is REPORTED but merged evidence is incomplete');
58
+ const qaVerification = merged.qaVerification;
59
+ const qaVerificationComplete = Boolean(qaVerification &&
60
+ typeof qaVerification === 'object' &&
61
+ !Array.isArray(qaVerification) &&
62
+ qaVerification.complete === true);
63
+ if (['DONE_PENDING_REPORT', 'REPORTED'].includes(task.status) && merged.complete !== true) {
64
+ issues.push(`task is ${task.status} but merged evidence is incomplete`);
65
+ }
66
+ if (['DONE_PENDING_REPORT', 'REPORTED'].includes(task.status) && merged.qaPresent === true && !qaVerificationComplete) {
67
+ issues.push('task reached reporting state without DoD↔test verification mapping for QA evidence');
39
68
  }
40
69
  }
41
- else if (task.status === 'REPORTED') {
42
- issues.push('task is REPORTED but merged evidence is missing');
70
+ else if (['DONE_PENDING_REPORT', 'REPORTED'].includes(task.status)) {
71
+ issues.push(`task is ${task.status} but merged evidence is missing`);
43
72
  }
44
73
  return {
45
74
  taskId: task.taskId,
46
75
  status: task.status,
47
76
  requiredAgents,
77
+ requiredRoles,
48
78
  workerAgents,
49
79
  presentEvidenceAgents,
50
80
  mergedExists: Boolean(merged),
@@ -52,14 +82,62 @@ function verifyTask(paths, task) {
52
82
  issues,
53
83
  };
54
84
  }
85
+ function verifyIndex(paths, tasks) {
86
+ const issues = [];
87
+ const index = safeReadJson(paths.indexFile);
88
+ if (!index) {
89
+ issues.push('index.json is missing or unreadable');
90
+ return { ok: false, issues };
91
+ }
92
+ const counts = index.counts && typeof index.counts === 'object' ? index.counts : {};
93
+ const statusBuckets = index.statusBuckets && typeof index.statusBuckets === 'object'
94
+ ? index.statusBuckets
95
+ : {};
96
+ const taskSummaries = index.taskSummaries && typeof index.taskSummaries === 'object'
97
+ ? index.taskSummaries
98
+ : {};
99
+ const activeTasks = index.activeTasks && typeof index.activeTasks === 'object'
100
+ ? index.activeTasks
101
+ : {};
102
+ if (Number(counts.tasks ?? -1) !== tasks.length) {
103
+ issues.push(`index counts.tasks mismatch: expected ${tasks.length}, got ${String(counts.tasks ?? 'missing')}`);
104
+ }
105
+ const expectedBuckets = new Map();
106
+ for (const task of tasks) {
107
+ expectedBuckets.set(task.status, [...(expectedBuckets.get(task.status) ?? []), task.taskId]);
108
+ const summary = taskSummaries[task.taskId];
109
+ if (!summary || typeof summary !== 'object' || Array.isArray(summary)) {
110
+ issues.push(`taskSummaries missing entry for ${task.taskId}`);
111
+ continue;
112
+ }
113
+ if (summary.status !== task.status) {
114
+ issues.push(`taskSummaries status mismatch for ${task.taskId}`);
115
+ }
116
+ }
117
+ for (const [status, taskIds] of expectedBuckets.entries()) {
118
+ const indexed = sortedStrings(statusBuckets[status]);
119
+ if (!sameStringArray(indexed, taskIds)) {
120
+ issues.push(`statusBuckets mismatch for ${status}`);
121
+ }
122
+ }
123
+ const expectedActiveStatuses = new Set(['OPEN', 'IN_PROGRESS', 'BLOCKED', 'DONE_PENDING_REPORT']);
124
+ const expectedActiveTaskIds = tasks.filter((task) => expectedActiveStatuses.has(task.status)).map((task) => task.taskId).sort();
125
+ const indexedActiveTaskIds = Object.keys(activeTasks).sort();
126
+ if (!sameStringArray(indexedActiveTaskIds, expectedActiveTaskIds)) {
127
+ issues.push('activeTasks mismatch with task statuses');
128
+ }
129
+ return { ok: issues.length === 0, issues };
130
+ }
55
131
  export function verifyState(paths) {
56
132
  const tasks = listTasks(paths);
57
133
  const checks = tasks.map((task) => verifyTask(paths, task));
58
134
  const failures = checks.filter((item) => item.ok === false);
135
+ const indexCheck = verifyIndex(paths, tasks);
59
136
  return {
60
- ok: failures.length === 0,
137
+ ok: failures.length === 0 && indexCheck.ok === true,
61
138
  taskCount: tasks.length,
62
- failedCount: failures.length,
139
+ failedCount: failures.length + (indexCheck.ok === true ? 0 : 1),
63
140
  checks,
141
+ index: indexCheck,
64
142
  };
65
143
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zigrix",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Multi-project parallel task orchestration CLI for agent-assisted development",
6
6
  "license": "Apache-2.0",