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.
Files changed (32) hide show
  1. package/README.md +417 -203
  2. package/dist/bin/cli.d.mts +1 -1
  3. package/dist/bin/cli.d.ts +1 -1
  4. package/dist/bin/cli.js +1736 -976
  5. package/dist/bin/cli.js.map +1 -1
  6. package/dist/bin/cli.mjs +1735 -975
  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 +33 -7
  11. package/dist/index.d.ts +33 -7
  12. package/dist/index.js +202 -27
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.mjs +202 -27
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/templates/scripts/install_requirements.sh +41 -3
  17. package/dist/templates/scripts/kanban.sh +4 -0
  18. package/dist/templates/services/__pycache__/pi.cpython-313.pyc +0 -0
  19. package/dist/templates/services/pi.py +1281 -238
  20. package/dist/templates/skills/claude/kanban-workflow/SKILL.md +138 -0
  21. package/dist/templates/skills/claude/plan-kanban-tasks/SKILL.md +1 -1
  22. package/dist/templates/skills/claude/ralph-loop/scripts/kanban.sh +4 -0
  23. package/dist/templates/skills/claude/understand-project/SKILL.md +1 -1
  24. package/dist/templates/skills/codex/kanban-workflow/SKILL.md +139 -0
  25. package/dist/templates/skills/codex/plan-kanban-tasks/SKILL.md +32 -0
  26. package/dist/templates/skills/codex/ralph-loop/scripts/kanban.sh +4 -0
  27. package/dist/templates/skills/codex/understand-project/SKILL.md +46 -0
  28. package/dist/templates/skills/pi/kanban-workflow/SKILL.md +139 -0
  29. package/dist/templates/skills/pi/plan-kanban-tasks/SKILL.md +1 -1
  30. package/dist/templates/skills/pi/ralph-loop/SKILL.md +4 -0
  31. package/dist/templates/skills/pi/understand-project/SKILL.md +1 -1
  32. 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.49";
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
- if (this.config?.debug) {
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
- usage: piEvent.usage,
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.boolean().describe("Enable verbose output"),
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('Named MCP server to connect to (e.g., "roundtable-ai")'),
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: false,
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
- config[configKey] = parseEnvValue(value);
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.info(`Starting hook execution: ${hookType}`, {
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.info(`Executing ${hook.commands.length} commands for hook ${hookType}`);
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.info(`Executing command ${i + 1}/${hook.commands.length}: ${command}`, {
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.info(`Command completed successfully`, {
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.info(`Hook execution completed`, {
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.info(`Starting batch hook execution`, {
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.info(`Batch hook execution completed`, {
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: process.env,
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.info(`Initialized ${backend.name} backend for execution`);
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
- config.verbose = value.toLowerCase() === "true";
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;