zigrix 0.1.0-alpha.8 → 0.1.0-alpha.9
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/LICENSE +1 -1
- package/README.md +248 -112
- package/dist/agents/registry.js +5 -2
- package/dist/agents/roles.d.ts +10 -0
- package/dist/agents/roles.js +83 -0
- package/dist/config/defaults.d.ts +2 -1
- package/dist/config/defaults.js +2 -1
- package/dist/config/schema.d.ts +25 -3
- package/dist/config/schema.js +34 -2
- package/dist/configure.d.ts +1 -0
- package/dist/configure.js +14 -1
- package/dist/dashboard/.next/BUILD_ID +1 -1
- package/dist/dashboard/.next/app-build-manifest.json +14 -14
- package/dist/dashboard/.next/app-path-routes-manifest.json +5 -5
- package/dist/dashboard/.next/build-manifest.json +2 -2
- package/dist/dashboard/.next/prerender-manifest.json +7 -7
- package/dist/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/_not-found.html +1 -1
- package/dist/dashboard/.next/server/app/_not-found.rsc +1 -1
- package/dist/dashboard/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/auth/session/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/auth/setup/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/overview/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/stream/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/tasks/[taskId]/conversation/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/login.html +1 -1
- package/dist/dashboard/.next/server/app/login.rsc +1 -1
- package/dist/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/setup.html +1 -1
- package/dist/dashboard/.next/server/app/setup.rsc +1 -1
- package/dist/dashboard/.next/server/app-paths-manifest.json +5 -5
- package/dist/dashboard/.next/server/functions-config-manifest.json +4 -4
- package/dist/dashboard/.next/server/pages/404.html +1 -1
- package/dist/dashboard/.next/server/pages/500.html +1 -1
- package/dist/index.js +5 -1
- package/dist/onboard.d.ts +16 -2
- package/dist/onboard.js +128 -9
- package/dist/orchestration/dispatch.d.ts +2 -1
- package/dist/orchestration/dispatch.js +157 -41
- package/dist/orchestration/evidence.js +17 -3
- package/dist/orchestration/finalize.js +2 -2
- package/dist/orchestration/report.js +6 -1
- package/dist/orchestration/worker.d.ts +1 -1
- package/dist/orchestration/worker.js +17 -2
- package/dist/rules/templates.js +3 -6
- package/dist/state/tasks.d.ts +7 -0
- package/package.json +1 -1
- package/skills/zigrix-main-agent-guide/SKILL.md +118 -0
- /package/dist/dashboard/.next/static/{2a4glWei05xr4Jg0Ly6cp → TlUj0t8APzTccK13DVZZW}/_buildManifest.js +0 -0
- /package/dist/dashboard/.next/static/{2a4glWei05xr4Jg0Ly6cp → TlUj0t8APzTccK13DVZZW}/_ssgManifest.js +0 -0
|
@@ -1,18 +1,106 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { ROLE_HINTS } from '../agents/roles.js';
|
|
3
4
|
import { appendEvent } from '../state/events.js';
|
|
4
|
-
import { ensureBaseState } from '../state/paths.js';
|
|
5
5
|
import { createTask, rebuildIndex, saveTask } from '../state/tasks.js';
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
import { ensureBaseState } from '../state/paths.js';
|
|
7
|
+
const BASELINE_REQUIRED_ROLES = ['orchestrator', 'qa'];
|
|
8
|
+
const CANDIDATE_ROLE_ORDER = ['frontend', 'backend', 'system', 'security'];
|
|
9
|
+
function unique(items) {
|
|
10
|
+
return [...new Set(items)];
|
|
11
|
+
}
|
|
12
|
+
function listEligibleAgentsByRole(config) {
|
|
13
|
+
const byRole = new Map();
|
|
14
|
+
const participants = new Set(config.agents.orchestration.participants);
|
|
15
|
+
const excluded = new Set(config.agents.orchestration.excluded);
|
|
16
|
+
const participantMode = participants.size > 0;
|
|
17
|
+
for (const [agentId, agent] of Object.entries(config.agents.registry).sort(([a], [b]) => a.localeCompare(b))) {
|
|
18
|
+
if (!agent.enabled)
|
|
19
|
+
continue;
|
|
20
|
+
if (excluded.has(agentId))
|
|
21
|
+
continue;
|
|
22
|
+
if (participantMode && !participants.has(agentId))
|
|
23
|
+
continue;
|
|
24
|
+
const role = agent.role;
|
|
25
|
+
const row = byRole.get(role) ?? [];
|
|
26
|
+
row.push(agentId);
|
|
27
|
+
byRole.set(role, row);
|
|
28
|
+
}
|
|
29
|
+
return byRole;
|
|
30
|
+
}
|
|
31
|
+
function pickRequiredAgentByRole(params) {
|
|
32
|
+
if (params.role === 'orchestrator') {
|
|
33
|
+
if (params.roleAgents.length === 0) {
|
|
34
|
+
throw new Error('dispatch validation failed: no eligible agent for required role "orchestrator"');
|
|
35
|
+
}
|
|
36
|
+
if (!params.roleAgents.includes(params.orchestratorId)) {
|
|
37
|
+
throw new Error(`dispatch validation failed: configured orchestratorId '${params.orchestratorId}' is not eligible (available: ${params.roleAgents.join(', ')})`);
|
|
38
|
+
}
|
|
39
|
+
return params.orchestratorId;
|
|
40
|
+
}
|
|
41
|
+
const picked = params.roleAgents[0];
|
|
42
|
+
if (!picked) {
|
|
43
|
+
throw new Error(`dispatch validation failed: no eligible agent for required role "${params.role}"`);
|
|
44
|
+
}
|
|
45
|
+
return picked;
|
|
46
|
+
}
|
|
47
|
+
function resolveAgentSelection(config, scale) {
|
|
48
|
+
const scalePolicy = config.rules.scales[scale];
|
|
49
|
+
if (!scalePolicy) {
|
|
50
|
+
throw new Error(`unknown scale: ${scale}`);
|
|
51
|
+
}
|
|
52
|
+
const requiredRoles = unique([...BASELINE_REQUIRED_ROLES, ...scalePolicy.requiredRoles]);
|
|
53
|
+
const optionalRoles = unique(scalePolicy.optionalRoles.filter((role) => !requiredRoles.includes(role)));
|
|
54
|
+
const eligibleByRole = listEligibleAgentsByRole(config);
|
|
55
|
+
const requiredAgents = [];
|
|
56
|
+
const roleAgentMap = {};
|
|
57
|
+
for (const role of requiredRoles) {
|
|
58
|
+
const roleAgents = eligibleByRole.get(role) ?? [];
|
|
59
|
+
roleAgentMap[role] = [...roleAgents];
|
|
60
|
+
const picked = pickRequiredAgentByRole({
|
|
61
|
+
role,
|
|
62
|
+
roleAgents,
|
|
63
|
+
orchestratorId: config.agents.orchestration.orchestratorId,
|
|
64
|
+
});
|
|
65
|
+
requiredAgents.push(picked);
|
|
66
|
+
}
|
|
67
|
+
const candidateRoles = unique([...optionalRoles, ...CANDIDATE_ROLE_ORDER.filter((role) => !requiredRoles.includes(role))]);
|
|
68
|
+
const candidateAgents = [];
|
|
69
|
+
for (const role of candidateRoles) {
|
|
70
|
+
const roleAgents = eligibleByRole.get(role) ?? [];
|
|
71
|
+
roleAgentMap[role] = [...roleAgents];
|
|
72
|
+
for (const agentId of roleAgents) {
|
|
73
|
+
if (requiredAgents.includes(agentId))
|
|
74
|
+
continue;
|
|
75
|
+
if (!candidateAgents.includes(agentId))
|
|
76
|
+
candidateAgents.push(agentId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const selectionHints = {};
|
|
80
|
+
for (const [role, agentIds] of Object.entries(roleAgentMap)) {
|
|
81
|
+
const hint = ROLE_HINTS[role] ?? 'role-based selection';
|
|
82
|
+
for (const agentId of agentIds) {
|
|
83
|
+
selectionHints[agentId] = `${hint} (role: ${role})`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const qaAgentId = requiredAgents.find((agentId) => {
|
|
87
|
+
const role = config.agents.registry[agentId]?.role;
|
|
88
|
+
return role === 'qa';
|
|
89
|
+
});
|
|
90
|
+
if (!qaAgentId) {
|
|
91
|
+
throw new Error('dispatch validation failed: no selected QA agent');
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
requiredRoles,
|
|
95
|
+
optionalRoles,
|
|
96
|
+
requiredAgents,
|
|
97
|
+
candidateAgents,
|
|
98
|
+
roleAgentMap,
|
|
99
|
+
selectionHints,
|
|
100
|
+
orchestratorId: config.agents.orchestration.orchestratorId,
|
|
101
|
+
qaAgentId,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
16
104
|
function defaultWorkPackages(scale) {
|
|
17
105
|
return [
|
|
18
106
|
{ id: 'WP1', key: 'planning', title: 'planning', parallel: false },
|
|
@@ -21,33 +109,33 @@ function defaultWorkPackages(scale) {
|
|
|
21
109
|
{ id: 'WP4', key: 'release', title: 'release', parallel: false },
|
|
22
110
|
];
|
|
23
111
|
}
|
|
24
|
-
function defaultExecutionUnits(scale) {
|
|
112
|
+
function defaultExecutionUnits(scale, owners) {
|
|
25
113
|
if (['normal', 'risky', 'large'].includes(scale)) {
|
|
26
114
|
return [
|
|
27
|
-
{ id: 'U1', title: 'spec confirmation', kind: 'planning', owner:
|
|
28
|
-
{ id: 'U2', title: 'implementation planning / work package split', kind: 'planning', owner:
|
|
29
|
-
{ id: 'U3', title: 'implementation slices', kind: 'implementation', owner:
|
|
30
|
-
{ id: 'U4', title: 'qa / regression', kind: 'verification', owner:
|
|
31
|
-
{ id: 'U5', title: 'report / deploy / wrap-up', kind: 'reporting', owner:
|
|
115
|
+
{ id: 'U1', title: 'spec confirmation', kind: 'planning', owner: owners.orchestratorId, workPackage: 'planning', dependsOn: [], parallel: false, status: 'OPEN', dod: 'scope / constraints / edge cases fixed' },
|
|
116
|
+
{ id: 'U2', title: 'implementation planning / work package split', kind: 'planning', owner: owners.orchestratorId, workPackage: 'planning', dependsOn: ['U1'], parallel: false, status: 'OPEN', dod: 'execution units and work packages fixed' },
|
|
117
|
+
{ id: 'U3', title: 'implementation slices', kind: 'implementation', owner: owners.orchestratorId, workPackage: 'implementation', dependsOn: ['U2'], parallel: true, status: 'OPEN', dod: 'required work packages complete' },
|
|
118
|
+
{ id: 'U4', title: 'qa / regression', kind: 'verification', owner: owners.qaAgentId, workPackage: 'verification', dependsOn: ['U3'], parallel: false, status: 'OPEN', dod: 'qa evidence attached' },
|
|
119
|
+
{ id: 'U5', title: 'report / deploy / wrap-up', kind: 'reporting', owner: owners.orchestratorId, workPackage: 'release', dependsOn: ['U4'], parallel: false, status: 'OPEN', dod: 'final report prepared and deployment decision recorded' },
|
|
32
120
|
];
|
|
33
121
|
}
|
|
34
122
|
return [
|
|
35
|
-
{ id: 'U1', title: 'spec confirmation', kind: 'planning', owner:
|
|
36
|
-
{ id: 'U2', title: 'implementation slice', kind: 'implementation', owner:
|
|
37
|
-
{ id: 'U3', title: 'qa / regression', kind: 'verification', owner:
|
|
123
|
+
{ id: 'U1', title: 'spec confirmation', kind: 'planning', owner: owners.orchestratorId, workPackage: 'planning', dependsOn: [], parallel: false, status: 'OPEN', dod: 'scope / constraints / edge cases fixed' },
|
|
124
|
+
{ id: 'U2', title: 'implementation slice', kind: 'implementation', owner: owners.orchestratorId, workPackage: 'implementation', dependsOn: ['U1'], parallel: false, status: 'OPEN', dod: 'main implementation slice complete' },
|
|
125
|
+
{ id: 'U3', title: 'qa / regression', kind: 'verification', owner: owners.qaAgentId, workPackage: 'verification', dependsOn: ['U2'], parallel: false, status: 'OPEN', dod: 'qa evidence attached' },
|
|
38
126
|
];
|
|
39
127
|
}
|
|
40
|
-
|
|
41
|
-
function buildBootPrompt(task) {
|
|
128
|
+
function buildBootPrompt(task, options) {
|
|
42
129
|
return `## Orchestration Task Boot: ${task.taskId}
|
|
43
130
|
- **Title:** ${task.title}
|
|
44
131
|
- **Scale:** ${task.scale}
|
|
132
|
+
- **Orchestrator:** ${options.orchestratorId}
|
|
45
133
|
|
|
46
134
|
---
|
|
47
135
|
|
|
48
|
-
## ⚠️ 절대 규칙:
|
|
136
|
+
## ⚠️ 절대 규칙: QA 역할 워커 호출 필수
|
|
49
137
|
|
|
50
|
-
|
|
138
|
+
**이 태스크는 QA 역할(${options.qaAgentId}) 워커 완료가 필수다.**
|
|
51
139
|
|
|
52
140
|
---
|
|
53
141
|
|
|
@@ -78,24 +166,31 @@ zigrix task finalize ${task.taskId} --json
|
|
|
78
166
|
\`\`\`
|
|
79
167
|
`;
|
|
80
168
|
}
|
|
81
|
-
|
|
82
|
-
export function dispatchTask(paths, params) {
|
|
169
|
+
export function dispatchTask(paths, config, params) {
|
|
83
170
|
ensureBaseState(paths);
|
|
84
|
-
|
|
171
|
+
const selection = resolveAgentSelection(config, params.scale);
|
|
85
172
|
const task = createTask(paths, {
|
|
86
173
|
title: params.title,
|
|
87
174
|
description: params.description,
|
|
88
175
|
scale: params.scale,
|
|
89
|
-
requiredAgents: [...
|
|
176
|
+
requiredAgents: [...selection.requiredAgents],
|
|
90
177
|
projectDir: params.projectDir,
|
|
91
178
|
requestedBy: params.requestedBy,
|
|
92
179
|
});
|
|
93
|
-
|
|
94
|
-
task.selectedAgents = [...BASELINE_REQUIRED];
|
|
180
|
+
task.selectedAgents = [...selection.requiredAgents];
|
|
95
181
|
task.workPackages = defaultWorkPackages(params.scale);
|
|
96
|
-
task.executionUnits = defaultExecutionUnits(params.scale
|
|
182
|
+
task.executionUnits = defaultExecutionUnits(params.scale, {
|
|
183
|
+
orchestratorId: selection.orchestratorId,
|
|
184
|
+
qaAgentId: selection.qaAgentId,
|
|
185
|
+
});
|
|
186
|
+
task.baselineRequiredAgents = [...selection.requiredAgents];
|
|
187
|
+
task.candidateAgents = [...selection.candidateAgents];
|
|
188
|
+
task.requiredRoles = [...selection.requiredRoles];
|
|
189
|
+
task.optionalRoles = [...selection.optionalRoles];
|
|
190
|
+
task.roleAgentMap = selection.roleAgentMap;
|
|
191
|
+
task.orchestratorId = selection.orchestratorId;
|
|
192
|
+
task.qaAgentId = selection.qaAgentId;
|
|
97
193
|
saveTask(paths, task);
|
|
98
|
-
// Write dispatch prompt
|
|
99
194
|
const promptPath = path.join(paths.promptsDir, `${task.taskId}-dispatch.md`);
|
|
100
195
|
const dispatchPrompt = [
|
|
101
196
|
`## Orchestration Task: ${task.taskId}`,
|
|
@@ -103,19 +198,30 @@ export function dispatchTask(paths, params) {
|
|
|
103
198
|
'### 기본 정보',
|
|
104
199
|
`- **Title:** ${task.title}`,
|
|
105
200
|
`- **Scale:** ${task.scale}`,
|
|
106
|
-
`- **
|
|
107
|
-
`- **
|
|
201
|
+
`- **Orchestrator:** ${selection.orchestratorId}`,
|
|
202
|
+
`- **Baseline Required Agents:** ${selection.requiredAgents.join(', ')}`,
|
|
203
|
+
`- **Candidate Agents:** ${selection.candidateAgents.length > 0 ? selection.candidateAgents.join(', ') : '(none)'}`,
|
|
204
|
+
`- **Required Roles:** ${selection.requiredRoles.join(', ')}`,
|
|
205
|
+
`- **Optional Roles:** ${selection.optionalRoles.length > 0 ? selection.optionalRoles.join(', ') : '(none)'}`,
|
|
108
206
|
params.projectDir ? `- **Project Dir:** ${params.projectDir}` : '',
|
|
109
207
|
'',
|
|
110
208
|
'### 요청 내용',
|
|
111
209
|
params.description,
|
|
112
210
|
params.constraints ? `\n### 제약사항\n${params.constraints}` : '',
|
|
113
211
|
'',
|
|
212
|
+
'### 역할 매핑',
|
|
213
|
+
...Object.entries(selection.roleAgentMap)
|
|
214
|
+
.filter(([, agentIds]) => agentIds.length > 0)
|
|
215
|
+
.map(([role, agentIds]) => `- ${role}: ${agentIds.join(', ')}`),
|
|
216
|
+
'',
|
|
114
217
|
'### 선택 규칙',
|
|
115
|
-
...Object.entries(
|
|
218
|
+
...Object.entries(selection.selectionHints).map(([agentId, hint]) => `- ${agentId}: ${hint}`),
|
|
116
219
|
].filter(Boolean).join('\n');
|
|
117
220
|
fs.writeFileSync(promptPath, `${dispatchPrompt}\n`, 'utf8');
|
|
118
|
-
const
|
|
221
|
+
const orchestratorPrompt = buildBootPrompt(task, {
|
|
222
|
+
orchestratorId: selection.orchestratorId,
|
|
223
|
+
qaAgentId: selection.qaAgentId,
|
|
224
|
+
});
|
|
119
225
|
appendEvent(paths.eventsFile, {
|
|
120
226
|
event: 'task_dispatched',
|
|
121
227
|
taskId: task.taskId,
|
|
@@ -124,8 +230,12 @@ export function dispatchTask(paths, params) {
|
|
|
124
230
|
status: 'OPEN',
|
|
125
231
|
payload: {
|
|
126
232
|
scale: task.scale,
|
|
127
|
-
|
|
128
|
-
|
|
233
|
+
orchestratorId: selection.orchestratorId,
|
|
234
|
+
baselineRequiredAgents: selection.requiredAgents,
|
|
235
|
+
candidateAgents: selection.candidateAgents,
|
|
236
|
+
requiredRoles: selection.requiredRoles,
|
|
237
|
+
optionalRoles: selection.optionalRoles,
|
|
238
|
+
roleAgentMap: selection.roleAgentMap,
|
|
129
239
|
projectDir: params.projectDir ?? null,
|
|
130
240
|
},
|
|
131
241
|
});
|
|
@@ -135,12 +245,18 @@ export function dispatchTask(paths, params) {
|
|
|
135
245
|
taskId: task.taskId,
|
|
136
246
|
title: task.title,
|
|
137
247
|
scale: task.scale,
|
|
138
|
-
|
|
139
|
-
|
|
248
|
+
orchestratorId: selection.orchestratorId,
|
|
249
|
+
qaAgentId: selection.qaAgentId,
|
|
250
|
+
baselineRequiredAgents: selection.requiredAgents,
|
|
251
|
+
candidateAgents: selection.candidateAgents,
|
|
252
|
+
requiredRoles: selection.requiredRoles,
|
|
253
|
+
optionalRoles: selection.optionalRoles,
|
|
254
|
+
roleAgentMap: selection.roleAgentMap,
|
|
140
255
|
specPath: path.join(paths.tasksDir, `${task.taskId}.md`),
|
|
141
256
|
metaPath: path.join(paths.tasksDir, `${task.taskId}.meta.json`),
|
|
142
257
|
promptPath,
|
|
143
|
-
|
|
258
|
+
orchestratorPrompt,
|
|
259
|
+
proZigPrompt: orchestratorPrompt,
|
|
144
260
|
projectDir: params.projectDir ?? null,
|
|
145
261
|
};
|
|
146
262
|
}
|
|
@@ -32,6 +32,19 @@ function extractEvidence(rows) {
|
|
|
32
32
|
}
|
|
33
33
|
return { lastAssistant, toolResults: toolResults.slice(-3) };
|
|
34
34
|
}
|
|
35
|
+
function resolveQaAgentId(task) {
|
|
36
|
+
if (typeof task.qaAgentId === 'string' && task.qaAgentId.trim().length > 0) {
|
|
37
|
+
return task.qaAgentId;
|
|
38
|
+
}
|
|
39
|
+
const roleMap = task.roleAgentMap;
|
|
40
|
+
if (roleMap && typeof roleMap === 'object') {
|
|
41
|
+
const qaAgents = roleMap.qa;
|
|
42
|
+
if (Array.isArray(qaAgents) && qaAgents.length > 0) {
|
|
43
|
+
return String(qaAgents[0]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return 'qa-zig';
|
|
47
|
+
}
|
|
35
48
|
export function collectEvidence(paths, params) {
|
|
36
49
|
ensureBaseState(paths);
|
|
37
50
|
const task = loadTask(paths, params.taskId);
|
|
@@ -83,9 +96,10 @@ export function mergeEvidence(paths, params) {
|
|
|
83
96
|
const presentAgents = [...new Set(items.map((item) => String(item.agentId)))].sort();
|
|
84
97
|
const requiredAgents = [...(params.requiredAgents?.length ? params.requiredAgents : resolveRequiredAgents(task))];
|
|
85
98
|
const missingAgents = requiredAgents.filter((agentId) => !presentAgents.includes(agentId));
|
|
86
|
-
const
|
|
99
|
+
const qaAgentId = resolveQaAgentId(task);
|
|
100
|
+
const qaPresent = presentAgents.includes(qaAgentId);
|
|
87
101
|
const complete = missingAgents.length === 0 && (!(params.requireQa ?? false) || qaPresent);
|
|
88
|
-
const merged = { ts: nowIso(), taskId: params.taskId, requiredAgents, presentAgents, missingAgents, qaPresent, complete, items };
|
|
102
|
+
const merged = { ts: nowIso(), taskId: params.taskId, requiredAgents, presentAgents, missingAgents, qaAgentId, qaPresent, complete, items };
|
|
89
103
|
const outPath = path.join(taskDir, '_merged.json');
|
|
90
104
|
fs.writeFileSync(outPath, `${JSON.stringify(merged, null, 2)}\n`, 'utf8');
|
|
91
105
|
appendEvent(paths.eventsFile, {
|
|
@@ -93,5 +107,5 @@ export function mergeEvidence(paths, params) {
|
|
|
93
107
|
payload: { requiredAgents, missingAgents, complete, mergedPath: outPath, qaPresent },
|
|
94
108
|
});
|
|
95
109
|
rebuildIndex(paths);
|
|
96
|
-
return { ok: true, taskId: params.taskId, complete, missingAgents, mergedPath: outPath };
|
|
110
|
+
return { ok: true, taskId: params.taskId, complete, missingAgents, qaAgentId, qaPresent, mergedPath: outPath };
|
|
97
111
|
}
|
|
@@ -15,8 +15,8 @@ function autoCloseCompletedUnits(task) {
|
|
|
15
15
|
if (s.status === 'done')
|
|
16
16
|
doneAgents.add(agentId);
|
|
17
17
|
}
|
|
18
|
-
//
|
|
19
|
-
doneAgents.add('pro-zig');
|
|
18
|
+
// orchestrator is always "done" at finalize time
|
|
19
|
+
doneAgents.add(task.orchestratorId ?? 'pro-zig');
|
|
20
20
|
let changed = false;
|
|
21
21
|
for (const unit of units) {
|
|
22
22
|
if (['OPEN', 'IN_PROGRESS'].includes(unit.status.toUpperCase()) && doneAgents.has(unit.owner)) {
|
|
@@ -44,7 +44,12 @@ function collectRisks(merged) {
|
|
|
44
44
|
}
|
|
45
45
|
function qaLine(merged) {
|
|
46
46
|
const present = new Set(Array.isArray(merged.presentAgents) ? merged.presentAgents.map(String) : []);
|
|
47
|
-
|
|
47
|
+
const qaAgentId = typeof merged.qaAgentId === 'string' && merged.qaAgentId.trim().length > 0
|
|
48
|
+
? merged.qaAgentId
|
|
49
|
+
: 'qa-zig';
|
|
50
|
+
return present.has(qaAgentId)
|
|
51
|
+
? `- ${qaAgentId} evidence 존재, QA 수행됨`
|
|
52
|
+
: `- ${qaAgentId} evidence 없음 또는 별도 QA 미실행`;
|
|
48
53
|
}
|
|
49
54
|
export function renderReport(paths, params) {
|
|
50
55
|
const task = loadTask(paths, params.taskId);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ZigrixPaths } from '../state/paths.js';
|
|
2
2
|
import { type ZigrixTask } from '../state/tasks.js';
|
|
3
|
-
export declare const DEFAULT_REQUIRED_AGENTS:
|
|
3
|
+
export declare const DEFAULT_REQUIRED_AGENTS: readonly ["orchestrator", "qa"];
|
|
4
4
|
export declare function resolveRequiredAgents(task: Partial<ZigrixTask> & Record<string, unknown>): string[];
|
|
5
5
|
export declare function prepareWorker(paths: ZigrixPaths, params: {
|
|
6
6
|
taskId: string;
|
|
@@ -3,7 +3,22 @@ import path from 'node:path';
|
|
|
3
3
|
import { appendEvent } from '../state/events.js';
|
|
4
4
|
import { ensureBaseState } from '../state/paths.js';
|
|
5
5
|
import { loadTask, saveTask } from '../state/tasks.js';
|
|
6
|
-
export const DEFAULT_REQUIRED_AGENTS = ['
|
|
6
|
+
export const DEFAULT_REQUIRED_AGENTS = ['orchestrator', 'qa'];
|
|
7
|
+
const DEFAULT_ORCHESTRATOR_ID = 'pro-zig';
|
|
8
|
+
const DEFAULT_QA_AGENT_ID = 'qa-zig';
|
|
9
|
+
function resolveDefaultRequiredAgents(task) {
|
|
10
|
+
const orchestratorId = typeof task.orchestratorId === 'string' && task.orchestratorId.trim().length > 0
|
|
11
|
+
? task.orchestratorId
|
|
12
|
+
: DEFAULT_ORCHESTRATOR_ID;
|
|
13
|
+
const qaAgentId = typeof task.qaAgentId === 'string' && task.qaAgentId.trim().length > 0
|
|
14
|
+
? task.qaAgentId
|
|
15
|
+
: DEFAULT_QA_AGENT_ID;
|
|
16
|
+
const resolvedByRole = {
|
|
17
|
+
orchestrator: orchestratorId,
|
|
18
|
+
qa: qaAgentId,
|
|
19
|
+
};
|
|
20
|
+
return [...new Set(DEFAULT_REQUIRED_AGENTS.map((role) => resolvedByRole[role]))];
|
|
21
|
+
}
|
|
7
22
|
export function resolveRequiredAgents(task) {
|
|
8
23
|
for (const key of ['requiredAgents', 'selectedAgents', 'baselineRequiredAgents']) {
|
|
9
24
|
const value = task[key];
|
|
@@ -15,7 +30,7 @@ export function resolveRequiredAgents(task) {
|
|
|
15
30
|
if (workers && typeof workers === 'object' && Object.keys(workers).length > 0) {
|
|
16
31
|
return Object.keys(workers).sort();
|
|
17
32
|
}
|
|
18
|
-
return
|
|
33
|
+
return resolveDefaultRequiredAgents(task);
|
|
19
34
|
}
|
|
20
35
|
function renderPrompt(params) {
|
|
21
36
|
const sections = [
|
package/dist/rules/templates.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { STANDARD_AGENT_ROLES } from '../agents/roles.js';
|
|
1
2
|
export const TEMPLATE_PLACEHOLDERS = {
|
|
2
3
|
workerPrompt: ['taskId', 'title', 'scale', 'agentId', 'description', 'constraints', 'requiredRoles', 'workPackage', 'unitId'],
|
|
3
4
|
finalReport: ['taskId', 'title', 'scale', 'status', 'summary', 'missingAgents'],
|
|
@@ -36,12 +37,8 @@ export function validateTemplate(kind, body) {
|
|
|
36
37
|
}
|
|
37
38
|
export function validateRules(config) {
|
|
38
39
|
const knownRoles = new Set(Object.values(config.agents.registry).map((agent) => agent.role));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
knownRoles.add('frontend');
|
|
42
|
-
knownRoles.add('backend');
|
|
43
|
-
knownRoles.add('security');
|
|
44
|
-
knownRoles.add('infra');
|
|
40
|
+
for (const role of STANDARD_AGENT_ROLES)
|
|
41
|
+
knownRoles.add(role);
|
|
45
42
|
const invalidRoles = new Set();
|
|
46
43
|
for (const scale of Object.values(config.rules.scales)) {
|
|
47
44
|
for (const role of [...scale.requiredRoles, ...scale.optionalRoles]) {
|
package/dist/state/tasks.d.ts
CHANGED
|
@@ -31,6 +31,13 @@ export type ZigrixTask = {
|
|
|
31
31
|
selectedAgents?: string[];
|
|
32
32
|
workPackages?: WorkPackage[];
|
|
33
33
|
executionUnits?: ExecutionUnit[];
|
|
34
|
+
baselineRequiredAgents?: string[];
|
|
35
|
+
candidateAgents?: string[];
|
|
36
|
+
requiredRoles?: string[];
|
|
37
|
+
optionalRoles?: string[];
|
|
38
|
+
roleAgentMap?: Record<string, string[]>;
|
|
39
|
+
orchestratorId?: string;
|
|
40
|
+
qaAgentId?: string;
|
|
34
41
|
orchestratorSessionKey?: string;
|
|
35
42
|
orchestratorSessionId?: string;
|
|
36
43
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zigrix-main-agent-guide
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
description: Main-agent guide for using Zigrix CLI (task issuance, orchestrator spawn, dashboard, and Python-script-to-CLI migration).
|
|
5
|
+
metadata:
|
|
6
|
+
openclaw:
|
|
7
|
+
requires:
|
|
8
|
+
bins: ["zigrix"]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Zigrix Main Agent Guide
|
|
12
|
+
|
|
13
|
+
메인 에이전트가 Zigrix를 사용할 때의 표준 흐름.
|
|
14
|
+
|
|
15
|
+
## 1) 기본 명령
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# 초기 설정
|
|
19
|
+
zigrix onboard --yes --json
|
|
20
|
+
|
|
21
|
+
# 환경/설정 점검
|
|
22
|
+
zigrix doctor
|
|
23
|
+
zigrix config validate --json
|
|
24
|
+
zigrix agent list --json
|
|
25
|
+
|
|
26
|
+
# 대시보드 실행
|
|
27
|
+
zigrix dashboard --port 5173
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 2) 태스크 발급 흐름 (권장)
|
|
31
|
+
|
|
32
|
+
`task create`보다 `task dispatch`를 우선 사용한다.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
zigrix task dispatch \
|
|
36
|
+
--title "Implement X" \
|
|
37
|
+
--description "..." \
|
|
38
|
+
--scale normal \
|
|
39
|
+
--project-dir /path/to/project \
|
|
40
|
+
--json
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
디스패치 결과에서 확인할 핵심 필드:
|
|
44
|
+
- `taskId`
|
|
45
|
+
- `orchestratorId`
|
|
46
|
+
- `qaAgentId`
|
|
47
|
+
- `baselineRequiredAgents`
|
|
48
|
+
- `candidateAgents`
|
|
49
|
+
- `orchestratorPrompt` (`proZigPrompt` 호환 별칭 포함)
|
|
50
|
+
|
|
51
|
+
## 3) 오케스트레이터 spawn 패턴
|
|
52
|
+
|
|
53
|
+
디스패치 응답의 `orchestratorPrompt`를 오케스트레이터 에이전트에게 전달한다.
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
sessions_spawn(
|
|
57
|
+
agentId: <orchestratorId>,
|
|
58
|
+
task: <dispatchResult.orchestratorPrompt>
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
오케스트레이터는 이후 워커를 `zigrix worker prepare/register/complete` 체인으로 관리한다.
|
|
63
|
+
|
|
64
|
+
## 4) 워커/검증/최종 보고 체인
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# 워커 준비
|
|
68
|
+
zigrix worker prepare --task-id <taskId> --agent-id <agentId> --description "..." --json
|
|
69
|
+
|
|
70
|
+
# 워커 세션 등록
|
|
71
|
+
zigrix worker register --task-id <taskId> --agent-id <agentId> --session-key <sessionKey> --run-id <runId> --json
|
|
72
|
+
|
|
73
|
+
# 워커 완료
|
|
74
|
+
zigrix worker complete --task-id <taskId> --agent-id <agentId> --session-key <sessionKey> --run-id <runId> --json
|
|
75
|
+
|
|
76
|
+
# evidence 수집/머지
|
|
77
|
+
zigrix evidence collect --task-id <taskId> --agent-id <agentId> --summary "..." --json
|
|
78
|
+
zigrix evidence merge --task-id <taskId> --require-qa --json
|
|
79
|
+
|
|
80
|
+
# 최종화
|
|
81
|
+
zigrix task finalize <taskId> --auto-report --json
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 5) 대시보드 사용 포인트
|
|
85
|
+
|
|
86
|
+
대시보드에서 확인:
|
|
87
|
+
- Task 상태 (OPEN → IN_PROGRESS → REPORTED)
|
|
88
|
+
- 워커 세션 매핑
|
|
89
|
+
- 이벤트 로그
|
|
90
|
+
- Conversation/증적 추적
|
|
91
|
+
|
|
92
|
+
문제 상황 디버깅 순서:
|
|
93
|
+
1. `zigrix task status <taskId> --json`
|
|
94
|
+
2. `zigrix task events <taskId> --json`
|
|
95
|
+
3. `zigrix evidence merge --task-id <taskId> --require-qa --json`
|
|
96
|
+
|
|
97
|
+
## 6) Python 스크립트 체인 → Zigrix CLI 전환 가이드
|
|
98
|
+
|
|
99
|
+
기존(레거시 Python):
|
|
100
|
+
- `dev_dispatch.py`
|
|
101
|
+
- `dev_start.py`
|
|
102
|
+
- `orch_prepare_worker.py`
|
|
103
|
+
- `orch_register_worker.py`
|
|
104
|
+
- `orch_complete_worker.py`
|
|
105
|
+
- `dev_finalize.py`
|
|
106
|
+
|
|
107
|
+
신규(권장 Zigrix CLI):
|
|
108
|
+
- `zigrix task dispatch`
|
|
109
|
+
- `zigrix task start`
|
|
110
|
+
- `zigrix worker prepare`
|
|
111
|
+
- `zigrix worker register`
|
|
112
|
+
- `zigrix worker complete`
|
|
113
|
+
- `zigrix task finalize`
|
|
114
|
+
|
|
115
|
+
전환 원칙:
|
|
116
|
+
- 신규 자동화는 CLI 우선
|
|
117
|
+
- Python 스크립트는 호환/과도기용
|
|
118
|
+
- JSON 출력(`--json`)을 기본으로 파이프라인에서 파싱
|
|
File without changes
|
/package/dist/dashboard/.next/static/{2a4glWei05xr4Jg0Ly6cp → TlUj0t8APzTccK13DVZZW}/_ssgManifest.js
RENAMED
|
File without changes
|