@magnusekdahl/parallix 1.2.1 → 1.3.1
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/index.js +190 -185
- package/lib/agents/agents.js +34 -4
- package/lib/agents/agents.ts +40 -5
- package/lib/commands/active.js +502 -579
- package/lib/commands/active.ts +665 -0
- package/lib/commands/checkpoint.js +98 -74
- package/lib/commands/checkpoint.ts +79 -0
- package/lib/commands/config.js +61 -31
- package/lib/commands/config.ts +47 -0
- package/lib/commands/coverage-gate.js +271 -246
- package/lib/commands/coverage-gate.ts +362 -0
- package/lib/commands/diff.js +140 -119
- package/lib/commands/diff.ts +125 -0
- package/lib/commands/draft.js +770 -923
- package/lib/commands/draft.ts +1021 -0
- package/lib/commands/handoff.js +588 -608
- package/lib/commands/handoff.ts +694 -0
- package/lib/commands/integrate.js +1331 -1468
- package/lib/commands/integrate.ts +1688 -0
- package/lib/commands/mission-start.js +279 -237
- package/lib/commands/mission-start.ts +263 -0
- package/lib/commands/rebase.js +606 -582
- package/lib/commands/rebase.ts +635 -0
- package/lib/commands/repair-handoff.js +181 -179
- package/lib/commands/repair-handoff.ts +228 -0
- package/lib/commands/resolve-conflict.js +121 -102
- package/lib/commands/resolve-conflict.ts +112 -0
- package/lib/commands/review.js +3 -4
- package/lib/commands/review.ts +14 -0
- package/lib/commands/setup-review.js +4 -5
- package/lib/commands/setup-review.ts +14 -0
- package/lib/commands/setup.js +3 -3
- package/lib/commands/setup.ts +2 -0
- package/lib/commands/stats-backfill.js +394 -359
- package/lib/commands/stats-backfill.ts +418 -0
- package/lib/commands/stats.js +1424 -1665
- package/lib/commands/stats.ts +2186 -0
- package/lib/commands/status.js +219 -200
- package/lib/commands/status.ts +219 -0
- package/lib/commands/verify.js +3 -1
- package/lib/commands/verify.ts +2 -0
- package/lib/core/nels.js +112 -127
- package/lib/core/nels.ts +189 -0
- package/lib/core/subagent-limit.js +15 -16
- package/lib/core/subagent-limit.ts +25 -0
- package/lib/index.js +142 -59
- package/lib/index.ts +134 -0
- package/lib/review/rebase.js +142 -145
- package/lib/review/rebase.ts +188 -0
- package/lib/review/review-adapter.js +99 -87
- package/lib/review/review-adapter.ts +198 -0
- package/lib/review/review-artifacts.js +486 -629
- package/lib/review/review-artifacts.ts +622 -0
- package/lib/review/review-commands.js +1220 -1373
- package/lib/review/review-commands.ts +1579 -0
- package/lib/review/review-events.js +544 -886
- package/lib/review/review-events.ts +954 -0
- package/lib/review/review-loop.js +945 -1020
- package/lib/review/review-loop.ts +1174 -0
- package/lib/review/review-polling.js +86 -115
- package/lib/review/review-polling.ts +194 -0
- package/lib/review/review-prompts.js +154 -189
- package/lib/review/review-prompts.ts +182 -0
- package/lib/review/review-state.js +246 -225
- package/lib/review/review-state.ts +353 -0
- package/lib/review/review.js +63 -84
- package/lib/review/review.ts +138 -0
- package/lib/tools/backlog.js +694 -680
- package/lib/tools/backlog.ts +835 -0
- package/lib/tools/forgejo.js +1270 -1405
- package/lib/tools/forgejo.ts +1793 -0
- package/lib/tools/gatekeeper.js +106 -96
- package/lib/tools/gatekeeper.ts +124 -0
- package/lib/tools/redgreen.js +200 -181
- package/lib/tools/redgreen.ts +221 -0
- package/lib/tools/sessions.js +58 -52
- package/lib/tools/sessions.ts +81 -0
- package/lib/tools/setup-review.js +902 -975
- package/lib/tools/setup-review.ts +1152 -0
- package/package.json +4 -4
- package/px.js +226 -208
package/index.js
CHANGED
|
@@ -1,209 +1,226 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
"use strict";
|
|
3
3
|
/**
|
|
4
4
|
* VisualBoard Parallix Coordination CLI
|
|
5
5
|
* ADR 0037 — Wave 1
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.KNOWN_COMMANDS = void 0;
|
|
45
|
+
exports.deriveAliases = deriveAliases;
|
|
46
|
+
exports.resolveAlias = resolveAlias;
|
|
47
|
+
exports.printAliases = printAliases;
|
|
48
|
+
exports.buildSuggestionSuffix = buildSuggestionSuffix;
|
|
49
|
+
exports.suggestCommand = suggestCommand;
|
|
50
|
+
exports.levenshteinDistance = levenshteinDistance;
|
|
51
|
+
exports.printUsage = printUsage;
|
|
52
|
+
exports.main = main;
|
|
53
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
54
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
55
|
+
const fmt = __importStar(require("./lib/core/fmt.js"));
|
|
56
|
+
const product_config_js_1 = require("./lib/core/product-config.js");
|
|
57
|
+
const state_map_js_1 = require("./lib/core/state-map.js");
|
|
14
58
|
// Fixed virtual-state → canonical-command invariants for alias derivation.
|
|
15
59
|
const STATE_COMMAND_MAP = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
60
|
+
ready: 'draft',
|
|
61
|
+
approved: 'integrate',
|
|
62
|
+
done: 'integrate',
|
|
19
63
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'diff',
|
|
64
|
+
exports.KNOWN_COMMANDS = [
|
|
65
|
+
'mission-start',
|
|
66
|
+
'verify-env',
|
|
67
|
+
'verify',
|
|
68
|
+
'setup',
|
|
69
|
+
'setup-review',
|
|
70
|
+
'draft',
|
|
71
|
+
'active',
|
|
72
|
+
'status',
|
|
73
|
+
'checkpoint',
|
|
74
|
+
'review',
|
|
75
|
+
'handoff',
|
|
76
|
+
'integrate',
|
|
77
|
+
'resolve-conflict',
|
|
78
|
+
'rebase',
|
|
79
|
+
'stats',
|
|
80
|
+
'aliases',
|
|
81
|
+
'config',
|
|
82
|
+
'diff',
|
|
40
83
|
];
|
|
41
|
-
|
|
42
84
|
const READ_ONLY_COMMANDS = new Set(['config']);
|
|
43
|
-
|
|
44
85
|
function loadStateMapForAliases(options = {}) {
|
|
45
|
-
|
|
86
|
+
return (0, state_map_js_1.loadStateMap)(options);
|
|
46
87
|
}
|
|
47
|
-
|
|
48
88
|
// Derives the command-alias table from state-map.json and fixed parallix invariants.
|
|
49
89
|
// Users only maintain state-map.json; aliases update automatically.
|
|
50
90
|
function deriveAliases(options = {}) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
91
|
+
const stateMap = loadStateMapForAliases(options);
|
|
92
|
+
const aliases = { ...STATE_COMMAND_MAP };
|
|
93
|
+
for (const [virtual, actual] of Object.entries(stateMap)) {
|
|
94
|
+
if (typeof actual === 'string' && actual !== virtual && STATE_COMMAND_MAP[virtual]) {
|
|
95
|
+
aliases[actual] = STATE_COMMAND_MAP[virtual];
|
|
96
|
+
}
|
|
56
97
|
}
|
|
57
|
-
|
|
58
|
-
return aliases;
|
|
98
|
+
return aliases;
|
|
59
99
|
}
|
|
60
|
-
|
|
61
100
|
function resolveAlias(command, aliases = deriveAliases()) {
|
|
62
|
-
|
|
101
|
+
return Object.prototype.hasOwnProperty.call(aliases, command) ? aliases[command] : null;
|
|
63
102
|
}
|
|
64
|
-
|
|
65
103
|
function printAliases(aliases, logFn = fmt.log.plain) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
104
|
+
const entries = Object.entries(aliases).sort(([a], [b]) => a.localeCompare(b));
|
|
105
|
+
if (entries.length === 0) {
|
|
106
|
+
logFn('No aliases configured.');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
logFn(fmt.bold('alias canonical'));
|
|
110
|
+
logFn(fmt.bold('--- ---'));
|
|
111
|
+
for (const [alias, canonical] of entries) {
|
|
112
|
+
logFn(`${alias.padEnd(30)}${canonical}`);
|
|
113
|
+
}
|
|
76
114
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
exitFn = process.exit,
|
|
85
|
-
errorFn = fmt.log.plainError,
|
|
86
|
-
logFn = fmt.log.plain,
|
|
87
|
-
loadAliasesFn = deriveAliases,
|
|
88
|
-
} = {}) {
|
|
89
|
-
const command = args[0];
|
|
90
|
-
|
|
91
|
-
if (command === '--version' || command === '-v') {
|
|
92
|
-
const pkg = requireFn(path.join(__dirname, 'package.json'));
|
|
93
|
-
logFn(pkg.name + '@' + pkg.version);
|
|
94
|
-
exitFn(0);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
99
|
-
printUsageFn();
|
|
100
|
-
exitFn(0);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (command === 'aliases') {
|
|
105
|
-
printAliases(loadAliasesFn({ rootDir: cwdFn() }), logFn);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const libPath = path.join(__dirname, 'lib', 'commands', `${command}.js`);
|
|
110
|
-
|
|
111
|
-
// Special case: verify-env is an alias for mission-start diagnostic.
|
|
112
|
-
// active is now its own command (real agent-start path).
|
|
113
|
-
let targetLib = libPath;
|
|
114
|
-
if (command === 'verify-env') {
|
|
115
|
-
targetLib = path.join(__dirname, 'lib', 'commands', 'mission-start.js');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (existsSyncFn(targetLib)) {
|
|
119
|
-
if (!READ_ONLY_COMMANDS.has(command)) {
|
|
120
|
-
const initResult = ensureStandaloneGitRepoFn(cwdFn());
|
|
121
|
-
if (initResult && initResult.failed) {
|
|
122
|
-
errorFn(fmt.status('FAIL', `Git init: ${initResult.message}`));
|
|
123
|
-
exitFn(1);
|
|
115
|
+
async function main(args = process.argv.slice(2), options = {}) {
|
|
116
|
+
const { existsSyncFn = node_fs_1.default.existsSync, cwdFn = () => process.cwd(), ensureStandaloneGitRepoFn = product_config_js_1.ensureStandaloneGitRepo, requireFn = require, printUsageFn = printUsage, exitFn = process.exit, errorFn = fmt.log.plainError, logFn = fmt.log.plain, loadAliasesFn = deriveAliases, } = options;
|
|
117
|
+
const command = args[0];
|
|
118
|
+
if (command === '--version' || command === '-v') {
|
|
119
|
+
const pkg = requireFn(node_path_1.default.join(__dirname, 'package.json'));
|
|
120
|
+
logFn(pkg.name + '@' + pkg.version);
|
|
121
|
+
exitFn(0);
|
|
124
122
|
return;
|
|
125
|
-
}
|
|
126
|
-
if (initResult && initResult.initialized) {
|
|
127
|
-
logFn(fmt.status('INFO', `Initialized git repository for standalone parallix in ${cwdFn()} (branch ${initResult.branch || 'main'}).`));
|
|
128
|
-
}
|
|
129
123
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
} else {
|
|
135
|
-
errorFn(fmt.status('FAIL', `Command module '${command}' does not export a function.`));
|
|
136
|
-
exitFn(1);
|
|
124
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
125
|
+
printUsageFn();
|
|
126
|
+
exitFn(0);
|
|
127
|
+
return;
|
|
137
128
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (canonical) {
|
|
142
|
-
logFn(fmt.status('INFO', `Resolving alias ${command} → ${canonical}`));
|
|
143
|
-
await main([canonical, ...args.slice(1)], { existsSyncFn, cwdFn, ensureStandaloneGitRepoFn, requireFn, printUsageFn, exitFn, errorFn, logFn, loadAliasesFn });
|
|
144
|
-
return;
|
|
129
|
+
if (command === 'aliases') {
|
|
130
|
+
printAliases(loadAliasesFn({ rootDir: cwdFn() }), logFn);
|
|
131
|
+
return;
|
|
145
132
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
133
|
+
const libPath = node_path_1.default.join(__dirname, 'lib', 'commands', `${command}.js`);
|
|
134
|
+
// Special case: verify-env is an alias for mission-start diagnostic.
|
|
135
|
+
// active is now its own command (real agent-start path).
|
|
136
|
+
let targetLib = libPath;
|
|
137
|
+
if (command === 'verify-env') {
|
|
138
|
+
targetLib = node_path_1.default.join(__dirname, 'lib', 'commands', 'mission-start.js');
|
|
139
|
+
}
|
|
140
|
+
if (existsSyncFn(targetLib)) {
|
|
141
|
+
if (!READ_ONLY_COMMANDS.has(command)) {
|
|
142
|
+
const initResult = ensureStandaloneGitRepoFn(cwdFn());
|
|
143
|
+
if (initResult && initResult.failed) {
|
|
144
|
+
errorFn(fmt.status('FAIL', `Git init: ${initResult.message}`));
|
|
145
|
+
exitFn(1);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (initResult && initResult.initialized) {
|
|
149
|
+
logFn(fmt.status('INFO', `Initialized git repository for standalone parallix in ${cwdFn()} (branch ${initResult.branch || 'main'}).`));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const cmdModule = requireFn(targetLib);
|
|
153
|
+
if (typeof cmdModule === 'function') {
|
|
154
|
+
await cmdModule(args.slice(1), { command });
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
errorFn(fmt.status('FAIL', `Command module '${command}' does not export a function.`));
|
|
158
|
+
exitFn(1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const aliases = loadAliasesFn({ rootDir: cwdFn() });
|
|
163
|
+
const canonical = resolveAlias(command, aliases);
|
|
164
|
+
if (canonical) {
|
|
165
|
+
logFn(fmt.status('INFO', `Resolving alias ${command} → ${canonical}`));
|
|
166
|
+
await main([canonical, ...args.slice(1)], { existsSyncFn, cwdFn, ensureStandaloneGitRepoFn, requireFn, printUsageFn, exitFn, errorFn, logFn, loadAliasesFn });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
errorFn(fmt.status('FAIL', `Unknown command: ${command}`));
|
|
170
|
+
const suggestion = suggestCommand(command);
|
|
171
|
+
if (suggestion) {
|
|
172
|
+
errorFn(fmt.status('INFO', `Did you mean: px ${suggestion}${buildSuggestionSuffix(suggestion)}`));
|
|
173
|
+
}
|
|
174
|
+
printUsageFn();
|
|
175
|
+
exitFn(1);
|
|
151
176
|
}
|
|
152
|
-
printUsageFn();
|
|
153
|
-
exitFn(1);
|
|
154
|
-
}
|
|
155
177
|
}
|
|
156
|
-
|
|
157
178
|
function buildSuggestionSuffix(command) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
179
|
+
if (command === 'diff' || command === 'resolve-conflict') {
|
|
180
|
+
return ' <slug>';
|
|
181
|
+
}
|
|
182
|
+
if (command === 'checkpoint') {
|
|
183
|
+
return ' <slug> <cp-name> "<next-action>"';
|
|
184
|
+
}
|
|
185
|
+
return '';
|
|
165
186
|
}
|
|
166
|
-
|
|
167
187
|
function suggestCommand(input) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
let best = null;
|
|
171
|
-
|
|
172
|
-
for (const candidate of KNOWN_COMMANDS) {
|
|
173
|
-
const distance = levenshteinDistance(normalizedInput, candidate);
|
|
174
|
-
if (distance > 2) continue;
|
|
175
|
-
if (!best || distance < best.distance) {
|
|
176
|
-
best = { candidate, distance };
|
|
188
|
+
if (!input) {
|
|
189
|
+
return null;
|
|
177
190
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
191
|
+
const normalizedInput = input.toLowerCase();
|
|
192
|
+
let best = null;
|
|
193
|
+
for (const candidate of exports.KNOWN_COMMANDS) {
|
|
194
|
+
const distance = levenshteinDistance(normalizedInput, candidate);
|
|
195
|
+
if (distance > 2) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (!best || distance < best.distance) {
|
|
199
|
+
best = { candidate, distance };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return best ? best.candidate : null;
|
|
181
203
|
}
|
|
182
|
-
|
|
183
204
|
function levenshteinDistance(a, b) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
for (let j = 0; j < cols; j += 1) dp[0][j] = j;
|
|
190
|
-
|
|
191
|
-
for (let i = 1; i < rows; i += 1) {
|
|
192
|
-
for (let j = 1; j < cols; j += 1) {
|
|
193
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
194
|
-
dp[i][j] = Math.min(
|
|
195
|
-
dp[i - 1][j] + 1,
|
|
196
|
-
dp[i][j - 1] + 1,
|
|
197
|
-
dp[i - 1][j - 1] + cost
|
|
198
|
-
);
|
|
205
|
+
const rows = a.length + 1;
|
|
206
|
+
const cols = b.length + 1;
|
|
207
|
+
const dp = Array.from({ length: rows }, () => Array(cols).fill(0));
|
|
208
|
+
for (let i = 0; i < rows; i += 1) {
|
|
209
|
+
dp[i][0] = i;
|
|
199
210
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
211
|
+
for (let j = 0; j < cols; j += 1) {
|
|
212
|
+
dp[0][j] = j;
|
|
213
|
+
}
|
|
214
|
+
for (let i = 1; i < rows; i += 1) {
|
|
215
|
+
for (let j = 1; j < cols; j += 1) {
|
|
216
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
217
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return dp[a.length][b.length];
|
|
203
221
|
}
|
|
204
|
-
|
|
205
222
|
function printUsage() {
|
|
206
|
-
|
|
223
|
+
fmt.log.plain(`
|
|
207
224
|
Usage: px <command> [args]
|
|
208
225
|
|
|
209
226
|
${fmt.bold('Core Commands:')}
|
|
@@ -239,19 +256,7 @@ ${fmt.bold('Notes:')}
|
|
|
239
256
|
- No npm dependencies — requires Node.js built-ins only.
|
|
240
257
|
`);
|
|
241
258
|
}
|
|
242
|
-
|
|
243
|
-
if (require.main === module) {
|
|
244
|
-
|
|
259
|
+
// Run main when executed directly (compiled to CJS, require.main === module applies)
|
|
260
|
+
if (typeof require !== 'undefined' && require.main === module) {
|
|
261
|
+
main();
|
|
245
262
|
}
|
|
246
|
-
|
|
247
|
-
module.exports = {
|
|
248
|
-
KNOWN_COMMANDS,
|
|
249
|
-
main,
|
|
250
|
-
printUsage,
|
|
251
|
-
printAliases,
|
|
252
|
-
suggestCommand,
|
|
253
|
-
buildSuggestionSuffix,
|
|
254
|
-
levenshteinDistance,
|
|
255
|
-
deriveAliases,
|
|
256
|
-
resolveAlias,
|
|
257
|
-
};
|
package/lib/agents/agents.js
CHANGED
|
@@ -51,6 +51,7 @@ exports.isInvalidAgentConfigError = isInvalidAgentConfigError;
|
|
|
51
51
|
exports.updateAgentBlock = updateAgentBlock;
|
|
52
52
|
exports.resolveBlocklistTargetPath = resolveBlocklistTargetPath;
|
|
53
53
|
exports.resolveNoOutputWatchdogConfig = resolveNoOutputWatchdogConfig;
|
|
54
|
+
exports.shouldPersistLaunchFailureBlock = shouldPersistLaunchFailureBlock;
|
|
54
55
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
55
56
|
const node_path_1 = __importDefault(require("node:path"));
|
|
56
57
|
const node_child_process_1 = require("node:child_process");
|
|
@@ -107,6 +108,17 @@ const KNOWN_AGENT_NAMES = Object.freeze([
|
|
|
107
108
|
'human'
|
|
108
109
|
]);
|
|
109
110
|
exports.KNOWN_AGENT_NAMES = KNOWN_AGENT_NAMES;
|
|
111
|
+
const NON_BLOCKING_LAUNCH_ERROR_PATTERNS = Object.freeze([
|
|
112
|
+
/\b(?:invalid|unknown|unsupported|unrecognized)\s+model\b/i,
|
|
113
|
+
/\bmodel\s+(?:identifier|id)\s+(?:is\s+)?invalid\b/i,
|
|
114
|
+
/\b(?:model\s+not\s+found|no\s+such\s+model)\b/i,
|
|
115
|
+
/\bunknown\s+option\b/i,
|
|
116
|
+
/\bauth(?:entication)?\b/i,
|
|
117
|
+
/\bunauthorized\b/i,
|
|
118
|
+
/\bforbidden\b/i,
|
|
119
|
+
/\bapi\s+key\b/i,
|
|
120
|
+
/\bread-only file system\b/i
|
|
121
|
+
]);
|
|
110
122
|
function workflowLauncherStatus(agent) {
|
|
111
123
|
const resolver = RESOLVERS[agent];
|
|
112
124
|
if (!resolver) {
|
|
@@ -148,6 +160,21 @@ function commandInPath(name) {
|
|
|
148
160
|
});
|
|
149
161
|
return result.status === 0 && result.stdout.trim().length > 0;
|
|
150
162
|
}
|
|
163
|
+
function shouldPersistLaunchFailureBlock(agent, result) {
|
|
164
|
+
if (!result || agent === 'custom') {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
const combined = [
|
|
168
|
+
result.stderr || '',
|
|
169
|
+
result.stdout || '',
|
|
170
|
+
result.error?.message || '',
|
|
171
|
+
result.error?.code || ''
|
|
172
|
+
].join('\n');
|
|
173
|
+
if (!combined.trim()) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return !NON_BLOCKING_LAUNCH_ERROR_PATTERNS.some(pattern => pattern.test(combined));
|
|
177
|
+
}
|
|
151
178
|
function buildInvalidAgentConfigError(configPath, scope, originalError) {
|
|
152
179
|
const location = node_path_1.default.resolve(configPath);
|
|
153
180
|
const detail = originalError && originalError.message ? originalError.message : 'invalid JSON';
|
|
@@ -790,10 +817,10 @@ async function startAgent(step, opts = { prompt: '' }) {
|
|
|
790
817
|
});
|
|
791
818
|
tried.add(chosen || '');
|
|
792
819
|
launched.add(chosen || '');
|
|
793
|
-
// Block
|
|
794
|
-
//
|
|
795
|
-
//
|
|
796
|
-
if (chosen
|
|
820
|
+
// Block retry candidates only when the failure looks transient. Deterministic
|
|
821
|
+
// setup/config errors (invalid model id, auth failure, read-only HOME, etc.)
|
|
822
|
+
// should fall through to the next family without poisoning agents.local.json.
|
|
823
|
+
if (shouldPersistLaunchFailureBlock(chosen || '', result)) {
|
|
797
824
|
const blockUntil = (0, limit_hit_js_1.formatBlockUntil)(new Date(Date.now() + limit_hit_js_1.DEFAULT_FALLBACK_HOURS * 60 * 60 * 1000));
|
|
798
825
|
try {
|
|
799
826
|
const blockResult = updateAgentBlockFn(chosen || '', blockUntil);
|
|
@@ -803,6 +830,9 @@ async function startAgent(step, opts = { prompt: '' }) {
|
|
|
803
830
|
log(fmt.status('WARN', `Could not persist blocklist entry for ${fmt.agent(chosen || '')}: ${err.message}`));
|
|
804
831
|
}
|
|
805
832
|
}
|
|
833
|
+
else {
|
|
834
|
+
log(fmt.status('INFO', `Skipping blocklist write for ${fmt.agent(chosen || '')}; launch failure looks like a deterministic config/setup error.`));
|
|
835
|
+
}
|
|
806
836
|
chosen = undefined;
|
|
807
837
|
continue;
|
|
808
838
|
}
|
package/lib/agents/agents.ts
CHANGED
|
@@ -22,6 +22,14 @@ interface LauncherStatus {
|
|
|
22
22
|
reason?: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
interface LaunchResultLike {
|
|
26
|
+
stdout?: string;
|
|
27
|
+
stderr?: string;
|
|
28
|
+
status?: number | null;
|
|
29
|
+
signal?: string | null;
|
|
30
|
+
error?: {code?: string, message?: string} | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
type AgentConfig = { blocklist?: {[key: string]: any}, steps?: {[key: string]: any} };
|
|
26
34
|
|
|
27
35
|
interface StartAgentOptions {
|
|
@@ -100,6 +108,18 @@ const KNOWN_AGENT_NAMES = Object.freeze([
|
|
|
100
108
|
'human'
|
|
101
109
|
]);
|
|
102
110
|
|
|
111
|
+
const NON_BLOCKING_LAUNCH_ERROR_PATTERNS = Object.freeze([
|
|
112
|
+
/\b(?:invalid|unknown|unsupported|unrecognized)\s+model\b/i,
|
|
113
|
+
/\bmodel\s+(?:identifier|id)\s+(?:is\s+)?invalid\b/i,
|
|
114
|
+
/\b(?:model\s+not\s+found|no\s+such\s+model)\b/i,
|
|
115
|
+
/\bunknown\s+option\b/i,
|
|
116
|
+
/\bauth(?:entication)?\b/i,
|
|
117
|
+
/\bunauthorized\b/i,
|
|
118
|
+
/\bforbidden\b/i,
|
|
119
|
+
/\bapi\s+key\b/i,
|
|
120
|
+
/\bread-only file system\b/i
|
|
121
|
+
]);
|
|
122
|
+
|
|
103
123
|
function workflowLauncherStatus(agent: string): LauncherStatus {
|
|
104
124
|
const resolver = RESOLVERS[agent];
|
|
105
125
|
if (!resolver) {
|
|
@@ -146,6 +166,18 @@ function commandInPath(name: string) {
|
|
|
146
166
|
return result.status === 0 && result.stdout.trim().length > 0;
|
|
147
167
|
}
|
|
148
168
|
|
|
169
|
+
function shouldPersistLaunchFailureBlock(agent: string, result: LaunchResultLike | null | undefined) {
|
|
170
|
+
if (!result || agent === 'custom') {return false;}
|
|
171
|
+
const combined = [
|
|
172
|
+
result.stderr || '',
|
|
173
|
+
result.stdout || '',
|
|
174
|
+
result.error?.message || '',
|
|
175
|
+
result.error?.code || ''
|
|
176
|
+
].join('\n');
|
|
177
|
+
if (!combined.trim()) {return true;}
|
|
178
|
+
return !NON_BLOCKING_LAUNCH_ERROR_PATTERNS.some(pattern => pattern.test(combined));
|
|
179
|
+
}
|
|
180
|
+
|
|
149
181
|
function buildInvalidAgentConfigError(configPath: string, scope: string, originalError: {message?: string} | null) {
|
|
150
182
|
const location = path.resolve(configPath);
|
|
151
183
|
const detail = originalError && originalError.message ? originalError.message : 'invalid JSON';
|
|
@@ -869,10 +901,10 @@ async function startAgent(step: string, opts: StartAgentOptions = { prompt: '' }
|
|
|
869
901
|
});
|
|
870
902
|
tried.add(chosen || '');
|
|
871
903
|
launched.add(chosen || '');
|
|
872
|
-
// Block
|
|
873
|
-
//
|
|
874
|
-
//
|
|
875
|
-
if (chosen
|
|
904
|
+
// Block retry candidates only when the failure looks transient. Deterministic
|
|
905
|
+
// setup/config errors (invalid model id, auth failure, read-only HOME, etc.)
|
|
906
|
+
// should fall through to the next family without poisoning agents.local.json.
|
|
907
|
+
if (shouldPersistLaunchFailureBlock(chosen || '', result)) {
|
|
876
908
|
const blockUntil = formatBlockUntil(new Date(Date.now() + DEFAULT_FALLBACK_HOURS * 60 * 60 * 1000));
|
|
877
909
|
try {
|
|
878
910
|
const blockResult = updateAgentBlockFn(chosen || '', blockUntil);
|
|
@@ -880,6 +912,8 @@ async function startAgent(step: string, opts: StartAgentOptions = { prompt: '' }
|
|
|
880
912
|
} catch (err) {
|
|
881
913
|
log(fmt.status('WARN', `Could not persist blocklist entry for ${fmt.agent(chosen || '')}: ${(err as any).message}`));
|
|
882
914
|
}
|
|
915
|
+
} else {
|
|
916
|
+
log(fmt.status('INFO', `Skipping blocklist write for ${fmt.agent(chosen || '')}; launch failure looks like a deterministic config/setup error.`));
|
|
883
917
|
}
|
|
884
918
|
chosen = undefined;
|
|
885
919
|
continue;
|
|
@@ -926,5 +960,6 @@ export {
|
|
|
926
960
|
isInvalidAgentConfigError,
|
|
927
961
|
updateAgentBlock,
|
|
928
962
|
resolveBlocklistTargetPath,
|
|
929
|
-
resolveNoOutputWatchdogConfig
|
|
963
|
+
resolveNoOutputWatchdogConfig,
|
|
964
|
+
shouldPersistLaunchFailureBlock
|
|
930
965
|
};
|