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.
Files changed (33) hide show
  1. package/README.md +508 -202
  2. package/dist/bin/cli.d.mts +1 -1
  3. package/dist/bin/cli.d.ts +1 -1
  4. package/dist/bin/cli.js +3332 -1421
  5. package/dist/bin/cli.js.map +1 -1
  6. package/dist/bin/cli.mjs +3316 -1405
  7. package/dist/bin/cli.mjs.map +1 -1
  8. package/dist/bin/feedback-collector.js.map +1 -1
  9. package/dist/bin/feedback-collector.mjs.map +1 -1
  10. package/dist/index.d.mts +56 -19
  11. package/dist/index.d.ts +56 -19
  12. package/dist/index.js +240 -36
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.mjs +240 -36
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/templates/scripts/install_requirements.sh +55 -5
  17. package/dist/templates/scripts/kanban.sh +11 -0
  18. package/dist/templates/services/README.md +23 -4
  19. package/dist/templates/services/__pycache__/pi.cpython-313.pyc +0 -0
  20. package/dist/templates/services/pi.py +1933 -262
  21. package/dist/templates/skills/claude/kanban-workflow/SKILL.md +138 -0
  22. package/dist/templates/skills/claude/plan-kanban-tasks/SKILL.md +1 -1
  23. package/dist/templates/skills/claude/ralph-loop/scripts/kanban.sh +11 -0
  24. package/dist/templates/skills/claude/understand-project/SKILL.md +1 -1
  25. package/dist/templates/skills/codex/kanban-workflow/SKILL.md +139 -0
  26. package/dist/templates/skills/codex/plan-kanban-tasks/SKILL.md +32 -0
  27. package/dist/templates/skills/codex/ralph-loop/scripts/kanban.sh +11 -0
  28. package/dist/templates/skills/codex/understand-project/SKILL.md +46 -0
  29. package/dist/templates/skills/pi/kanban-workflow/SKILL.md +139 -0
  30. package/dist/templates/skills/pi/plan-kanban-tasks/SKILL.md +1 -1
  31. package/dist/templates/skills/pi/ralph-loop/SKILL.md +4 -0
  32. package/dist/templates/skills/pi/understand-project/SKILL.md +1 -1
  33. 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.49";
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
- if (this.config?.debug) {
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
- usage: piEvent.usage,
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.boolean().describe("Enable verbose output"),
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('Named MCP server to connect to (e.g., "roundtable-ai")'),
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: false,
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
- config[configKey] = parseEnvValue(value);
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
- const modelDefaults = {
2099
- claude: ":sonnet",
2100
- codex: ":codex",
2101
- gemini: ":pro",
2102
- cursor: "auto"
2103
- };
2104
- existingConfig.defaultModel = modelDefaults[subagent] || ":sonnet";
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.info(`Starting hook execution: ${hookType}`, {
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.info(`Executing ${hook.commands.length} commands for hook ${hookType}`);
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.info(`Executing command ${i + 1}/${hook.commands.length}: ${command}`, {
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.info(`Command completed successfully`, {
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.info(`Hook execution completed`, {
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.info(`Starting batch hook execution`, {
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.info(`Batch hook execution completed`, {
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: process.env,
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.info(`Initialized ${backend.name} backend for execution`);
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
- config.verbose = value.toLowerCase() === "true";
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;