juno-code 1.0.49 → 1.0.50
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/README.md +417 -203
- package/dist/bin/cli.d.mts +1 -1
- package/dist/bin/cli.d.ts +1 -1
- package/dist/bin/cli.js +1736 -976
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +1735 -975
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/bin/feedback-collector.js.map +1 -1
- package/dist/bin/feedback-collector.mjs.map +1 -1
- package/dist/index.d.mts +33 -7
- package/dist/index.d.ts +33 -7
- package/dist/index.js +202 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +202 -27
- package/dist/index.mjs.map +1 -1
- package/dist/templates/scripts/install_requirements.sh +41 -3
- package/dist/templates/scripts/kanban.sh +4 -0
- package/dist/templates/services/__pycache__/pi.cpython-313.pyc +0 -0
- package/dist/templates/services/pi.py +1281 -238
- package/dist/templates/skills/claude/kanban-workflow/SKILL.md +138 -0
- package/dist/templates/skills/claude/plan-kanban-tasks/SKILL.md +1 -1
- package/dist/templates/skills/claude/ralph-loop/scripts/kanban.sh +4 -0
- package/dist/templates/skills/claude/understand-project/SKILL.md +1 -1
- package/dist/templates/skills/codex/kanban-workflow/SKILL.md +139 -0
- package/dist/templates/skills/codex/plan-kanban-tasks/SKILL.md +32 -0
- package/dist/templates/skills/codex/ralph-loop/scripts/kanban.sh +4 -0
- package/dist/templates/skills/codex/understand-project/SKILL.md +46 -0
- package/dist/templates/skills/pi/kanban-workflow/SKILL.md +139 -0
- package/dist/templates/skills/pi/plan-kanban-tasks/SKILL.md +1 -1
- package/dist/templates/skills/pi/ralph-loop/SKILL.md +4 -0
- package/dist/templates/skills/pi/understand-project/SKILL.md +1 -1
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -62,7 +62,7 @@ var __export = (target, all) => {
|
|
|
62
62
|
exports.version = void 0;
|
|
63
63
|
var init_version = __esm({
|
|
64
64
|
"src/version.ts"() {
|
|
65
|
-
exports.version = "1.0.
|
|
65
|
+
exports.version = "1.0.50";
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
68
|
|
|
@@ -1013,6 +1013,9 @@ var init_shell_backend = __esm({
|
|
|
1013
1013
|
args.push("--disallowedTools");
|
|
1014
1014
|
args.push(...request.arguments.disallowedTools);
|
|
1015
1015
|
}
|
|
1016
|
+
if (isPython && request.arguments?.thinking) {
|
|
1017
|
+
args.push("--thinking", String(request.arguments.thinking));
|
|
1018
|
+
}
|
|
1016
1019
|
if (isPython && request.arguments?.resume) {
|
|
1017
1020
|
args.push("--resume", String(request.arguments.resume));
|
|
1018
1021
|
}
|
|
@@ -1116,7 +1119,8 @@ var init_shell_backend = __esm({
|
|
|
1116
1119
|
subAgentResponse = JSON.parse(captured);
|
|
1117
1120
|
}
|
|
1118
1121
|
} catch (error) {
|
|
1119
|
-
|
|
1122
|
+
const isNotFound = error?.code === "ENOENT";
|
|
1123
|
+
if (!isNotFound && this.config?.debug) {
|
|
1120
1124
|
engineLogger.warn(
|
|
1121
1125
|
`Failed to read subagent capture: ${error instanceof Error ? error.message : String(error)}`
|
|
1122
1126
|
);
|
|
@@ -1327,6 +1331,8 @@ var init_shell_backend = __esm({
|
|
|
1327
1331
|
delete inner.type;
|
|
1328
1332
|
sanitizedPiEvent.sub_agent_response = inner;
|
|
1329
1333
|
}
|
|
1334
|
+
const usage = piEvent.usage;
|
|
1335
|
+
const totalCostUsd = typeof piEvent.total_cost_usd === "number" ? piEvent.total_cost_usd : typeof usage?.cost?.total === "number" ? usage.cost.total : void 0;
|
|
1330
1336
|
const structuredPayload = {
|
|
1331
1337
|
type: "result",
|
|
1332
1338
|
subtype: piEvent.subtype || (isError ? "error" : "success"),
|
|
@@ -1337,7 +1343,8 @@ var init_shell_backend = __esm({
|
|
|
1337
1343
|
session_id: piEvent.session_id,
|
|
1338
1344
|
exit_code: result.exitCode,
|
|
1339
1345
|
duration_ms: piEvent.duration_ms ?? result.duration,
|
|
1340
|
-
|
|
1346
|
+
total_cost_usd: totalCostUsd,
|
|
1347
|
+
usage,
|
|
1341
1348
|
sub_agent_response: sanitizedPiEvent
|
|
1342
1349
|
};
|
|
1343
1350
|
const metadata = {
|
|
@@ -1782,13 +1789,25 @@ var JunoTaskConfigSchema = zod.z.object({
|
|
|
1782
1789
|
// Logging settings
|
|
1783
1790
|
logLevel: LogLevelSchema.describe("Logging level for the application"),
|
|
1784
1791
|
logFile: zod.z.string().optional().describe("Path to log file (optional)"),
|
|
1785
|
-
verbose: zod.z.
|
|
1792
|
+
verbose: zod.z.preprocess(
|
|
1793
|
+
(val) => {
|
|
1794
|
+
if (val === true) return 1;
|
|
1795
|
+
if (val === false) return 0;
|
|
1796
|
+
if (typeof val === "string") {
|
|
1797
|
+
const lower = val.toLowerCase().trim();
|
|
1798
|
+
if (lower === "true" || lower === "yes") return 1;
|
|
1799
|
+
if (lower === "false" || lower === "no") return 0;
|
|
1800
|
+
}
|
|
1801
|
+
return val;
|
|
1802
|
+
},
|
|
1803
|
+
zod.z.number().int().min(0).max(2)
|
|
1804
|
+
).describe("Verbosity level: 0=quiet, 1=normal+helping texts (default), 2=debug+hooks"),
|
|
1786
1805
|
quiet: zod.z.boolean().describe("Enable quiet mode (minimal output)"),
|
|
1787
1806
|
// MCP settings
|
|
1788
1807
|
mcpTimeout: zod.z.number().int().min(1e3).max(864e5).describe("MCP server timeout in milliseconds"),
|
|
1789
1808
|
mcpRetries: zod.z.number().int().min(0).max(10).describe("Number of retries for MCP operations"),
|
|
1790
1809
|
mcpServerPath: zod.z.string().optional().describe("Path to MCP server executable (auto-discovered if not specified)"),
|
|
1791
|
-
mcpServerName: zod.z.string().optional().describe(
|
|
1810
|
+
mcpServerName: zod.z.string().optional().describe("Named MCP server to connect to"),
|
|
1792
1811
|
// Hook settings
|
|
1793
1812
|
hookCommandTimeout: zod.z.number().int().min(1e3).max(36e5).optional().describe(
|
|
1794
1813
|
"Timeout for individual hook commands in milliseconds (default: 300000 = 5 minutes)"
|
|
@@ -1803,6 +1822,11 @@ var JunoTaskConfigSchema = zod.z.object({
|
|
|
1803
1822
|
// Paths
|
|
1804
1823
|
workingDirectory: zod.z.string().describe("Working directory for task execution"),
|
|
1805
1824
|
sessionDirectory: zod.z.string().describe("Directory for storing session data"),
|
|
1825
|
+
// Project environment bootstrap
|
|
1826
|
+
envFilePath: zod.z.string().optional().describe(
|
|
1827
|
+
"Path to the project env file loaded before execution (relative to project root or absolute)"
|
|
1828
|
+
),
|
|
1829
|
+
envFileCopied: zod.z.boolean().optional().describe("Tracks whether configured env file has been initialized from .env.juno"),
|
|
1806
1830
|
// Hooks configuration
|
|
1807
1831
|
hooks: HooksSchema.describe(
|
|
1808
1832
|
"Hook system configuration for executing commands at specific lifecycle events"
|
|
@@ -1817,14 +1841,12 @@ var DEFAULT_CONFIG = {
|
|
|
1817
1841
|
defaultMaxIterations: 1,
|
|
1818
1842
|
// Logging settings
|
|
1819
1843
|
logLevel: "info",
|
|
1820
|
-
verbose:
|
|
1844
|
+
verbose: 1,
|
|
1821
1845
|
quiet: false,
|
|
1822
1846
|
// MCP settings (also used by shell backend)
|
|
1823
1847
|
mcpTimeout: 432e5,
|
|
1824
1848
|
// 43200 seconds (12 hours) - default for long-running shell backend operations
|
|
1825
1849
|
mcpRetries: 3,
|
|
1826
|
-
mcpServerName: "roundtable-ai",
|
|
1827
|
-
// Default to roundtable-ai server
|
|
1828
1850
|
// Quota/hourly limit settings
|
|
1829
1851
|
onHourlyLimit: "raise",
|
|
1830
1852
|
// Default to exit immediately when hourly limit is reached
|
|
@@ -1834,6 +1856,9 @@ var DEFAULT_CONFIG = {
|
|
|
1834
1856
|
// Paths
|
|
1835
1857
|
workingDirectory: process.cwd(),
|
|
1836
1858
|
sessionDirectory: path4__namespace.join(process.cwd(), ".juno_task"),
|
|
1859
|
+
// Project environment bootstrap
|
|
1860
|
+
envFilePath: ".env.juno",
|
|
1861
|
+
envFileCopied: false,
|
|
1837
1862
|
// Hooks configuration - populated with default hooks template
|
|
1838
1863
|
hooks: getDefaultHooks()
|
|
1839
1864
|
};
|
|
@@ -1846,6 +1871,7 @@ var GLOBAL_CONFIG_FILE_NAMES = [
|
|
|
1846
1871
|
// Will look for 'junoCode' field
|
|
1847
1872
|
];
|
|
1848
1873
|
var PROJECT_CONFIG_FILE = ".juno_task/config.json";
|
|
1874
|
+
var DEFAULT_PROJECT_ENV_FILE = ".env.juno";
|
|
1849
1875
|
function resolvePath(inputPath, basePath = process.cwd()) {
|
|
1850
1876
|
if (path4__namespace.isAbsolute(inputPath)) {
|
|
1851
1877
|
return inputPath;
|
|
@@ -1867,7 +1893,12 @@ function loadConfigFromEnv() {
|
|
|
1867
1893
|
for (const [envVar, configKey] of Object.entries(ENV_VAR_MAPPING)) {
|
|
1868
1894
|
const value = process.env[envVar];
|
|
1869
1895
|
if (value !== void 0) {
|
|
1870
|
-
|
|
1896
|
+
let parsed = parseEnvValue(value);
|
|
1897
|
+
if (configKey === "verbose") {
|
|
1898
|
+
if (parsed === true) parsed = 1;
|
|
1899
|
+
else if (parsed === false) parsed = 0;
|
|
1900
|
+
}
|
|
1901
|
+
config[configKey] = parsed;
|
|
1871
1902
|
}
|
|
1872
1903
|
}
|
|
1873
1904
|
return config;
|
|
@@ -2101,6 +2132,95 @@ function validateConfig(config) {
|
|
|
2101
2132
|
throw error;
|
|
2102
2133
|
}
|
|
2103
2134
|
}
|
|
2135
|
+
function parseEnvFileContent(content) {
|
|
2136
|
+
const envVars = {};
|
|
2137
|
+
const lines = content.split(/\r?\n/);
|
|
2138
|
+
for (const rawLine of lines) {
|
|
2139
|
+
const trimmedLine = rawLine.trim();
|
|
2140
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
2141
|
+
continue;
|
|
2142
|
+
}
|
|
2143
|
+
const line = trimmedLine.startsWith("export ") ? trimmedLine.slice(7).trim() : trimmedLine;
|
|
2144
|
+
const separatorIndex = line.indexOf("=");
|
|
2145
|
+
if (separatorIndex === -1) {
|
|
2146
|
+
continue;
|
|
2147
|
+
}
|
|
2148
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
2149
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
2150
|
+
continue;
|
|
2151
|
+
}
|
|
2152
|
+
let value = line.slice(separatorIndex + 1).trim();
|
|
2153
|
+
if ((value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) && value.length >= 2) {
|
|
2154
|
+
const quote = value[0];
|
|
2155
|
+
value = value.slice(1, -1);
|
|
2156
|
+
if (quote === '"') {
|
|
2157
|
+
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ");
|
|
2158
|
+
}
|
|
2159
|
+
} else {
|
|
2160
|
+
const inlineCommentIndex = value.indexOf(" #");
|
|
2161
|
+
if (inlineCommentIndex >= 0) {
|
|
2162
|
+
value = value.slice(0, inlineCommentIndex).trimEnd();
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
envVars[key] = value;
|
|
2166
|
+
}
|
|
2167
|
+
return envVars;
|
|
2168
|
+
}
|
|
2169
|
+
async function loadEnvFileIntoProcess(envFilePath) {
|
|
2170
|
+
try {
|
|
2171
|
+
const content = await fs3.promises.readFile(envFilePath, "utf-8");
|
|
2172
|
+
const parsed = parseEnvFileContent(content);
|
|
2173
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
2174
|
+
process.env[key] = value;
|
|
2175
|
+
}
|
|
2176
|
+
} catch (error) {
|
|
2177
|
+
console.warn(`Warning: Failed to load env file ${envFilePath}: ${error}`);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
async function ensureAndLoadProjectEnv(baseDir) {
|
|
2181
|
+
const configPath = path4__namespace.join(baseDir, PROJECT_CONFIG_FILE);
|
|
2182
|
+
const defaultEnvPath = resolvePath(DEFAULT_PROJECT_ENV_FILE, baseDir);
|
|
2183
|
+
await fs__default.default.ensureFile(defaultEnvPath);
|
|
2184
|
+
let existingConfig = null;
|
|
2185
|
+
if (await fs__default.default.pathExists(configPath)) {
|
|
2186
|
+
try {
|
|
2187
|
+
existingConfig = await fs__default.default.readJson(configPath);
|
|
2188
|
+
} catch (error) {
|
|
2189
|
+
console.warn(`Warning: Failed to read ${configPath} for env bootstrap: ${error}`);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
const configuredEnvPathRaw = existingConfig && typeof existingConfig.envFilePath === "string" && existingConfig.envFilePath ? existingConfig.envFilePath : DEFAULT_PROJECT_ENV_FILE;
|
|
2193
|
+
const configuredEnvPath = resolvePath(configuredEnvPathRaw, baseDir);
|
|
2194
|
+
let envFileCopied = existingConfig && typeof existingConfig.envFileCopied === "boolean" ? existingConfig.envFileCopied : false;
|
|
2195
|
+
let needsConfigUpdate = false;
|
|
2196
|
+
if (configuredEnvPath !== defaultEnvPath) {
|
|
2197
|
+
const configuredExists = await fs__default.default.pathExists(configuredEnvPath);
|
|
2198
|
+
if (!configuredExists) {
|
|
2199
|
+
await fs__default.default.ensureDir(path4__namespace.dirname(configuredEnvPath));
|
|
2200
|
+
if (!envFileCopied) {
|
|
2201
|
+
await fs3.promises.copyFile(defaultEnvPath, configuredEnvPath);
|
|
2202
|
+
} else {
|
|
2203
|
+
await fs__default.default.ensureFile(configuredEnvPath);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
if (!envFileCopied) {
|
|
2207
|
+
envFileCopied = true;
|
|
2208
|
+
needsConfigUpdate = true;
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
if (existingConfig && (needsConfigUpdate || typeof existingConfig.envFilePath !== "string" || typeof existingConfig.envFileCopied !== "boolean")) {
|
|
2212
|
+
const updatedConfig = {
|
|
2213
|
+
...existingConfig,
|
|
2214
|
+
envFilePath: configuredEnvPathRaw,
|
|
2215
|
+
envFileCopied
|
|
2216
|
+
};
|
|
2217
|
+
await fs__default.default.writeJson(configPath, updatedConfig, { spaces: 2 });
|
|
2218
|
+
}
|
|
2219
|
+
await loadEnvFileIntoProcess(defaultEnvPath);
|
|
2220
|
+
if (configuredEnvPath !== defaultEnvPath) {
|
|
2221
|
+
await loadEnvFileIntoProcess(configuredEnvPath);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2104
2224
|
async function ensureHooksConfig(baseDir) {
|
|
2105
2225
|
try {
|
|
2106
2226
|
const configDir = path4__namespace.join(baseDir, ".juno_task");
|
|
@@ -2136,6 +2256,14 @@ async function ensureHooksConfig(baseDir) {
|
|
|
2136
2256
|
existingConfig.defaultMaxIterations = DEFAULT_CONFIG.defaultMaxIterations;
|
|
2137
2257
|
needsUpdate = true;
|
|
2138
2258
|
}
|
|
2259
|
+
if (!existingConfig.envFilePath) {
|
|
2260
|
+
existingConfig.envFilePath = DEFAULT_PROJECT_ENV_FILE;
|
|
2261
|
+
needsUpdate = true;
|
|
2262
|
+
}
|
|
2263
|
+
if (typeof existingConfig.envFileCopied !== "boolean") {
|
|
2264
|
+
existingConfig.envFileCopied = false;
|
|
2265
|
+
needsUpdate = true;
|
|
2266
|
+
}
|
|
2139
2267
|
if (needsUpdate) {
|
|
2140
2268
|
await fs__default.default.writeJson(configPath, existingConfig, { spaces: 2 });
|
|
2141
2269
|
}
|
|
@@ -2147,6 +2275,7 @@ async function ensureHooksConfig(baseDir) {
|
|
|
2147
2275
|
async function loadConfig(options = {}) {
|
|
2148
2276
|
const { baseDir = process.cwd(), configFile, cliConfig } = options;
|
|
2149
2277
|
await ensureHooksConfig(baseDir);
|
|
2278
|
+
await ensureAndLoadProjectEnv(baseDir);
|
|
2150
2279
|
const loader = new ConfigLoader(baseDir);
|
|
2151
2280
|
loader.fromEnvironment();
|
|
2152
2281
|
if (configFile) {
|
|
@@ -2179,7 +2308,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2179
2308
|
logContext = "SYSTEM" /* SYSTEM */
|
|
2180
2309
|
} = options;
|
|
2181
2310
|
const contextLogger = logger.child(logContext);
|
|
2182
|
-
contextLogger.
|
|
2311
|
+
contextLogger.debug(`Starting hook execution: ${hookType}`, {
|
|
2183
2312
|
context,
|
|
2184
2313
|
workingDirectory: context.workingDirectory || process.cwd(),
|
|
2185
2314
|
commandTimeout,
|
|
@@ -2208,13 +2337,13 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2208
2337
|
commandsFailed: 0
|
|
2209
2338
|
};
|
|
2210
2339
|
}
|
|
2211
|
-
contextLogger.
|
|
2340
|
+
contextLogger.debug(`Executing ${hook.commands.length} commands for hook ${hookType}`);
|
|
2212
2341
|
const commandResults = [];
|
|
2213
2342
|
let commandsFailed = 0;
|
|
2214
2343
|
for (let i = 0; i < hook.commands.length; i++) {
|
|
2215
2344
|
const command = hook.commands[i];
|
|
2216
2345
|
const commandStartTime = Date.now();
|
|
2217
|
-
contextLogger.
|
|
2346
|
+
contextLogger.debug(`Executing command ${i + 1}/${hook.commands.length}: ${command}`, {
|
|
2218
2347
|
commandIndex: i,
|
|
2219
2348
|
totalCommands: hook.commands.length
|
|
2220
2349
|
});
|
|
@@ -2262,7 +2391,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2262
2391
|
};
|
|
2263
2392
|
commandResults.push(commandResult);
|
|
2264
2393
|
if (success2) {
|
|
2265
|
-
contextLogger.
|
|
2394
|
+
contextLogger.debug(`Command completed successfully`, {
|
|
2266
2395
|
command,
|
|
2267
2396
|
exitCode: result2.exitCode,
|
|
2268
2397
|
duration,
|
|
@@ -2343,7 +2472,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2343
2472
|
commandsExecuted,
|
|
2344
2473
|
commandsFailed
|
|
2345
2474
|
};
|
|
2346
|
-
contextLogger.
|
|
2475
|
+
contextLogger.debug(`Hook execution completed`, {
|
|
2347
2476
|
hookType,
|
|
2348
2477
|
totalDuration,
|
|
2349
2478
|
commandsExecuted,
|
|
@@ -2354,7 +2483,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2354
2483
|
}
|
|
2355
2484
|
async function executeHooks(hookTypes, hooks, context = {}, options = {}) {
|
|
2356
2485
|
const results = [];
|
|
2357
|
-
hookLogger.
|
|
2486
|
+
hookLogger.debug(`Starting batch hook execution`, {
|
|
2358
2487
|
hookTypes,
|
|
2359
2488
|
context
|
|
2360
2489
|
});
|
|
@@ -2365,7 +2494,7 @@ async function executeHooks(hookTypes, hooks, context = {}, options = {}) {
|
|
|
2365
2494
|
const totalSuccess = results.every((r) => r.success);
|
|
2366
2495
|
const totalCommands = results.reduce((sum, r) => sum + r.commandsExecuted, 0);
|
|
2367
2496
|
const totalFailed = results.reduce((sum, r) => sum + r.commandsFailed, 0);
|
|
2368
|
-
hookLogger.
|
|
2497
|
+
hookLogger.debug(`Batch hook execution completed`, {
|
|
2369
2498
|
hookTypes,
|
|
2370
2499
|
totalHooks: results.length,
|
|
2371
2500
|
totalCommands,
|
|
@@ -2657,6 +2786,30 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2657
2786
|
*/
|
|
2658
2787
|
setupProgressTracking() {
|
|
2659
2788
|
}
|
|
2789
|
+
/**
|
|
2790
|
+
* Display hook execution output to stderr at verbose level 2 (debug+hooks).
|
|
2791
|
+
* At level 2: shows hook name, command stdout/stderr output.
|
|
2792
|
+
* At level 0-1: output is suppressed (hooks still execute).
|
|
2793
|
+
*/
|
|
2794
|
+
displayHookOutput(hookResult) {
|
|
2795
|
+
if (this.engineConfig.config.verbose < 2) return;
|
|
2796
|
+
for (const cmdResult of hookResult.commandResults) {
|
|
2797
|
+
const prefix = `[hook:${hookResult.hookType}]`;
|
|
2798
|
+
if (cmdResult.stdout) {
|
|
2799
|
+
for (const line of cmdResult.stdout.split("\n")) {
|
|
2800
|
+
if (line.trim()) console.error(`${prefix} ${line}`);
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
if (cmdResult.stderr) {
|
|
2804
|
+
for (const line of cmdResult.stderr.split("\n")) {
|
|
2805
|
+
if (line.trim()) console.error(`${prefix} ${line}`);
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
if (!cmdResult.success) {
|
|
2809
|
+
console.error(`${prefix} command failed (exit ${cmdResult.exitCode}): ${cmdResult.command}`);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2660
2813
|
/**
|
|
2661
2814
|
* Initialize backend for execution request.
|
|
2662
2815
|
* Directly creates and configures a ShellBackend (no factory indirection).
|
|
@@ -2671,11 +2824,14 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2671
2824
|
backend.configure({
|
|
2672
2825
|
workingDirectory: request.workingDirectory,
|
|
2673
2826
|
servicesPath: `${process.env.HOME || process.env.USERPROFILE}/.juno_code/services`,
|
|
2674
|
-
debug: this.engineConfig.config.verbose,
|
|
2827
|
+
debug: this.engineConfig.config.verbose >= 2,
|
|
2675
2828
|
timeout: request.timeoutMs || this.engineConfig.config.mcpTimeout || 432e5,
|
|
2676
2829
|
enableJsonStreaming: true,
|
|
2677
|
-
outputRawJson: this.engineConfig.config.verbose,
|
|
2678
|
-
environment:
|
|
2830
|
+
outputRawJson: this.engineConfig.config.verbose >= 1,
|
|
2831
|
+
environment: {
|
|
2832
|
+
...process.env,
|
|
2833
|
+
JUNO_TASK_ROOT: process.env.JUNO_TASK_ROOT || request.workingDirectory
|
|
2834
|
+
},
|
|
2679
2835
|
sessionId: request.requestId
|
|
2680
2836
|
});
|
|
2681
2837
|
await backend.initialize();
|
|
@@ -2697,7 +2853,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2697
2853
|
this.emit("progress:error", { event, error });
|
|
2698
2854
|
}
|
|
2699
2855
|
});
|
|
2700
|
-
engineLogger.
|
|
2856
|
+
engineLogger.debug(`Initialized ${backend.name} backend for execution`);
|
|
2701
2857
|
}
|
|
2702
2858
|
/**
|
|
2703
2859
|
* Validate execution request parameters
|
|
@@ -2800,10 +2956,13 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2800
2956
|
async executeInternal(context) {
|
|
2801
2957
|
context.status = "running" /* RUNNING */;
|
|
2802
2958
|
context.sessionContext = { ...context.sessionContext, state: "active" };
|
|
2959
|
+
if (!process.env.JUNO_TASK_ROOT) {
|
|
2960
|
+
process.env.JUNO_TASK_ROOT = context.request.workingDirectory;
|
|
2961
|
+
}
|
|
2803
2962
|
await this.initializeBackend(context.request);
|
|
2804
2963
|
try {
|
|
2805
2964
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
2806
|
-
await executeHook(
|
|
2965
|
+
const hookResult = await executeHook(
|
|
2807
2966
|
"START_RUN",
|
|
2808
2967
|
this.engineConfig.config.hooks,
|
|
2809
2968
|
{
|
|
@@ -2823,6 +2982,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2823
2982
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
2824
2983
|
}
|
|
2825
2984
|
);
|
|
2985
|
+
this.displayHookOutput(hookResult);
|
|
2826
2986
|
}
|
|
2827
2987
|
} catch (error) {
|
|
2828
2988
|
engineLogger.warn("Hook START_RUN failed", { error });
|
|
@@ -2846,7 +3006,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2846
3006
|
}
|
|
2847
3007
|
try {
|
|
2848
3008
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
2849
|
-
await executeHook(
|
|
3009
|
+
const hookResult = await executeHook(
|
|
2850
3010
|
"END_RUN",
|
|
2851
3011
|
this.engineConfig.config.hooks,
|
|
2852
3012
|
{
|
|
@@ -2868,6 +3028,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2868
3028
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
2869
3029
|
}
|
|
2870
3030
|
);
|
|
3031
|
+
this.displayHookOutput(hookResult);
|
|
2871
3032
|
}
|
|
2872
3033
|
} catch (error) {
|
|
2873
3034
|
engineLogger.warn("Hook END_RUN failed", { error });
|
|
@@ -2912,7 +3073,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2912
3073
|
const iterationStart = /* @__PURE__ */ new Date();
|
|
2913
3074
|
try {
|
|
2914
3075
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
2915
|
-
await executeHook(
|
|
3076
|
+
const hookResult = await executeHook(
|
|
2916
3077
|
"START_ITERATION",
|
|
2917
3078
|
this.engineConfig.config.hooks,
|
|
2918
3079
|
{
|
|
@@ -2933,6 +3094,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2933
3094
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
2934
3095
|
}
|
|
2935
3096
|
);
|
|
3097
|
+
this.displayHookOutput(hookResult);
|
|
2936
3098
|
}
|
|
2937
3099
|
} catch (error) {
|
|
2938
3100
|
engineLogger.warn("Hook START_ITERATION failed", { error, iterationNumber });
|
|
@@ -2959,6 +3121,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2959
3121
|
...context.request.continueConversation !== void 0 && {
|
|
2960
3122
|
continueConversation: context.request.continueConversation
|
|
2961
3123
|
},
|
|
3124
|
+
...context.request.thinking !== void 0 && { thinking: context.request.thinking },
|
|
2962
3125
|
iteration: iterationNumber
|
|
2963
3126
|
},
|
|
2964
3127
|
timeout: context.request.timeoutMs || this.engineConfig.config.mcpTimeout,
|
|
@@ -2995,7 +3158,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2995
3158
|
this.emit("iteration:complete", { context, iterationResult });
|
|
2996
3159
|
try {
|
|
2997
3160
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
2998
|
-
await executeHook(
|
|
3161
|
+
const hookResult = await executeHook(
|
|
2999
3162
|
"END_ITERATION",
|
|
3000
3163
|
this.engineConfig.config.hooks,
|
|
3001
3164
|
{
|
|
@@ -3017,6 +3180,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
3017
3180
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
3018
3181
|
}
|
|
3019
3182
|
);
|
|
3183
|
+
this.displayHookOutput(hookResult);
|
|
3020
3184
|
}
|
|
3021
3185
|
} catch (error) {
|
|
3022
3186
|
engineLogger.warn("Hook END_ITERATION failed", { error, iterationNumber });
|
|
@@ -3051,7 +3215,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
3051
3215
|
this.emit("iteration:error", { context, iterationResult });
|
|
3052
3216
|
try {
|
|
3053
3217
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
3054
|
-
await executeHook(
|
|
3218
|
+
const hookResult = await executeHook(
|
|
3055
3219
|
"END_ITERATION",
|
|
3056
3220
|
this.engineConfig.config.hooks,
|
|
3057
3221
|
{
|
|
@@ -3074,6 +3238,7 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
3074
3238
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
3075
3239
|
}
|
|
3076
3240
|
);
|
|
3241
|
+
this.displayHookOutput(hookResult);
|
|
3077
3242
|
}
|
|
3078
3243
|
} catch (hookError) {
|
|
3079
3244
|
engineLogger.warn("Hook END_ITERATION failed", { error: hookError, iterationNumber });
|
|
@@ -3549,6 +3714,9 @@ function createExecutionRequest(options) {
|
|
|
3549
3714
|
if (options.continueConversation !== void 0) {
|
|
3550
3715
|
result.continueConversation = options.continueConversation;
|
|
3551
3716
|
}
|
|
3717
|
+
if (options.thinking !== void 0) {
|
|
3718
|
+
result.thinking = options.thinking;
|
|
3719
|
+
}
|
|
3552
3720
|
return result;
|
|
3553
3721
|
}
|
|
3554
3722
|
|
|
@@ -4228,9 +4396,16 @@ function validateEnvironmentVars(envVars) {
|
|
|
4228
4396
|
case "JUNO_TASK_DEFAULT_MAX_ITERATIONS":
|
|
4229
4397
|
config.defaultMaxIterations = validateIterations(parseInt(value, 10));
|
|
4230
4398
|
break;
|
|
4231
|
-
case "JUNO_TASK_VERBOSE":
|
|
4232
|
-
|
|
4399
|
+
case "JUNO_TASK_VERBOSE": {
|
|
4400
|
+
const lower = value.toLowerCase();
|
|
4401
|
+
if (lower === "true" || lower === "yes") config.verbose = 1;
|
|
4402
|
+
else if (lower === "false" || lower === "no") config.verbose = 0;
|
|
4403
|
+
else {
|
|
4404
|
+
const n = Number(value);
|
|
4405
|
+
config.verbose = !isNaN(n) && n >= 0 && n <= 2 ? Math.floor(n) : 1;
|
|
4406
|
+
}
|
|
4233
4407
|
break;
|
|
4408
|
+
}
|
|
4234
4409
|
case "JUNO_TASK_QUIET":
|
|
4235
4410
|
config.quiet = value.toLowerCase() === "true";
|
|
4236
4411
|
break;
|