coder-config 0.50.7-beta → 0.50.8-beta
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/lib/cli.js +10 -4
- package/lib/constants.js +1 -1
- package/lib/loops.js +63 -1
- package/package.json +1 -1
- package/ui/routes/loops.js +7 -0
- package/ui/server.cjs +4 -0
package/lib/cli.js
CHANGED
|
@@ -263,10 +263,16 @@ function runCli(manager) {
|
|
|
263
263
|
manager.loopHistory();
|
|
264
264
|
} else if (args[1] === 'config') {
|
|
265
265
|
const updates = {};
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
// Dot-notation key=value: coder-config loop config heartbeat.staleThresholdMinutes 30
|
|
267
|
+
if (args[2] && args[3] && !args[2].startsWith('--')) {
|
|
268
|
+
updates[args[2]] = args[3];
|
|
269
|
+
} else {
|
|
270
|
+
// Flag-based (backwards compatible)
|
|
271
|
+
const maxIterIdx = args.indexOf('--max-iterations');
|
|
272
|
+
if (maxIterIdx !== -1) updates.maxIterations = args[maxIterIdx + 1];
|
|
273
|
+
if (args.includes('--auto-approve-plan')) updates.autoApprovePlan = true;
|
|
274
|
+
if (args.includes('--no-auto-approve-plan')) updates.autoApprovePlan = false;
|
|
275
|
+
}
|
|
270
276
|
if (Object.keys(updates).length > 0) {
|
|
271
277
|
manager.loopConfig(updates);
|
|
272
278
|
} else {
|
package/lib/constants.js
CHANGED
package/lib/loops.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const { getDefaultHeartbeatConfig } = require('./heartbeat');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Get loops directory path
|
|
@@ -621,6 +622,32 @@ function archiveLoop(installDir, loopId) {
|
|
|
621
622
|
}
|
|
622
623
|
}
|
|
623
624
|
|
|
625
|
+
/**
|
|
626
|
+
* Set a nested value on an object using dot-notation path.
|
|
627
|
+
* Creates intermediate objects as needed.
|
|
628
|
+
*/
|
|
629
|
+
function setNestedValue(obj, dotPath, value) {
|
|
630
|
+
const parts = dotPath.split('.');
|
|
631
|
+
let current = obj;
|
|
632
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
633
|
+
if (current[parts[i]] === undefined || typeof current[parts[i]] !== 'object') {
|
|
634
|
+
current[parts[i]] = {};
|
|
635
|
+
}
|
|
636
|
+
current = current[parts[i]];
|
|
637
|
+
}
|
|
638
|
+
current[parts[parts.length - 1]] = value;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Auto-convert string values to appropriate types.
|
|
643
|
+
*/
|
|
644
|
+
function coerceValue(value) {
|
|
645
|
+
if (value === 'true') return true;
|
|
646
|
+
if (value === 'false') return false;
|
|
647
|
+
if (typeof value === 'string' && /^\d+$/.test(value)) return parseInt(value, 10);
|
|
648
|
+
return value;
|
|
649
|
+
}
|
|
650
|
+
|
|
624
651
|
/**
|
|
625
652
|
* Get/set loop configuration
|
|
626
653
|
*/
|
|
@@ -634,11 +661,46 @@ function loopConfig(installDir, updates = null) {
|
|
|
634
661
|
console.log(` Auto-approve Plan: ${data.config.autoApprovePlan}`);
|
|
635
662
|
console.log(` Max Clarify Iterations: ${data.config.maxClarifyIterations}`);
|
|
636
663
|
console.log(` Completion Promise: ${data.config.completionPromise || 'DONE'}`);
|
|
664
|
+
if (data.config.heartbeat) {
|
|
665
|
+
const hb = data.config.heartbeat;
|
|
666
|
+
const slack = hb.notifications && hb.notifications.slack;
|
|
667
|
+
const macos = hb.notifications && hb.notifications.macos;
|
|
668
|
+
console.log(' Heartbeat:');
|
|
669
|
+
console.log(` Stale Threshold: ${hb.staleThresholdMinutes !== undefined ? hb.staleThresholdMinutes + 'm' : '30m'}`);
|
|
670
|
+
console.log(` Iteration Limit: ${hb.iterationLimitPercent !== undefined ? hb.iterationLimitPercent + '%' : '80%'}`);
|
|
671
|
+
console.log(` Cooldown: ${hb.cooldownMinutes !== undefined ? hb.cooldownMinutes + 'm' : '15m'}`);
|
|
672
|
+
console.log(` macOS Notifications: ${macos && macos.enabled !== undefined ? (macos.enabled ? 'on' : 'off') : 'on'}`);
|
|
673
|
+
if (slack) {
|
|
674
|
+
const slackStatus = slack.enabled ? `on (${slack.channel || '#dev-loops'})` : 'off';
|
|
675
|
+
console.log(` Slack: ${slackStatus}`);
|
|
676
|
+
} else {
|
|
677
|
+
console.log(' Slack: off');
|
|
678
|
+
}
|
|
679
|
+
}
|
|
637
680
|
console.log('');
|
|
638
681
|
return data.config;
|
|
639
682
|
}
|
|
640
683
|
|
|
641
|
-
//
|
|
684
|
+
// Check for dot-notation heartbeat keys
|
|
685
|
+
const dotKeys = Object.keys(updates).filter(k => k.includes('.'));
|
|
686
|
+
if (dotKeys.length > 0) {
|
|
687
|
+
// Initialize heartbeat config with defaults if not present
|
|
688
|
+
if (!data.config.heartbeat) {
|
|
689
|
+
data.config.heartbeat = getDefaultHeartbeatConfig();
|
|
690
|
+
}
|
|
691
|
+
for (const key of dotKeys) {
|
|
692
|
+
if (key.startsWith('heartbeat.')) {
|
|
693
|
+
const subPath = key.slice('heartbeat.'.length);
|
|
694
|
+
setNestedValue(data.config.heartbeat, subPath, coerceValue(updates[key]));
|
|
695
|
+
} else {
|
|
696
|
+
setNestedValue(data.config, key, coerceValue(updates[key]));
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
// Remove dot-notation keys so they don't fall through to flat handling
|
|
700
|
+
dotKeys.forEach(k => delete updates[k]);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Apply flat updates
|
|
642
704
|
if (updates.maxIterations !== undefined) {
|
|
643
705
|
data.config.maxIterations = parseInt(updates.maxIterations, 10);
|
|
644
706
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coder-config",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.8-beta",
|
|
4
4
|
"description": "Configuration manager for AI coding tools - Claude Code, Gemini CLI, Codex CLI, Antigravity. Manage MCPs, rules, permissions, memory, and workstreams.",
|
|
5
5
|
"author": "regression.io",
|
|
6
6
|
"main": "config-loader.js",
|
package/ui/routes/loops.js
CHANGED
|
@@ -598,7 +598,14 @@ function setupLoopHooks(projectPath) {
|
|
|
598
598
|
return installLoopHooks(null, projectPath);
|
|
599
599
|
}
|
|
600
600
|
|
|
601
|
+
function getHeartbeat(manager) {
|
|
602
|
+
if (!manager) return { error: 'Manager not available' };
|
|
603
|
+
const { heartbeat: runHeartbeat } = require('../../lib/heartbeat');
|
|
604
|
+
return runHeartbeat(manager.installDir);
|
|
605
|
+
}
|
|
606
|
+
|
|
601
607
|
module.exports = {
|
|
608
|
+
getHeartbeat,
|
|
602
609
|
getLoops,
|
|
603
610
|
getActiveLoop,
|
|
604
611
|
getLoop,
|
package/ui/server.cjs
CHANGED
|
@@ -886,6 +886,10 @@ class ConfigUIServer {
|
|
|
886
886
|
}
|
|
887
887
|
break;
|
|
888
888
|
|
|
889
|
+
case '/api/loops/heartbeat':
|
|
890
|
+
if (req.method === 'GET') return this.json(res, routes.loops.getHeartbeat(this.manager));
|
|
891
|
+
break;
|
|
892
|
+
|
|
889
893
|
case '/api/loops/tune-prompt':
|
|
890
894
|
if (req.method === 'POST') {
|
|
891
895
|
const result = await routes.loops.tunePrompt(
|