juno-code 1.0.49 → 1.0.51
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 +508 -202
- package/dist/bin/cli.d.mts +1 -1
- package/dist/bin/cli.d.ts +1 -1
- package/dist/bin/cli.js +3332 -1421
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +3316 -1405
- 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 +56 -19
- package/dist/index.d.ts +56 -19
- package/dist/index.js +240 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +240 -36
- package/dist/index.mjs.map +1 -1
- package/dist/templates/scripts/install_requirements.sh +55 -5
- package/dist/templates/scripts/kanban.sh +11 -0
- package/dist/templates/services/README.md +23 -4
- package/dist/templates/services/__pycache__/pi.cpython-313.pyc +0 -0
- package/dist/templates/services/pi.py +1933 -262
- 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 +11 -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 +11 -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.mjs
CHANGED
|
@@ -34,7 +34,7 @@ var __export = (target, all) => {
|
|
|
34
34
|
var version;
|
|
35
35
|
var init_version = __esm({
|
|
36
36
|
"src/version.ts"() {
|
|
37
|
-
version = "1.0.
|
|
37
|
+
version = "1.0.51";
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
@@ -985,6 +985,9 @@ var init_shell_backend = __esm({
|
|
|
985
985
|
args.push("--disallowedTools");
|
|
986
986
|
args.push(...request.arguments.disallowedTools);
|
|
987
987
|
}
|
|
988
|
+
if (isPython && request.arguments?.thinking) {
|
|
989
|
+
args.push("--thinking", String(request.arguments.thinking));
|
|
990
|
+
}
|
|
988
991
|
if (isPython && request.arguments?.resume) {
|
|
989
992
|
args.push("--resume", String(request.arguments.resume));
|
|
990
993
|
}
|
|
@@ -994,6 +997,9 @@ var init_shell_backend = __esm({
|
|
|
994
997
|
if (isPython && subagentType === "pi" && request.arguments?.project_path) {
|
|
995
998
|
args.push("--cd", String(request.arguments.project_path));
|
|
996
999
|
}
|
|
1000
|
+
if (isPython && subagentType === "pi" && request.arguments?.live === true) {
|
|
1001
|
+
args.push("--live");
|
|
1002
|
+
}
|
|
997
1003
|
if (isPython && this.config.debug) {
|
|
998
1004
|
args.push("--verbose");
|
|
999
1005
|
}
|
|
@@ -1011,12 +1017,19 @@ var init_shell_backend = __esm({
|
|
|
1011
1017
|
`Environment variables: ${Object.keys(env2).filter((k) => k.startsWith("JUNO_") || k.startsWith("PI_")).join(", ")}`
|
|
1012
1018
|
);
|
|
1013
1019
|
}
|
|
1020
|
+
const isPiLiveMode = isPython && subagentType === "pi" && request.arguments?.live === true;
|
|
1021
|
+
const shouldAttachLiveTerminal = isPiLiveMode && process.stdout.isTTY === true;
|
|
1022
|
+
if (this.config.debug && isPiLiveMode) {
|
|
1023
|
+
engineLogger.debug(
|
|
1024
|
+
`Pi live mode stdio: ${shouldAttachLiveTerminal ? "inherit (interactive TTY or stdout-tty fallback)" : "pipe (headless/non-TTY)"}`
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1014
1027
|
const child = spawn(command, args, {
|
|
1015
1028
|
env: env2,
|
|
1016
1029
|
cwd: this.config.workingDirectory,
|
|
1017
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1030
|
+
stdio: shouldAttachLiveTerminal ? "inherit" : ["pipe", "pipe", "pipe"]
|
|
1018
1031
|
});
|
|
1019
|
-
if (child.stdin) {
|
|
1032
|
+
if (!shouldAttachLiveTerminal && child.stdin) {
|
|
1020
1033
|
child.stdin.end();
|
|
1021
1034
|
}
|
|
1022
1035
|
let stdout2 = "";
|
|
@@ -1088,7 +1101,8 @@ var init_shell_backend = __esm({
|
|
|
1088
1101
|
subAgentResponse = JSON.parse(captured);
|
|
1089
1102
|
}
|
|
1090
1103
|
} catch (error) {
|
|
1091
|
-
|
|
1104
|
+
const isNotFound = error?.code === "ENOENT";
|
|
1105
|
+
if (!isNotFound && this.config?.debug) {
|
|
1092
1106
|
engineLogger.warn(
|
|
1093
1107
|
`Failed to read subagent capture: ${error instanceof Error ? error.message : String(error)}`
|
|
1094
1108
|
);
|
|
@@ -1299,6 +1313,8 @@ var init_shell_backend = __esm({
|
|
|
1299
1313
|
delete inner.type;
|
|
1300
1314
|
sanitizedPiEvent.sub_agent_response = inner;
|
|
1301
1315
|
}
|
|
1316
|
+
const usage = piEvent.usage;
|
|
1317
|
+
const totalCostUsd = typeof piEvent.total_cost_usd === "number" ? piEvent.total_cost_usd : typeof usage?.cost?.total === "number" ? usage.cost.total : void 0;
|
|
1302
1318
|
const structuredPayload = {
|
|
1303
1319
|
type: "result",
|
|
1304
1320
|
subtype: piEvent.subtype || (isError ? "error" : "success"),
|
|
@@ -1309,7 +1325,8 @@ var init_shell_backend = __esm({
|
|
|
1309
1325
|
session_id: piEvent.session_id,
|
|
1310
1326
|
exit_code: result.exitCode,
|
|
1311
1327
|
duration_ms: piEvent.duration_ms ?? result.duration,
|
|
1312
|
-
|
|
1328
|
+
total_cost_usd: totalCostUsd,
|
|
1329
|
+
usage,
|
|
1313
1330
|
sub_agent_response: sanitizedPiEvent
|
|
1314
1331
|
};
|
|
1315
1332
|
const metadata = {
|
|
@@ -1700,6 +1717,16 @@ function getDefaultHooksJson(indent = 2) {
|
|
|
1700
1717
|
return JSON.stringify(DEFAULT_HOOKS, null, indent);
|
|
1701
1718
|
}
|
|
1702
1719
|
|
|
1720
|
+
// src/core/subagent-models.ts
|
|
1721
|
+
init_version();
|
|
1722
|
+
var SUBAGENT_DEFAULT_MODELS = {
|
|
1723
|
+
claude: ":sonnet",
|
|
1724
|
+
codex: ":codex",
|
|
1725
|
+
gemini: ":pro",
|
|
1726
|
+
cursor: "auto",
|
|
1727
|
+
pi: ":pi"
|
|
1728
|
+
};
|
|
1729
|
+
|
|
1703
1730
|
// src/core/config.ts
|
|
1704
1731
|
var ENV_VAR_MAPPING = {
|
|
1705
1732
|
// Core settings
|
|
@@ -1749,18 +1776,31 @@ var JunoTaskConfigSchema = z.object({
|
|
|
1749
1776
|
defaultBackend: BackendTypeSchema.describe("Default backend to use for task execution"),
|
|
1750
1777
|
defaultMaxIterations: z.number().int().min(1).max(1e3).describe("Default maximum number of iterations for task execution"),
|
|
1751
1778
|
defaultModel: z.string().optional().describe("Default model to use for the subagent"),
|
|
1779
|
+
defaultModels: z.record(SubagentTypeSchema, z.string()).optional().describe("Optional per-subagent default model overrides"),
|
|
1752
1780
|
// Project metadata
|
|
1753
1781
|
mainTask: z.string().optional().describe("Main task objective for the project"),
|
|
1754
1782
|
// Logging settings
|
|
1755
1783
|
logLevel: LogLevelSchema.describe("Logging level for the application"),
|
|
1756
1784
|
logFile: z.string().optional().describe("Path to log file (optional)"),
|
|
1757
|
-
verbose: z.
|
|
1785
|
+
verbose: z.preprocess(
|
|
1786
|
+
(val) => {
|
|
1787
|
+
if (val === true) return 1;
|
|
1788
|
+
if (val === false) return 0;
|
|
1789
|
+
if (typeof val === "string") {
|
|
1790
|
+
const lower = val.toLowerCase().trim();
|
|
1791
|
+
if (lower === "true" || lower === "yes") return 1;
|
|
1792
|
+
if (lower === "false" || lower === "no") return 0;
|
|
1793
|
+
}
|
|
1794
|
+
return val;
|
|
1795
|
+
},
|
|
1796
|
+
z.number().int().min(0).max(2)
|
|
1797
|
+
).describe("Verbosity level: 0=quiet, 1=normal+helping texts (default), 2=debug+hooks"),
|
|
1758
1798
|
quiet: z.boolean().describe("Enable quiet mode (minimal output)"),
|
|
1759
1799
|
// MCP settings
|
|
1760
1800
|
mcpTimeout: z.number().int().min(1e3).max(864e5).describe("MCP server timeout in milliseconds"),
|
|
1761
1801
|
mcpRetries: z.number().int().min(0).max(10).describe("Number of retries for MCP operations"),
|
|
1762
1802
|
mcpServerPath: z.string().optional().describe("Path to MCP server executable (auto-discovered if not specified)"),
|
|
1763
|
-
mcpServerName: z.string().optional().describe(
|
|
1803
|
+
mcpServerName: z.string().optional().describe("Named MCP server to connect to"),
|
|
1764
1804
|
// Hook settings
|
|
1765
1805
|
hookCommandTimeout: z.number().int().min(1e3).max(36e5).optional().describe(
|
|
1766
1806
|
"Timeout for individual hook commands in milliseconds (default: 300000 = 5 minutes)"
|
|
@@ -1775,6 +1815,11 @@ var JunoTaskConfigSchema = z.object({
|
|
|
1775
1815
|
// Paths
|
|
1776
1816
|
workingDirectory: z.string().describe("Working directory for task execution"),
|
|
1777
1817
|
sessionDirectory: z.string().describe("Directory for storing session data"),
|
|
1818
|
+
// Project environment bootstrap
|
|
1819
|
+
envFilePath: z.string().optional().describe(
|
|
1820
|
+
"Path to the project env file loaded before execution (relative to project root or absolute)"
|
|
1821
|
+
),
|
|
1822
|
+
envFileCopied: z.boolean().optional().describe("Tracks whether configured env file has been initialized from .env.juno"),
|
|
1778
1823
|
// Hooks configuration
|
|
1779
1824
|
hooks: HooksSchema.describe(
|
|
1780
1825
|
"Hook system configuration for executing commands at specific lifecycle events"
|
|
@@ -1787,16 +1832,15 @@ var DEFAULT_CONFIG = {
|
|
|
1787
1832
|
defaultSubagent: "claude",
|
|
1788
1833
|
defaultBackend: "shell",
|
|
1789
1834
|
defaultMaxIterations: 1,
|
|
1835
|
+
defaultModels: { ...SUBAGENT_DEFAULT_MODELS },
|
|
1790
1836
|
// Logging settings
|
|
1791
1837
|
logLevel: "info",
|
|
1792
|
-
verbose:
|
|
1838
|
+
verbose: 1,
|
|
1793
1839
|
quiet: false,
|
|
1794
1840
|
// MCP settings (also used by shell backend)
|
|
1795
1841
|
mcpTimeout: 432e5,
|
|
1796
1842
|
// 43200 seconds (12 hours) - default for long-running shell backend operations
|
|
1797
1843
|
mcpRetries: 3,
|
|
1798
|
-
mcpServerName: "roundtable-ai",
|
|
1799
|
-
// Default to roundtable-ai server
|
|
1800
1844
|
// Quota/hourly limit settings
|
|
1801
1845
|
onHourlyLimit: "raise",
|
|
1802
1846
|
// Default to exit immediately when hourly limit is reached
|
|
@@ -1806,6 +1850,9 @@ var DEFAULT_CONFIG = {
|
|
|
1806
1850
|
// Paths
|
|
1807
1851
|
workingDirectory: process.cwd(),
|
|
1808
1852
|
sessionDirectory: path4.join(process.cwd(), ".juno_task"),
|
|
1853
|
+
// Project environment bootstrap
|
|
1854
|
+
envFilePath: ".env.juno",
|
|
1855
|
+
envFileCopied: false,
|
|
1809
1856
|
// Hooks configuration - populated with default hooks template
|
|
1810
1857
|
hooks: getDefaultHooks()
|
|
1811
1858
|
};
|
|
@@ -1818,6 +1865,7 @@ var GLOBAL_CONFIG_FILE_NAMES = [
|
|
|
1818
1865
|
// Will look for 'junoCode' field
|
|
1819
1866
|
];
|
|
1820
1867
|
var PROJECT_CONFIG_FILE = ".juno_task/config.json";
|
|
1868
|
+
var DEFAULT_PROJECT_ENV_FILE = ".env.juno";
|
|
1821
1869
|
function resolvePath(inputPath, basePath = process.cwd()) {
|
|
1822
1870
|
if (path4.isAbsolute(inputPath)) {
|
|
1823
1871
|
return inputPath;
|
|
@@ -1839,7 +1887,12 @@ function loadConfigFromEnv() {
|
|
|
1839
1887
|
for (const [envVar, configKey] of Object.entries(ENV_VAR_MAPPING)) {
|
|
1840
1888
|
const value = process.env[envVar];
|
|
1841
1889
|
if (value !== void 0) {
|
|
1842
|
-
|
|
1890
|
+
let parsed = parseEnvValue(value);
|
|
1891
|
+
if (configKey === "verbose") {
|
|
1892
|
+
if (parsed === true) parsed = 1;
|
|
1893
|
+
else if (parsed === false) parsed = 0;
|
|
1894
|
+
}
|
|
1895
|
+
config[configKey] = parsed;
|
|
1843
1896
|
}
|
|
1844
1897
|
}
|
|
1845
1898
|
return config;
|
|
@@ -2073,6 +2126,95 @@ function validateConfig(config) {
|
|
|
2073
2126
|
throw error;
|
|
2074
2127
|
}
|
|
2075
2128
|
}
|
|
2129
|
+
function parseEnvFileContent(content) {
|
|
2130
|
+
const envVars = {};
|
|
2131
|
+
const lines = content.split(/\r?\n/);
|
|
2132
|
+
for (const rawLine of lines) {
|
|
2133
|
+
const trimmedLine = rawLine.trim();
|
|
2134
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
2135
|
+
continue;
|
|
2136
|
+
}
|
|
2137
|
+
const line = trimmedLine.startsWith("export ") ? trimmedLine.slice(7).trim() : trimmedLine;
|
|
2138
|
+
const separatorIndex = line.indexOf("=");
|
|
2139
|
+
if (separatorIndex === -1) {
|
|
2140
|
+
continue;
|
|
2141
|
+
}
|
|
2142
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
2143
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
2144
|
+
continue;
|
|
2145
|
+
}
|
|
2146
|
+
let value = line.slice(separatorIndex + 1).trim();
|
|
2147
|
+
if ((value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) && value.length >= 2) {
|
|
2148
|
+
const quote = value[0];
|
|
2149
|
+
value = value.slice(1, -1);
|
|
2150
|
+
if (quote === '"') {
|
|
2151
|
+
value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
2152
|
+
}
|
|
2153
|
+
} else {
|
|
2154
|
+
const inlineCommentIndex = value.indexOf(" #");
|
|
2155
|
+
if (inlineCommentIndex >= 0) {
|
|
2156
|
+
value = value.slice(0, inlineCommentIndex).trimEnd();
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
envVars[key] = value;
|
|
2160
|
+
}
|
|
2161
|
+
return envVars;
|
|
2162
|
+
}
|
|
2163
|
+
async function loadEnvFileIntoProcess(envFilePath) {
|
|
2164
|
+
try {
|
|
2165
|
+
const content = await promises.readFile(envFilePath, "utf-8");
|
|
2166
|
+
const parsed = parseEnvFileContent(content);
|
|
2167
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
2168
|
+
process.env[key] = value;
|
|
2169
|
+
}
|
|
2170
|
+
} catch (error) {
|
|
2171
|
+
console.warn(`Warning: Failed to load env file ${envFilePath}: ${error}`);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
async function ensureAndLoadProjectEnv(baseDir) {
|
|
2175
|
+
const configPath = path4.join(baseDir, PROJECT_CONFIG_FILE);
|
|
2176
|
+
const defaultEnvPath = resolvePath(DEFAULT_PROJECT_ENV_FILE, baseDir);
|
|
2177
|
+
await fs.ensureFile(defaultEnvPath);
|
|
2178
|
+
let existingConfig = null;
|
|
2179
|
+
if (await fs.pathExists(configPath)) {
|
|
2180
|
+
try {
|
|
2181
|
+
existingConfig = await fs.readJson(configPath);
|
|
2182
|
+
} catch (error) {
|
|
2183
|
+
console.warn(`Warning: Failed to read ${configPath} for env bootstrap: ${error}`);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
const configuredEnvPathRaw = existingConfig && typeof existingConfig.envFilePath === "string" && existingConfig.envFilePath ? existingConfig.envFilePath : DEFAULT_PROJECT_ENV_FILE;
|
|
2187
|
+
const configuredEnvPath = resolvePath(configuredEnvPathRaw, baseDir);
|
|
2188
|
+
let envFileCopied = existingConfig && typeof existingConfig.envFileCopied === "boolean" ? existingConfig.envFileCopied : false;
|
|
2189
|
+
let needsConfigUpdate = false;
|
|
2190
|
+
if (configuredEnvPath !== defaultEnvPath) {
|
|
2191
|
+
const configuredExists = await fs.pathExists(configuredEnvPath);
|
|
2192
|
+
if (!configuredExists) {
|
|
2193
|
+
await fs.ensureDir(path4.dirname(configuredEnvPath));
|
|
2194
|
+
if (!envFileCopied) {
|
|
2195
|
+
await promises.copyFile(defaultEnvPath, configuredEnvPath);
|
|
2196
|
+
} else {
|
|
2197
|
+
await fs.ensureFile(configuredEnvPath);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
if (!envFileCopied) {
|
|
2201
|
+
envFileCopied = true;
|
|
2202
|
+
needsConfigUpdate = true;
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
if (existingConfig && (needsConfigUpdate || typeof existingConfig.envFilePath !== "string" || typeof existingConfig.envFileCopied !== "boolean")) {
|
|
2206
|
+
const updatedConfig = {
|
|
2207
|
+
...existingConfig,
|
|
2208
|
+
envFilePath: configuredEnvPathRaw,
|
|
2209
|
+
envFileCopied
|
|
2210
|
+
};
|
|
2211
|
+
await fs.writeJson(configPath, updatedConfig, { spaces: 2 });
|
|
2212
|
+
}
|
|
2213
|
+
await loadEnvFileIntoProcess(defaultEnvPath);
|
|
2214
|
+
if (configuredEnvPath !== defaultEnvPath) {
|
|
2215
|
+
await loadEnvFileIntoProcess(configuredEnvPath);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2076
2218
|
async function ensureHooksConfig(baseDir) {
|
|
2077
2219
|
try {
|
|
2078
2220
|
const configDir = path4.join(baseDir, ".juno_task");
|
|
@@ -2095,19 +2237,30 @@ async function ensureHooksConfig(baseDir) {
|
|
|
2095
2237
|
}
|
|
2096
2238
|
if (!existingConfig.defaultModel) {
|
|
2097
2239
|
const subagent = existingConfig.defaultSubagent || "claude";
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
existingConfig.defaultModel
|
|
2240
|
+
existingConfig.defaultModel = SUBAGENT_DEFAULT_MODELS[subagent] || SUBAGENT_DEFAULT_MODELS.claude;
|
|
2241
|
+
needsUpdate = true;
|
|
2242
|
+
}
|
|
2243
|
+
if (!existingConfig.defaultModels || typeof existingConfig.defaultModels !== "object" || Array.isArray(existingConfig.defaultModels)) {
|
|
2244
|
+
const baseDefaults = { ...SUBAGENT_DEFAULT_MODELS };
|
|
2245
|
+
const subagent = existingConfig.defaultSubagent || "claude";
|
|
2246
|
+
if (typeof existingConfig.defaultModel === "string") {
|
|
2247
|
+
baseDefaults[subagent] = existingConfig.defaultModel;
|
|
2248
|
+
}
|
|
2249
|
+
existingConfig.defaultModels = baseDefaults;
|
|
2105
2250
|
needsUpdate = true;
|
|
2106
2251
|
}
|
|
2107
2252
|
if (existingConfig.defaultMaxIterations === 50) {
|
|
2108
2253
|
existingConfig.defaultMaxIterations = DEFAULT_CONFIG.defaultMaxIterations;
|
|
2109
2254
|
needsUpdate = true;
|
|
2110
2255
|
}
|
|
2256
|
+
if (!existingConfig.envFilePath) {
|
|
2257
|
+
existingConfig.envFilePath = DEFAULT_PROJECT_ENV_FILE;
|
|
2258
|
+
needsUpdate = true;
|
|
2259
|
+
}
|
|
2260
|
+
if (typeof existingConfig.envFileCopied !== "boolean") {
|
|
2261
|
+
existingConfig.envFileCopied = false;
|
|
2262
|
+
needsUpdate = true;
|
|
2263
|
+
}
|
|
2111
2264
|
if (needsUpdate) {
|
|
2112
2265
|
await fs.writeJson(configPath, existingConfig, { spaces: 2 });
|
|
2113
2266
|
}
|
|
@@ -2119,6 +2272,7 @@ async function ensureHooksConfig(baseDir) {
|
|
|
2119
2272
|
async function loadConfig(options = {}) {
|
|
2120
2273
|
const { baseDir = process.cwd(), configFile, cliConfig } = options;
|
|
2121
2274
|
await ensureHooksConfig(baseDir);
|
|
2275
|
+
await ensureAndLoadProjectEnv(baseDir);
|
|
2122
2276
|
const loader = new ConfigLoader(baseDir);
|
|
2123
2277
|
loader.fromEnvironment();
|
|
2124
2278
|
if (configFile) {
|
|
@@ -2151,7 +2305,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2151
2305
|
logContext = "SYSTEM" /* SYSTEM */
|
|
2152
2306
|
} = options;
|
|
2153
2307
|
const contextLogger = logger.child(logContext);
|
|
2154
|
-
contextLogger.
|
|
2308
|
+
contextLogger.debug(`Starting hook execution: ${hookType}`, {
|
|
2155
2309
|
context,
|
|
2156
2310
|
workingDirectory: context.workingDirectory || process.cwd(),
|
|
2157
2311
|
commandTimeout,
|
|
@@ -2180,13 +2334,13 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2180
2334
|
commandsFailed: 0
|
|
2181
2335
|
};
|
|
2182
2336
|
}
|
|
2183
|
-
contextLogger.
|
|
2337
|
+
contextLogger.debug(`Executing ${hook.commands.length} commands for hook ${hookType}`);
|
|
2184
2338
|
const commandResults = [];
|
|
2185
2339
|
let commandsFailed = 0;
|
|
2186
2340
|
for (let i = 0; i < hook.commands.length; i++) {
|
|
2187
2341
|
const command = hook.commands[i];
|
|
2188
2342
|
const commandStartTime = Date.now();
|
|
2189
|
-
contextLogger.
|
|
2343
|
+
contextLogger.debug(`Executing command ${i + 1}/${hook.commands.length}: ${command}`, {
|
|
2190
2344
|
commandIndex: i,
|
|
2191
2345
|
totalCommands: hook.commands.length
|
|
2192
2346
|
});
|
|
@@ -2234,7 +2388,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2234
2388
|
};
|
|
2235
2389
|
commandResults.push(commandResult);
|
|
2236
2390
|
if (success2) {
|
|
2237
|
-
contextLogger.
|
|
2391
|
+
contextLogger.debug(`Command completed successfully`, {
|
|
2238
2392
|
command,
|
|
2239
2393
|
exitCode: result2.exitCode,
|
|
2240
2394
|
duration,
|
|
@@ -2315,7 +2469,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2315
2469
|
commandsExecuted,
|
|
2316
2470
|
commandsFailed
|
|
2317
2471
|
};
|
|
2318
|
-
contextLogger.
|
|
2472
|
+
contextLogger.debug(`Hook execution completed`, {
|
|
2319
2473
|
hookType,
|
|
2320
2474
|
totalDuration,
|
|
2321
2475
|
commandsExecuted,
|
|
@@ -2326,7 +2480,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
|
|
|
2326
2480
|
}
|
|
2327
2481
|
async function executeHooks(hookTypes, hooks, context = {}, options = {}) {
|
|
2328
2482
|
const results = [];
|
|
2329
|
-
hookLogger.
|
|
2483
|
+
hookLogger.debug(`Starting batch hook execution`, {
|
|
2330
2484
|
hookTypes,
|
|
2331
2485
|
context
|
|
2332
2486
|
});
|
|
@@ -2337,7 +2491,7 @@ async function executeHooks(hookTypes, hooks, context = {}, options = {}) {
|
|
|
2337
2491
|
const totalSuccess = results.every((r) => r.success);
|
|
2338
2492
|
const totalCommands = results.reduce((sum, r) => sum + r.commandsExecuted, 0);
|
|
2339
2493
|
const totalFailed = results.reduce((sum, r) => sum + r.commandsFailed, 0);
|
|
2340
|
-
hookLogger.
|
|
2494
|
+
hookLogger.debug(`Batch hook execution completed`, {
|
|
2341
2495
|
hookTypes,
|
|
2342
2496
|
totalHooks: results.length,
|
|
2343
2497
|
totalCommands,
|
|
@@ -2629,6 +2783,30 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2629
2783
|
*/
|
|
2630
2784
|
setupProgressTracking() {
|
|
2631
2785
|
}
|
|
2786
|
+
/**
|
|
2787
|
+
* Display hook execution output to stderr at verbose level 2 (debug+hooks).
|
|
2788
|
+
* At level 2: shows hook name, command stdout/stderr output.
|
|
2789
|
+
* At level 0-1: output is suppressed (hooks still execute).
|
|
2790
|
+
*/
|
|
2791
|
+
displayHookOutput(hookResult) {
|
|
2792
|
+
if (this.engineConfig.config.verbose < 2) return;
|
|
2793
|
+
for (const cmdResult of hookResult.commandResults) {
|
|
2794
|
+
const prefix = `[hook:${hookResult.hookType}]`;
|
|
2795
|
+
if (cmdResult.stdout) {
|
|
2796
|
+
for (const line of cmdResult.stdout.split("\n")) {
|
|
2797
|
+
if (line.trim()) console.error(`${prefix} ${line}`);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
if (cmdResult.stderr) {
|
|
2801
|
+
for (const line of cmdResult.stderr.split("\n")) {
|
|
2802
|
+
if (line.trim()) console.error(`${prefix} ${line}`);
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
if (!cmdResult.success) {
|
|
2806
|
+
console.error(`${prefix} command failed (exit ${cmdResult.exitCode}): ${cmdResult.command}`);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2632
2810
|
/**
|
|
2633
2811
|
* Initialize backend for execution request.
|
|
2634
2812
|
* Directly creates and configures a ShellBackend (no factory indirection).
|
|
@@ -2643,11 +2821,14 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2643
2821
|
backend.configure({
|
|
2644
2822
|
workingDirectory: request.workingDirectory,
|
|
2645
2823
|
servicesPath: `${process.env.HOME || process.env.USERPROFILE}/.juno_code/services`,
|
|
2646
|
-
debug: this.engineConfig.config.verbose,
|
|
2824
|
+
debug: this.engineConfig.config.verbose >= 2,
|
|
2647
2825
|
timeout: request.timeoutMs || this.engineConfig.config.mcpTimeout || 432e5,
|
|
2648
2826
|
enableJsonStreaming: true,
|
|
2649
|
-
outputRawJson: this.engineConfig.config.verbose,
|
|
2650
|
-
environment:
|
|
2827
|
+
outputRawJson: this.engineConfig.config.verbose >= 1,
|
|
2828
|
+
environment: {
|
|
2829
|
+
...process.env,
|
|
2830
|
+
JUNO_TASK_ROOT: process.env.JUNO_TASK_ROOT || request.workingDirectory
|
|
2831
|
+
},
|
|
2651
2832
|
sessionId: request.requestId
|
|
2652
2833
|
});
|
|
2653
2834
|
await backend.initialize();
|
|
@@ -2669,7 +2850,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2669
2850
|
this.emit("progress:error", { event, error });
|
|
2670
2851
|
}
|
|
2671
2852
|
});
|
|
2672
|
-
engineLogger.
|
|
2853
|
+
engineLogger.debug(`Initialized ${backend.name} backend for execution`);
|
|
2673
2854
|
}
|
|
2674
2855
|
/**
|
|
2675
2856
|
* Validate execution request parameters
|
|
@@ -2772,10 +2953,13 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2772
2953
|
async executeInternal(context) {
|
|
2773
2954
|
context.status = "running" /* RUNNING */;
|
|
2774
2955
|
context.sessionContext = { ...context.sessionContext, state: "active" };
|
|
2956
|
+
if (!process.env.JUNO_TASK_ROOT) {
|
|
2957
|
+
process.env.JUNO_TASK_ROOT = context.request.workingDirectory;
|
|
2958
|
+
}
|
|
2775
2959
|
await this.initializeBackend(context.request);
|
|
2776
2960
|
try {
|
|
2777
2961
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
2778
|
-
await executeHook(
|
|
2962
|
+
const hookResult = await executeHook(
|
|
2779
2963
|
"START_RUN",
|
|
2780
2964
|
this.engineConfig.config.hooks,
|
|
2781
2965
|
{
|
|
@@ -2795,6 +2979,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2795
2979
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
2796
2980
|
}
|
|
2797
2981
|
);
|
|
2982
|
+
this.displayHookOutput(hookResult);
|
|
2798
2983
|
}
|
|
2799
2984
|
} catch (error) {
|
|
2800
2985
|
engineLogger.warn("Hook START_RUN failed", { error });
|
|
@@ -2818,7 +3003,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2818
3003
|
}
|
|
2819
3004
|
try {
|
|
2820
3005
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
2821
|
-
await executeHook(
|
|
3006
|
+
const hookResult = await executeHook(
|
|
2822
3007
|
"END_RUN",
|
|
2823
3008
|
this.engineConfig.config.hooks,
|
|
2824
3009
|
{
|
|
@@ -2840,6 +3025,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2840
3025
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
2841
3026
|
}
|
|
2842
3027
|
);
|
|
3028
|
+
this.displayHookOutput(hookResult);
|
|
2843
3029
|
}
|
|
2844
3030
|
} catch (error) {
|
|
2845
3031
|
engineLogger.warn("Hook END_RUN failed", { error });
|
|
@@ -2884,7 +3070,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2884
3070
|
const iterationStart = /* @__PURE__ */ new Date();
|
|
2885
3071
|
try {
|
|
2886
3072
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
2887
|
-
await executeHook(
|
|
3073
|
+
const hookResult = await executeHook(
|
|
2888
3074
|
"START_ITERATION",
|
|
2889
3075
|
this.engineConfig.config.hooks,
|
|
2890
3076
|
{
|
|
@@ -2905,6 +3091,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2905
3091
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
2906
3092
|
}
|
|
2907
3093
|
);
|
|
3094
|
+
this.displayHookOutput(hookResult);
|
|
2908
3095
|
}
|
|
2909
3096
|
} catch (error) {
|
|
2910
3097
|
engineLogger.warn("Hook START_ITERATION failed", { error, iterationNumber });
|
|
@@ -2931,6 +3118,8 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2931
3118
|
...context.request.continueConversation !== void 0 && {
|
|
2932
3119
|
continueConversation: context.request.continueConversation
|
|
2933
3120
|
},
|
|
3121
|
+
...context.request.thinking !== void 0 && { thinking: context.request.thinking },
|
|
3122
|
+
...context.request.live !== void 0 && { live: context.request.live },
|
|
2934
3123
|
iteration: iterationNumber
|
|
2935
3124
|
},
|
|
2936
3125
|
timeout: context.request.timeoutMs || this.engineConfig.config.mcpTimeout,
|
|
@@ -2967,7 +3156,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2967
3156
|
this.emit("iteration:complete", { context, iterationResult });
|
|
2968
3157
|
try {
|
|
2969
3158
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
2970
|
-
await executeHook(
|
|
3159
|
+
const hookResult = await executeHook(
|
|
2971
3160
|
"END_ITERATION",
|
|
2972
3161
|
this.engineConfig.config.hooks,
|
|
2973
3162
|
{
|
|
@@ -2989,6 +3178,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
2989
3178
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
2990
3179
|
}
|
|
2991
3180
|
);
|
|
3181
|
+
this.displayHookOutput(hookResult);
|
|
2992
3182
|
}
|
|
2993
3183
|
} catch (error) {
|
|
2994
3184
|
engineLogger.warn("Hook END_ITERATION failed", { error, iterationNumber });
|
|
@@ -3023,7 +3213,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
3023
3213
|
this.emit("iteration:error", { context, iterationResult });
|
|
3024
3214
|
try {
|
|
3025
3215
|
if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
|
|
3026
|
-
await executeHook(
|
|
3216
|
+
const hookResult = await executeHook(
|
|
3027
3217
|
"END_ITERATION",
|
|
3028
3218
|
this.engineConfig.config.hooks,
|
|
3029
3219
|
{
|
|
@@ -3046,6 +3236,7 @@ var ExecutionEngine = class extends EventEmitter {
|
|
|
3046
3236
|
commandTimeout: this.engineConfig.config.hookCommandTimeout
|
|
3047
3237
|
}
|
|
3048
3238
|
);
|
|
3239
|
+
this.displayHookOutput(hookResult);
|
|
3049
3240
|
}
|
|
3050
3241
|
} catch (hookError) {
|
|
3051
3242
|
engineLogger.warn("Hook END_ITERATION failed", { error: hookError, iterationNumber });
|
|
@@ -3521,6 +3712,12 @@ function createExecutionRequest(options) {
|
|
|
3521
3712
|
if (options.continueConversation !== void 0) {
|
|
3522
3713
|
result.continueConversation = options.continueConversation;
|
|
3523
3714
|
}
|
|
3715
|
+
if (options.thinking !== void 0) {
|
|
3716
|
+
result.thinking = options.thinking;
|
|
3717
|
+
}
|
|
3718
|
+
if (options.live !== void 0) {
|
|
3719
|
+
result.live = options.live;
|
|
3720
|
+
}
|
|
3524
3721
|
return result;
|
|
3525
3722
|
}
|
|
3526
3723
|
|
|
@@ -4200,9 +4397,16 @@ function validateEnvironmentVars(envVars) {
|
|
|
4200
4397
|
case "JUNO_TASK_DEFAULT_MAX_ITERATIONS":
|
|
4201
4398
|
config.defaultMaxIterations = validateIterations(parseInt(value, 10));
|
|
4202
4399
|
break;
|
|
4203
|
-
case "JUNO_TASK_VERBOSE":
|
|
4204
|
-
|
|
4400
|
+
case "JUNO_TASK_VERBOSE": {
|
|
4401
|
+
const lower = value.toLowerCase();
|
|
4402
|
+
if (lower === "true" || lower === "yes") config.verbose = 1;
|
|
4403
|
+
else if (lower === "false" || lower === "no") config.verbose = 0;
|
|
4404
|
+
else {
|
|
4405
|
+
const n = Number(value);
|
|
4406
|
+
config.verbose = !isNaN(n) && n >= 0 && n <= 2 ? Math.floor(n) : 1;
|
|
4407
|
+
}
|
|
4205
4408
|
break;
|
|
4409
|
+
}
|
|
4206
4410
|
case "JUNO_TASK_QUIET":
|
|
4207
4411
|
config.quiet = value.toLowerCase() === "true";
|
|
4208
4412
|
break;
|