knoxis-helper 1.7.1 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -36,6 +36,7 @@ const os = require('os');
|
|
|
36
36
|
const { SessionRecorder } = require('./session-recorder');
|
|
37
37
|
const { scaffoldStateLayout } = require('./state-scaffold');
|
|
38
38
|
const { syncSessionToPortal } = require('./portal-sync');
|
|
39
|
+
const kitTemplates = require('./templates');
|
|
39
40
|
|
|
40
41
|
// === CONFIG ===
|
|
41
42
|
const CONFIG_PATH = path.join(os.homedir(), '.knoxis', 'config.json');
|
|
@@ -354,10 +355,89 @@ async function finalizeSession(recorder) {
|
|
|
354
355
|
}
|
|
355
356
|
}
|
|
356
357
|
|
|
358
|
+
// === KIT MODE (kickoff / resume) ===
|
|
359
|
+
// When the local agent routes a kit mode here, it sets KNOXIS_KIT_MODE so we
|
|
360
|
+
// load the matching kit template (the same module pair-program.js uses) and
|
|
361
|
+
// run it as a single Claude turn via runClaudeTurn — i.e. through `claude -p
|
|
362
|
+
// --session-id`, which has the file-writing tool access the kit prompts need.
|
|
363
|
+
// The 4-phase Groq flow is bypassed entirely; kit prompts are self-contained
|
|
364
|
+
// frameworks with their own state machine.
|
|
365
|
+
async function runKitMode(kitMode, task, identity, scaffoldResult) {
|
|
366
|
+
const archetype = process.env.KNOXIS_KIT_ARCHETYPE || null;
|
|
367
|
+
const pattern = process.env.KNOXIS_KIT_PATTERN || null;
|
|
368
|
+
let tpl;
|
|
369
|
+
try {
|
|
370
|
+
tpl = kitTemplates.getTemplate(kitMode, {
|
|
371
|
+
taskDescription: task || null,
|
|
372
|
+
archetype,
|
|
373
|
+
pattern,
|
|
374
|
+
productSlug: identity.productSlug,
|
|
375
|
+
projectSlug: identity.projectSlug,
|
|
376
|
+
workspace: identity.workspace
|
|
377
|
+
});
|
|
378
|
+
} catch (e) {
|
|
379
|
+
console.error('Kit template error: ' + e.message);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const recorder = new SessionRecorder(task || `[${kitMode} session]`, identity.workspace, 'Claude Code (kit)', {
|
|
384
|
+
mode: kitMode,
|
|
385
|
+
archetype,
|
|
386
|
+
productSlug: identity.productSlug,
|
|
387
|
+
projectSlug: identity.projectSlug,
|
|
388
|
+
engineerId: identity.engineerId,
|
|
389
|
+
userId: identity.userId,
|
|
390
|
+
workspaceId: identity.workspaceId,
|
|
391
|
+
taskIds: identity.taskIds
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
console.log('');
|
|
395
|
+
console.log('╔══════════════════════════════════════════════════════════════╗');
|
|
396
|
+
console.log('║ KNOXIS KIT MODE: ' + kitMode.toUpperCase().padEnd(36) + '║');
|
|
397
|
+
console.log('╚══════════════════════════════════════════════════════════════╝');
|
|
398
|
+
console.log('');
|
|
399
|
+
console.log(' Mode: ' + kitMode + (archetype ? ' (archetype: ' + archetype + ')' : ''));
|
|
400
|
+
console.log(' Workspace: ' + identity.workspace);
|
|
401
|
+
console.log(' Session: ' + SESSION_ID);
|
|
402
|
+
console.log(' Recorded: ' + recorder.sessionId);
|
|
403
|
+
if (task) console.log(' Hint: ' + task.substring(0, 100) + (task.length > 100 ? '...' : ''));
|
|
404
|
+
if (scaffoldResult && (scaffoldResult.dirs.length || scaffoldResult.files.length)) {
|
|
405
|
+
console.log(' Scaffolded: ' + (scaffoldResult.dirs.length + scaffoldResult.files.length) + ' new entries (CODING_RULES + docs/state/)');
|
|
406
|
+
}
|
|
407
|
+
console.log('');
|
|
408
|
+
|
|
409
|
+
appendLog('# Knoxis Kit Mode: ' + kitMode);
|
|
410
|
+
appendLog('Session: ' + SESSION_ID);
|
|
411
|
+
if (task) appendLog('Hint: ' + task);
|
|
412
|
+
appendLog('Date: ' + new Date().toISOString());
|
|
413
|
+
appendLog('');
|
|
414
|
+
|
|
415
|
+
const stepKey = 'kit-' + kitMode;
|
|
416
|
+
const stepIdx = recorder.startStep(stepKey, 'Claude Code', tpl.body);
|
|
417
|
+
recorder.setStepPrompt(stepIdx, tpl.body);
|
|
418
|
+
const result = await runClaudeTurn(tpl.body, false);
|
|
419
|
+
appendLog(result.stdout.substring(0, 10000) + '\n');
|
|
420
|
+
recorder.completeStep(stepIdx, result.stdout, result.code !== 0 ? 'exit ' + result.code + (result.stderr ? ': ' + result.stderr.slice(0, 200) : '') : null);
|
|
421
|
+
|
|
422
|
+
console.log('');
|
|
423
|
+
console.log('╔══════════════════════════════════════════════════════════════╗');
|
|
424
|
+
console.log('║ KIT SESSION COMPLETE ║');
|
|
425
|
+
console.log('╚══════════════════════════════════════════════════════════════╝');
|
|
426
|
+
console.log('');
|
|
427
|
+
const logFile = saveSessionLog();
|
|
428
|
+
if (logFile) console.log(' Log: ' + logFile);
|
|
429
|
+
console.log(' Resume: claude --resume ' + SESSION_ID);
|
|
430
|
+
console.log('');
|
|
431
|
+
|
|
432
|
+
await finalizeSession(recorder);
|
|
433
|
+
process.exit(result.code || 0);
|
|
434
|
+
}
|
|
435
|
+
|
|
357
436
|
// === MAIN ===
|
|
358
437
|
async function main() {
|
|
438
|
+
const kitMode = process.env.KNOXIS_KIT_MODE || null;
|
|
359
439
|
const task = loadTask();
|
|
360
|
-
if (!task) {
|
|
440
|
+
if (!task && !kitMode) {
|
|
361
441
|
console.error('No task found. Set KNOXIS_TASK_FILE, pass as CLI argument, or include in CLAUDE.md.');
|
|
362
442
|
process.exit(1);
|
|
363
443
|
}
|
|
@@ -376,6 +456,17 @@ async function main() {
|
|
|
376
456
|
console.warn(' Scaffold failed: ' + e.message);
|
|
377
457
|
}
|
|
378
458
|
|
|
459
|
+
// Kit modes (kickoff/resume) take a different path: run the kit template as
|
|
460
|
+
// a single Claude turn. Skips the 4-phase Groq plan/implement/verify flow —
|
|
461
|
+
// the kit prompt is the framework.
|
|
462
|
+
if (kitMode) {
|
|
463
|
+
if (!kitTemplates.isKitMode(kitMode)) {
|
|
464
|
+
console.error('Unknown KNOXIS_KIT_MODE "' + kitMode + '". Supported: ' + kitTemplates.listModes().join(', '));
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
return runKitMode(kitMode, task, identity, scaffoldResult);
|
|
468
|
+
}
|
|
469
|
+
|
|
379
470
|
const recorder = new SessionRecorder(task, identity.workspace, 'Claude Code + Knoxis (Groq)', {
|
|
380
471
|
mode: null, // interactive flow isn't a kit mode
|
|
381
472
|
archetype: null,
|
|
@@ -595,6 +595,13 @@ function resolvePairProgramScript() {
|
|
|
595
595
|
}
|
|
596
596
|
|
|
597
597
|
const KIT_MODES = new Set(['kickoff', 'resume', 'session-end', 'recovery']);
|
|
598
|
+
// kickoff and resume rely on Claude actually writing files to disk
|
|
599
|
+
// (docs/state/OPEN_QUESTIONS.md and the rest of the context pack). The
|
|
600
|
+
// interactive runner invokes `claude -p --session-id`, which gives Claude
|
|
601
|
+
// proper tool access; pair-program.js pipes to `claude` without -p and
|
|
602
|
+
// silently skips the file writes. Route those two kits through the
|
|
603
|
+
// interactive runner so their kit prompts actually do their work.
|
|
604
|
+
const KIT_MODES_VIA_INTERACTIVE = new Set(['kickoff', 'resume']);
|
|
598
605
|
|
|
599
606
|
function buildPairProgramCommand({ scriptPath, workspace, mode, archetype, pattern, productSlug, projectSlug, taskPrompt, userId, workspaceId, taskIds }) {
|
|
600
607
|
const q = v => `"${escapeForDoubleQuotedShellArg(v)}"`;
|
|
@@ -965,11 +972,28 @@ async function handleRequest(req, res) {
|
|
|
965
972
|
// 4-step pipeline. Interactive (Groq) mode and the legacy claude-pipe
|
|
966
973
|
// fallback remain available when the runner isn't present.
|
|
967
974
|
const ppScript = resolvePairProgramScript();
|
|
968
|
-
|
|
975
|
+
const kitViaInteractive = kitMode && KIT_MODES_VIA_INTERACTIVE.has(kitMode);
|
|
976
|
+
if (kitMode && !kitViaInteractive && !ppScript) {
|
|
969
977
|
return sendJSON(res, 500, { success: false, error: 'knoxis-pair-program.js not found — reinstall knoxis-helper.' }, requestOrigin);
|
|
970
978
|
}
|
|
971
|
-
|
|
972
|
-
|
|
979
|
+
if (kitViaInteractive) {
|
|
980
|
+
const scriptPath = resolveInteractiveScript();
|
|
981
|
+
if (!scriptPath) {
|
|
982
|
+
return sendJSON(res, 500, { success: false, error: 'knoxis-interactive-pair.js not found — reinstall knoxis-helper.' }, requestOrigin);
|
|
983
|
+
}
|
|
984
|
+
const kitEnv = { KNOXIS_WORKSPACE_PATH: workspaceDir, KNOXIS_KIT_MODE: kitMode };
|
|
985
|
+
if (promptText) kitEnv.KNOXIS_TASK_FILE = promptFile;
|
|
986
|
+
if (archetype) kitEnv.KNOXIS_KIT_ARCHETYPE = archetype;
|
|
987
|
+
if (pattern) kitEnv.KNOXIS_KIT_PATTERN = pattern;
|
|
988
|
+
if (productSlug) kitEnv.KNOXIS_PRODUCT_SLUG = productSlug;
|
|
989
|
+
if (projectSlug) kitEnv.KNOXIS_PROJECT_SLUG = projectSlug;
|
|
990
|
+
if (portalUserId) kitEnv.KNOXIS_USER_ID = portalUserId;
|
|
991
|
+
if (portalWorkspaceId) kitEnv.KNOXIS_WORKSPACE_ID = portalWorkspaceId;
|
|
992
|
+
if (portalTaskIds && portalTaskIds.length) kitEnv.KNOXIS_TASK_IDS = portalTaskIds.join(',');
|
|
993
|
+
command = buildEnvCommand(kitEnv, `node "${scriptPath}"`);
|
|
994
|
+
mode = `kit:${kitMode}${archetype ? `/${archetype}` : ''}`;
|
|
995
|
+
console.log(`🧰 Kit (interactive runner): ${mode} — ${scriptPath}`);
|
|
996
|
+
} else if (kitMode || (ppScript && !interactive && promptText)) {
|
|
973
997
|
command = buildPairProgramCommand({
|
|
974
998
|
scriptPath: ppScript,
|
|
975
999
|
workspace: workspaceDir,
|
|
@@ -1447,9 +1471,35 @@ function connectRelayWebSocket() {
|
|
|
1447
1471
|
? msg.taskIds.filter(t => typeof t === 'string' && t)
|
|
1448
1472
|
: (typeof msg.taskId === 'string' ? [msg.taskId] : []);
|
|
1449
1473
|
const ppScript = resolvePairProgramScript();
|
|
1450
|
-
const
|
|
1451
|
-
|
|
1452
|
-
|
|
1474
|
+
const kitViaInteractive = kitMode && KIT_MODES_VIA_INTERACTIVE.has(kitMode);
|
|
1475
|
+
const routeViaPairProgram = !kitViaInteractive && (kitMode || (ppScript && !interactive && taskPrompt));
|
|
1476
|
+
|
|
1477
|
+
if (kitViaInteractive) {
|
|
1478
|
+
const scriptPath = resolveInteractiveScript();
|
|
1479
|
+
if (scriptPath) {
|
|
1480
|
+
// kickoff/resume need Claude's tool path (claude -p --session-id),
|
|
1481
|
+
// which the interactive runner provides. Pass the kit mode + identity
|
|
1482
|
+
// via env so the runner picks the kit branch instead of the 4-phase
|
|
1483
|
+
// Groq flow.
|
|
1484
|
+
if (taskPrompt) {
|
|
1485
|
+
promptFile = path.join(os.tmpdir(), `knoxis-task-${msg.requestId || Date.now()}.txt`);
|
|
1486
|
+
fs.writeFileSync(promptFile, taskPrompt, 'utf8');
|
|
1487
|
+
}
|
|
1488
|
+
const kitEnv = { KNOXIS_WORKSPACE_PATH: wsDir, KNOXIS_KIT_MODE: kitMode };
|
|
1489
|
+
if (promptFile) kitEnv.KNOXIS_TASK_FILE = promptFile;
|
|
1490
|
+
if (archetype) kitEnv.KNOXIS_KIT_ARCHETYPE = archetype;
|
|
1491
|
+
if (pattern) kitEnv.KNOXIS_KIT_PATTERN = pattern;
|
|
1492
|
+
if (productSlug) kitEnv.KNOXIS_PRODUCT_SLUG = productSlug;
|
|
1493
|
+
if (projectSlug) kitEnv.KNOXIS_PROJECT_SLUG = projectSlug;
|
|
1494
|
+
if (portalUserId) kitEnv.KNOXIS_USER_ID = portalUserId;
|
|
1495
|
+
if (portalWorkspaceId) kitEnv.KNOXIS_WORKSPACE_ID = portalWorkspaceId;
|
|
1496
|
+
if (portalTaskIds && portalTaskIds.length) kitEnv.KNOXIS_TASK_IDS = portalTaskIds.join(',');
|
|
1497
|
+
command = buildEnvCommand(kitEnv, `node "${scriptPath}"`);
|
|
1498
|
+
console.log(` 🧰 Kit (interactive runner): ${kitMode}${archetype ? `/${archetype}` : ''} — ${scriptPath}`);
|
|
1499
|
+
} else {
|
|
1500
|
+
console.warn(` ⚠️ knoxis-interactive-pair.js not found — cannot run kit mode ${kitMode}`);
|
|
1501
|
+
}
|
|
1502
|
+
} else if (routeViaPairProgram && ppScript) {
|
|
1453
1503
|
command = buildPairProgramCommand({
|
|
1454
1504
|
scriptPath: ppScript,
|
|
1455
1505
|
workspace: wsDir,
|