knoxis-helper 1.0.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.
- package/bin/knoxis-helper.js +191 -0
- package/lib/knoxis-local-agent.js +1137 -0
- package/lib/knoxis-pair-program.js +513 -0
- package/package.json +17 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawn, spawnSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
function parseArgs(argv) {
|
|
8
|
+
const args = {};
|
|
9
|
+
const multi = {};
|
|
10
|
+
for (let i = 2; i < argv.length; i++) {
|
|
11
|
+
const arg = argv[i];
|
|
12
|
+
if (arg.startsWith('--')) {
|
|
13
|
+
const key = arg.slice(2);
|
|
14
|
+
const next = argv[i + 1];
|
|
15
|
+
if (!next || next.startsWith('--')) {
|
|
16
|
+
args[key] = true;
|
|
17
|
+
} else {
|
|
18
|
+
if (multi[key]) {
|
|
19
|
+
multi[key].push(next);
|
|
20
|
+
} else if (args[key]) {
|
|
21
|
+
multi[key] = [args[key], next];
|
|
22
|
+
delete args[key];
|
|
23
|
+
} else {
|
|
24
|
+
args[key] = next;
|
|
25
|
+
}
|
|
26
|
+
i++;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
Object.entries(multi).forEach(([key, list]) => {
|
|
31
|
+
args[key] = list;
|
|
32
|
+
});
|
|
33
|
+
return args;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function decodeBase64(value) {
|
|
37
|
+
try {
|
|
38
|
+
return Buffer.from(value, 'base64').toString('utf8');
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error('Failed to decode base64 value:', err.message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function decodeJsonBase64(value, label) {
|
|
46
|
+
const decoded = decodeBase64(value);
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(decoded);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error(`Failed to parse ${label} JSON: ${err.message}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function commandExists(cmd) {
|
|
56
|
+
const detector = process.platform === 'win32' ? 'where' : 'which';
|
|
57
|
+
const result = spawnSync(detector, [cmd], { stdio: 'ignore' });
|
|
58
|
+
return result.status === 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Resolve workspace name to path using knoxis registry
|
|
62
|
+
function resolveWorkspacePath(nameOrPath) {
|
|
63
|
+
const os = require('os');
|
|
64
|
+
|
|
65
|
+
// Direct path - check if exists
|
|
66
|
+
if (fs.existsSync(nameOrPath)) {
|
|
67
|
+
return path.resolve(nameOrPath);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Try knoxis workspace registry
|
|
71
|
+
const workspacesFile = path.join(os.homedir(), '.knoxis', 'workspaces.json');
|
|
72
|
+
if (fs.existsSync(workspacesFile)) {
|
|
73
|
+
try {
|
|
74
|
+
const workspaces = JSON.parse(fs.readFileSync(workspacesFile, 'utf8'));
|
|
75
|
+
|
|
76
|
+
// Exact match
|
|
77
|
+
if (workspaces[nameOrPath]) {
|
|
78
|
+
return workspaces[nameOrPath];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Fuzzy match
|
|
82
|
+
const lower = nameOrPath.toLowerCase();
|
|
83
|
+
for (const [name, wsPath] of Object.entries(workspaces)) {
|
|
84
|
+
if (name.toLowerCase().includes(lower)) {
|
|
85
|
+
console.log(`Matched workspace: ${name} -> ${wsPath}`);
|
|
86
|
+
return wsPath;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (e) {
|
|
90
|
+
// Fall through
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveAiProvider(preference) {
|
|
98
|
+
const normalized = (preference || 'auto').toLowerCase();
|
|
99
|
+
if (normalized === 'claude' && commandExists('claude')) {
|
|
100
|
+
return { cmd: 'claude', args: ['--dangerously-skip-permissions'], label: 'Claude Code' };
|
|
101
|
+
}
|
|
102
|
+
if (normalized === 'codex' && commandExists('codex')) {
|
|
103
|
+
return { cmd: 'codex', args: [], label: 'Codex' };
|
|
104
|
+
}
|
|
105
|
+
if (normalized === 'auto') {
|
|
106
|
+
if (commandExists('claude')) {
|
|
107
|
+
return { cmd: 'claude', args: ['--dangerously-skip-permissions'], label: 'Claude Code' };
|
|
108
|
+
}
|
|
109
|
+
if (commandExists('codex')) {
|
|
110
|
+
return { cmd: 'codex', args: [], label: 'Codex' };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
console.error('No supported AI provider found. Install the Claude or Codex CLI and try again.');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function formatSection(title, body) {
|
|
118
|
+
const border = '-'.repeat(title.length + 4);
|
|
119
|
+
return `${border}\n| ${title} |\n${border}\n${body.trim()}\n`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function toArray(value) {
|
|
123
|
+
if (!value) return [];
|
|
124
|
+
if (Array.isArray(value)) {
|
|
125
|
+
const result = [];
|
|
126
|
+
value.forEach(item => {
|
|
127
|
+
result.push(...toArray(item));
|
|
128
|
+
});
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
return [value];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function gatherContext(workspace, inputs) {
|
|
135
|
+
const sections = [];
|
|
136
|
+
const labels = [];
|
|
137
|
+
const seen = new Set();
|
|
138
|
+
|
|
139
|
+
toArray(inputs).forEach(entry => {
|
|
140
|
+
if (typeof entry !== 'string') {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const trimmed = entry.trim();
|
|
144
|
+
if (!trimmed) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const absolute = path.isAbsolute(trimmed) ? trimmed : path.join(workspace, trimmed);
|
|
148
|
+
if (!fs.existsSync(absolute)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (seen.has(absolute)) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
seen.add(absolute);
|
|
155
|
+
const content = fs.readFileSync(absolute, 'utf8');
|
|
156
|
+
const title = path.relative(workspace, absolute) || path.basename(absolute);
|
|
157
|
+
labels.push(title);
|
|
158
|
+
sections.push(formatSection(title, content));
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return { sections, labels };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildDefaultTemplates(stepInput) {
|
|
165
|
+
const defaults = [
|
|
166
|
+
{
|
|
167
|
+
key: 'understand',
|
|
168
|
+
title: 'Understanding',
|
|
169
|
+
template: ({ taskDescription }) => `Let's understand what we're working with before diving in.
|
|
170
|
+
|
|
171
|
+
The task: ${taskDescription}
|
|
172
|
+
|
|
173
|
+
First:
|
|
174
|
+
1. Read the relevant code to understand the current state
|
|
175
|
+
2. Identify what needs to change
|
|
176
|
+
3. Note any potential issues or dependencies
|
|
177
|
+
|
|
178
|
+
Share your understanding briefly. For any ambiguities, state your assumption and move on - do not ask questions.`
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
key: 'plan',
|
|
182
|
+
title: 'Planning',
|
|
183
|
+
template: ({ taskDescription }) => `Good. Now create a concrete implementation plan for: ${taskDescription}
|
|
184
|
+
|
|
185
|
+
Plan requirements:
|
|
186
|
+
1. What files need to be created or modified?
|
|
187
|
+
2. What's the order of changes?
|
|
188
|
+
3. What could go wrong?
|
|
189
|
+
4. How will we verify it works?
|
|
190
|
+
|
|
191
|
+
For any open questions from the previous step, make your best judgment call and note the decision. Do not ask - decide and move forward.`
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
key: 'implement',
|
|
195
|
+
title: 'Implementation',
|
|
196
|
+
template: ({ taskDescription }) => `Now implement the plan. Work through it step by step.
|
|
197
|
+
|
|
198
|
+
Rules:
|
|
199
|
+
- Make one logical change at a time
|
|
200
|
+
- For any decisions or unknowns, pick the most standard approach and note your choice
|
|
201
|
+
- If you flagged questions earlier, answer them yourself with reasonable defaults and proceed
|
|
202
|
+
- Flag if you hit something truly blocking (missing credentials, broken dependencies)
|
|
203
|
+
- Otherwise, keep building
|
|
204
|
+
|
|
205
|
+
Start implementing now.`
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
key: 'verify',
|
|
209
|
+
title: 'Review',
|
|
210
|
+
template: () => `Let's review what we built.
|
|
211
|
+
|
|
212
|
+
Quick checklist:
|
|
213
|
+
- Does it solve the original problem?
|
|
214
|
+
- Any edge cases we missed?
|
|
215
|
+
- Is the code clean and following project patterns?
|
|
216
|
+
- Anything we should test?
|
|
217
|
+
|
|
218
|
+
Give me the summary and any follow-up recommendations.`
|
|
219
|
+
}
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
if (!stepInput) {
|
|
223
|
+
return defaults;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const requested = (Array.isArray(stepInput) ? stepInput.join(',') : stepInput)
|
|
227
|
+
.split(',')
|
|
228
|
+
.map(s => s.trim().toLowerCase())
|
|
229
|
+
.filter(Boolean);
|
|
230
|
+
|
|
231
|
+
if (!requested.length) {
|
|
232
|
+
return defaults;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const resolved = [];
|
|
236
|
+
requested.forEach(key => {
|
|
237
|
+
const match = defaults.find(step => step.key === key);
|
|
238
|
+
if (match) {
|
|
239
|
+
resolved.push(match);
|
|
240
|
+
} else {
|
|
241
|
+
resolved.push({
|
|
242
|
+
key,
|
|
243
|
+
title: key.charAt(0).toUpperCase() + key.slice(1),
|
|
244
|
+
template: ({ taskDescription }) => `Directive (${key}): ${taskDescription}`
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return resolved.length ? resolved : defaults;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function scheduleDefaultSteps(templates, taskDescription, agentLabel) {
|
|
253
|
+
const trimmedTask = taskDescription.trim();
|
|
254
|
+
return templates.map(template => ({
|
|
255
|
+
key: template.key,
|
|
256
|
+
title: template.title,
|
|
257
|
+
displayName: agentLabel,
|
|
258
|
+
persona: null,
|
|
259
|
+
instruction: template.template({ taskDescription: trimmedTask }),
|
|
260
|
+
contextPaths: [],
|
|
261
|
+
includeContext: false
|
|
262
|
+
}));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function buildPrompt(options) {
|
|
266
|
+
const {
|
|
267
|
+
systemIntro,
|
|
268
|
+
personaIntro,
|
|
269
|
+
conversation,
|
|
270
|
+
instructionLabel,
|
|
271
|
+
instructionTitle,
|
|
272
|
+
instruction,
|
|
273
|
+
includeContextBlock,
|
|
274
|
+
globalContext,
|
|
275
|
+
stepContext,
|
|
276
|
+
agentLabel
|
|
277
|
+
} = options;
|
|
278
|
+
|
|
279
|
+
const sections = [];
|
|
280
|
+
|
|
281
|
+
if (systemIntro) {
|
|
282
|
+
sections.push(systemIntro);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (personaIntro) {
|
|
286
|
+
sections.push(personaIntro);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (includeContextBlock && globalContext) {
|
|
290
|
+
sections.push(`Project context:\n${globalContext}`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (includeContextBlock && stepContext) {
|
|
294
|
+
sections.push(`Step-specific context:\n${stepContext}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (conversation) {
|
|
298
|
+
sections.push(`Conversation so far:\n${conversation}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const heading = instructionTitle
|
|
302
|
+
? `${instructionLabel} -> ${agentLabel} (${instructionTitle})`
|
|
303
|
+
: `${instructionLabel} -> ${agentLabel}`;
|
|
304
|
+
|
|
305
|
+
sections.push(`${heading}:\n${instruction}`);
|
|
306
|
+
sections.push(`${agentLabel}:`);
|
|
307
|
+
|
|
308
|
+
return sections.join('\n\n');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function callAi(aiConfig, prompt, livePrinter) {
|
|
312
|
+
return new Promise((resolve, reject) => {
|
|
313
|
+
const proc = spawn(aiConfig.cmd, aiConfig.args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
314
|
+
let stdout = '';
|
|
315
|
+
let stderr = '';
|
|
316
|
+
let pendingLine = '';
|
|
317
|
+
|
|
318
|
+
proc.stdout.on('data', chunk => {
|
|
319
|
+
const text = chunk.toString();
|
|
320
|
+
stdout += text;
|
|
321
|
+
pendingLine += text;
|
|
322
|
+
let index;
|
|
323
|
+
while ((index = pendingLine.indexOf('\n')) !== -1) {
|
|
324
|
+
const line = pendingLine.slice(0, index);
|
|
325
|
+
pendingLine = pendingLine.slice(index + 1);
|
|
326
|
+
livePrinter(line);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
proc.stderr.on('data', chunk => {
|
|
331
|
+
const text = chunk.toString();
|
|
332
|
+
stderr += text;
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
proc.on('close', code => {
|
|
336
|
+
if (pendingLine.length) {
|
|
337
|
+
livePrinter(pendingLine);
|
|
338
|
+
pendingLine = '';
|
|
339
|
+
}
|
|
340
|
+
if (code === 0) {
|
|
341
|
+
resolve(stdout.trim());
|
|
342
|
+
} else {
|
|
343
|
+
reject(new Error(stderr.trim() || `AI command exited with status ${code}`));
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
proc.stdin.write(prompt);
|
|
348
|
+
proc.stdin.end();
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async function run() {
|
|
353
|
+
const args = parseArgs(process.argv);
|
|
354
|
+
const workspaceArg = args.workspace || args['workspace-dir'] || args['working-directory'] || args.w;
|
|
355
|
+
|
|
356
|
+
if (!workspaceArg) {
|
|
357
|
+
console.error('Missing required --workspace argument.');
|
|
358
|
+
console.error('Usage: knoxis-pair-program --workspace <name-or-path> --prompt "task"');
|
|
359
|
+
console.error(' knoxis-pair-program -w voice-backend --prompt "add authentication"');
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Resolve workspace (supports names from knoxis registry)
|
|
364
|
+
let workspace = resolveWorkspacePath(workspaceArg);
|
|
365
|
+
if (!workspace) {
|
|
366
|
+
// Maybe it's a new path to create
|
|
367
|
+
workspace = path.resolve(workspaceArg);
|
|
368
|
+
console.log(`Creating new workspace: ${workspace}`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (!fs.existsSync(workspace)) {
|
|
372
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const timeline = args['timeline-base64'] ? decodeJsonBase64(args['timeline-base64'], 'timeline') : null;
|
|
376
|
+
|
|
377
|
+
let task = args['prompt-base64'] ? decodeBase64(args['prompt-base64']) : args.prompt;
|
|
378
|
+
if ((!task || !task.trim()) && timeline && typeof timeline.task === 'string') {
|
|
379
|
+
task = timeline.task;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (!task || !task.trim()) {
|
|
383
|
+
console.error('A task prompt is required via --prompt, --prompt-base64, or timeline.task.');
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
task = task.trim();
|
|
388
|
+
|
|
389
|
+
const aiConfig = resolveAiProvider(args['ai-provider'] || args.provider);
|
|
390
|
+
|
|
391
|
+
const globalContextInputs = [];
|
|
392
|
+
if (args['context']) {
|
|
393
|
+
globalContextInputs.push(args['context']);
|
|
394
|
+
}
|
|
395
|
+
if (args['context-file']) {
|
|
396
|
+
globalContextInputs.push(args['context-file']);
|
|
397
|
+
}
|
|
398
|
+
if (timeline && Array.isArray(timeline.sharedContext)) {
|
|
399
|
+
globalContextInputs.push(timeline.sharedContext);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const globalContext = gatherContext(workspace, globalContextInputs);
|
|
403
|
+
const globalContextBlock = globalContext.sections.join('\n\n');
|
|
404
|
+
|
|
405
|
+
let scheduledSteps;
|
|
406
|
+
if (timeline && Array.isArray(timeline.steps) && timeline.steps.length) {
|
|
407
|
+
scheduledSteps = timeline.steps.map((step, index) => ({
|
|
408
|
+
key: step.key || `step-${index + 1}`,
|
|
409
|
+
title: step.title || '',
|
|
410
|
+
displayName: step.displayName || step.agentId || `Agent ${index + 1}`,
|
|
411
|
+
persona: step.persona || null,
|
|
412
|
+
instruction: typeof step.instruction === 'string' && step.instruction.trim().length
|
|
413
|
+
? step.instruction
|
|
414
|
+
: `Proceed with the shared task: ${task}`,
|
|
415
|
+
contextPaths: step.contextFiles || [],
|
|
416
|
+
includeContext: Boolean(step.includeContext)
|
|
417
|
+
}));
|
|
418
|
+
} else {
|
|
419
|
+
const templates = buildDefaultTemplates(args['steps']);
|
|
420
|
+
scheduledSteps = scheduleDefaultSteps(templates, task, aiConfig.label);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (!scheduledSteps.length) {
|
|
424
|
+
console.error('No steps configured for the pair programming session.');
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
console.log('==============================================');
|
|
429
|
+
console.log('Knoxis Pair Programming Session');
|
|
430
|
+
console.log(`Workspace: ${workspace}`);
|
|
431
|
+
console.log(`AI Partner: ${aiConfig.label}`);
|
|
432
|
+
console.log(`Task: ${task}`);
|
|
433
|
+
console.log('==============================================');
|
|
434
|
+
console.log('');
|
|
435
|
+
|
|
436
|
+
if (globalContext.labels.length) {
|
|
437
|
+
console.log(`Shared context files: ${globalContext.labels.join(', ')}`);
|
|
438
|
+
console.log('');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const history = [];
|
|
442
|
+
const conversationLines = () => history.map(entry => `${entry.role}: ${entry.content}`).join('\n\n');
|
|
443
|
+
|
|
444
|
+
const systemIntro = `You are a senior software developer pair programming with your colleague Knoxis.
|
|
445
|
+
|
|
446
|
+
Your communication style:
|
|
447
|
+
- Be direct and concise - no fluff or excessive politeness
|
|
448
|
+
- Think out loud briefly before acting
|
|
449
|
+
- Do NOT ask clarifying questions. Make your best engineering judgment and proceed.
|
|
450
|
+
- If you need to choose between approaches, pick the most standard one and briefly note why.
|
|
451
|
+
- Warn about potential issues or gotchas you notice
|
|
452
|
+
- Suggest better approaches when you see them
|
|
453
|
+
|
|
454
|
+
Your technical approach:
|
|
455
|
+
- Read and understand existing code before making changes
|
|
456
|
+
- Follow existing patterns in the codebase
|
|
457
|
+
- Write clean, maintainable code
|
|
458
|
+
- Consider edge cases and error handling
|
|
459
|
+
- Don't over-engineer - solve the problem at hand
|
|
460
|
+
- Leave the codebase better than you found it
|
|
461
|
+
|
|
462
|
+
IMPORTANT: Work autonomously. Do not ask questions or wait for confirmation. Make decisions and implement.
|
|
463
|
+
Only work inside the provided workspace and preserve user data.`;
|
|
464
|
+
|
|
465
|
+
for (const step of scheduledSteps) {
|
|
466
|
+
const stepContext = gatherContext(workspace, step.contextPaths);
|
|
467
|
+
if (stepContext.labels.length) {
|
|
468
|
+
console.log(`Step context for ${step.displayName}: ${stepContext.labels.join(', ')}`);
|
|
469
|
+
console.log('');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const titleSuffix = step.title ? ` (${step.title})` : '';
|
|
473
|
+
console.log(`Coordinator -> ${step.displayName}${titleSuffix}:`);
|
|
474
|
+
console.log(step.instruction);
|
|
475
|
+
console.log('');
|
|
476
|
+
|
|
477
|
+
history.push({ role: 'Coordinator', content: step.instruction });
|
|
478
|
+
|
|
479
|
+
console.log(`${step.displayName}:`);
|
|
480
|
+
|
|
481
|
+
const includeContext = history.length <= 1 || step.includeContext || stepContext.sections.length > 0;
|
|
482
|
+
const prompt = buildPrompt({
|
|
483
|
+
systemIntro,
|
|
484
|
+
personaIntro: step.persona,
|
|
485
|
+
conversation: conversationLines(),
|
|
486
|
+
instructionLabel: 'Coordinator',
|
|
487
|
+
instructionTitle: step.title,
|
|
488
|
+
instruction: step.instruction,
|
|
489
|
+
includeContextBlock: includeContext,
|
|
490
|
+
globalContext: globalContextBlock,
|
|
491
|
+
stepContext: stepContext.sections.join('\n\n'),
|
|
492
|
+
agentLabel: step.displayName
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
try {
|
|
496
|
+
const response = await callAi(aiConfig, prompt, line => {
|
|
497
|
+
console.log(` ${line}`);
|
|
498
|
+
});
|
|
499
|
+
history.push({ role: step.displayName, content: response });
|
|
500
|
+
console.log('');
|
|
501
|
+
} catch (err) {
|
|
502
|
+
console.error(`Failed to complete step "${step.title || step.key}": ${err.message}`);
|
|
503
|
+
process.exit(1);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
console.log('Session complete. Knoxis and the AI partner are standing by for further instructions.');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
run().catch(err => {
|
|
511
|
+
console.error(err.message);
|
|
512
|
+
process.exit(1);
|
|
513
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "knoxis-helper",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Local helper for Knoxis pair programming - connects your machine to Knoxis on qig.ai",
|
|
5
|
+
"bin": {
|
|
6
|
+
"knoxis-helper": "./bin/knoxis-helper.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": ["knoxis", "pair-programming", "claude", "qig"],
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin/",
|
|
15
|
+
"lib/"
|
|
16
|
+
]
|
|
17
|
+
}
|