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.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.51";
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
  }
@@ -1022,6 +1025,9 @@ var init_shell_backend = __esm({
1022
1025
  if (isPython && subagentType === "pi" && request.arguments?.project_path) {
1023
1026
  args.push("--cd", String(request.arguments.project_path));
1024
1027
  }
1028
+ if (isPython && subagentType === "pi" && request.arguments?.live === true) {
1029
+ args.push("--live");
1030
+ }
1025
1031
  if (isPython && this.config.debug) {
1026
1032
  args.push("--verbose");
1027
1033
  }
@@ -1039,12 +1045,19 @@ var init_shell_backend = __esm({
1039
1045
  `Environment variables: ${Object.keys(env2).filter((k) => k.startsWith("JUNO_") || k.startsWith("PI_")).join(", ")}`
1040
1046
  );
1041
1047
  }
1048
+ const isPiLiveMode = isPython && subagentType === "pi" && request.arguments?.live === true;
1049
+ const shouldAttachLiveTerminal = isPiLiveMode && process.stdout.isTTY === true;
1050
+ if (this.config.debug && isPiLiveMode) {
1051
+ engineLogger.debug(
1052
+ `Pi live mode stdio: ${shouldAttachLiveTerminal ? "inherit (interactive TTY or stdout-tty fallback)" : "pipe (headless/non-TTY)"}`
1053
+ );
1054
+ }
1042
1055
  const child = child_process.spawn(command, args, {
1043
1056
  env: env2,
1044
1057
  cwd: this.config.workingDirectory,
1045
- stdio: ["pipe", "pipe", "pipe"]
1058
+ stdio: shouldAttachLiveTerminal ? "inherit" : ["pipe", "pipe", "pipe"]
1046
1059
  });
1047
- if (child.stdin) {
1060
+ if (!shouldAttachLiveTerminal && child.stdin) {
1048
1061
  child.stdin.end();
1049
1062
  }
1050
1063
  let stdout2 = "";
@@ -1116,7 +1129,8 @@ var init_shell_backend = __esm({
1116
1129
  subAgentResponse = JSON.parse(captured);
1117
1130
  }
1118
1131
  } catch (error) {
1119
- if (this.config?.debug) {
1132
+ const isNotFound = error?.code === "ENOENT";
1133
+ if (!isNotFound && this.config?.debug) {
1120
1134
  engineLogger.warn(
1121
1135
  `Failed to read subagent capture: ${error instanceof Error ? error.message : String(error)}`
1122
1136
  );
@@ -1327,6 +1341,8 @@ var init_shell_backend = __esm({
1327
1341
  delete inner.type;
1328
1342
  sanitizedPiEvent.sub_agent_response = inner;
1329
1343
  }
1344
+ const usage = piEvent.usage;
1345
+ const totalCostUsd = typeof piEvent.total_cost_usd === "number" ? piEvent.total_cost_usd : typeof usage?.cost?.total === "number" ? usage.cost.total : void 0;
1330
1346
  const structuredPayload = {
1331
1347
  type: "result",
1332
1348
  subtype: piEvent.subtype || (isError ? "error" : "success"),
@@ -1337,7 +1353,8 @@ var init_shell_backend = __esm({
1337
1353
  session_id: piEvent.session_id,
1338
1354
  exit_code: result.exitCode,
1339
1355
  duration_ms: piEvent.duration_ms ?? result.duration,
1340
- usage: piEvent.usage,
1356
+ total_cost_usd: totalCostUsd,
1357
+ usage,
1341
1358
  sub_agent_response: sanitizedPiEvent
1342
1359
  };
1343
1360
  const metadata = {
@@ -1728,6 +1745,16 @@ function getDefaultHooksJson(indent = 2) {
1728
1745
  return JSON.stringify(DEFAULT_HOOKS, null, indent);
1729
1746
  }
1730
1747
 
1748
+ // src/core/subagent-models.ts
1749
+ init_version();
1750
+ var SUBAGENT_DEFAULT_MODELS = {
1751
+ claude: ":sonnet",
1752
+ codex: ":codex",
1753
+ gemini: ":pro",
1754
+ cursor: "auto",
1755
+ pi: ":pi"
1756
+ };
1757
+
1731
1758
  // src/core/config.ts
1732
1759
  var ENV_VAR_MAPPING = {
1733
1760
  // Core settings
@@ -1777,18 +1804,31 @@ var JunoTaskConfigSchema = zod.z.object({
1777
1804
  defaultBackend: BackendTypeSchema.describe("Default backend to use for task execution"),
1778
1805
  defaultMaxIterations: zod.z.number().int().min(1).max(1e3).describe("Default maximum number of iterations for task execution"),
1779
1806
  defaultModel: zod.z.string().optional().describe("Default model to use for the subagent"),
1807
+ defaultModels: zod.z.record(SubagentTypeSchema, zod.z.string()).optional().describe("Optional per-subagent default model overrides"),
1780
1808
  // Project metadata
1781
1809
  mainTask: zod.z.string().optional().describe("Main task objective for the project"),
1782
1810
  // Logging settings
1783
1811
  logLevel: LogLevelSchema.describe("Logging level for the application"),
1784
1812
  logFile: zod.z.string().optional().describe("Path to log file (optional)"),
1785
- verbose: zod.z.boolean().describe("Enable verbose output"),
1813
+ verbose: zod.z.preprocess(
1814
+ (val) => {
1815
+ if (val === true) return 1;
1816
+ if (val === false) return 0;
1817
+ if (typeof val === "string") {
1818
+ const lower = val.toLowerCase().trim();
1819
+ if (lower === "true" || lower === "yes") return 1;
1820
+ if (lower === "false" || lower === "no") return 0;
1821
+ }
1822
+ return val;
1823
+ },
1824
+ zod.z.number().int().min(0).max(2)
1825
+ ).describe("Verbosity level: 0=quiet, 1=normal+helping texts (default), 2=debug+hooks"),
1786
1826
  quiet: zod.z.boolean().describe("Enable quiet mode (minimal output)"),
1787
1827
  // MCP settings
1788
1828
  mcpTimeout: zod.z.number().int().min(1e3).max(864e5).describe("MCP server timeout in milliseconds"),
1789
1829
  mcpRetries: zod.z.number().int().min(0).max(10).describe("Number of retries for MCP operations"),
1790
1830
  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")'),
1831
+ mcpServerName: zod.z.string().optional().describe("Named MCP server to connect to"),
1792
1832
  // Hook settings
1793
1833
  hookCommandTimeout: zod.z.number().int().min(1e3).max(36e5).optional().describe(
1794
1834
  "Timeout for individual hook commands in milliseconds (default: 300000 = 5 minutes)"
@@ -1803,6 +1843,11 @@ var JunoTaskConfigSchema = zod.z.object({
1803
1843
  // Paths
1804
1844
  workingDirectory: zod.z.string().describe("Working directory for task execution"),
1805
1845
  sessionDirectory: zod.z.string().describe("Directory for storing session data"),
1846
+ // Project environment bootstrap
1847
+ envFilePath: zod.z.string().optional().describe(
1848
+ "Path to the project env file loaded before execution (relative to project root or absolute)"
1849
+ ),
1850
+ envFileCopied: zod.z.boolean().optional().describe("Tracks whether configured env file has been initialized from .env.juno"),
1806
1851
  // Hooks configuration
1807
1852
  hooks: HooksSchema.describe(
1808
1853
  "Hook system configuration for executing commands at specific lifecycle events"
@@ -1815,16 +1860,15 @@ var DEFAULT_CONFIG = {
1815
1860
  defaultSubagent: "claude",
1816
1861
  defaultBackend: "shell",
1817
1862
  defaultMaxIterations: 1,
1863
+ defaultModels: { ...SUBAGENT_DEFAULT_MODELS },
1818
1864
  // Logging settings
1819
1865
  logLevel: "info",
1820
- verbose: false,
1866
+ verbose: 1,
1821
1867
  quiet: false,
1822
1868
  // MCP settings (also used by shell backend)
1823
1869
  mcpTimeout: 432e5,
1824
1870
  // 43200 seconds (12 hours) - default for long-running shell backend operations
1825
1871
  mcpRetries: 3,
1826
- mcpServerName: "roundtable-ai",
1827
- // Default to roundtable-ai server
1828
1872
  // Quota/hourly limit settings
1829
1873
  onHourlyLimit: "raise",
1830
1874
  // Default to exit immediately when hourly limit is reached
@@ -1834,6 +1878,9 @@ var DEFAULT_CONFIG = {
1834
1878
  // Paths
1835
1879
  workingDirectory: process.cwd(),
1836
1880
  sessionDirectory: path4__namespace.join(process.cwd(), ".juno_task"),
1881
+ // Project environment bootstrap
1882
+ envFilePath: ".env.juno",
1883
+ envFileCopied: false,
1837
1884
  // Hooks configuration - populated with default hooks template
1838
1885
  hooks: getDefaultHooks()
1839
1886
  };
@@ -1846,6 +1893,7 @@ var GLOBAL_CONFIG_FILE_NAMES = [
1846
1893
  // Will look for 'junoCode' field
1847
1894
  ];
1848
1895
  var PROJECT_CONFIG_FILE = ".juno_task/config.json";
1896
+ var DEFAULT_PROJECT_ENV_FILE = ".env.juno";
1849
1897
  function resolvePath(inputPath, basePath = process.cwd()) {
1850
1898
  if (path4__namespace.isAbsolute(inputPath)) {
1851
1899
  return inputPath;
@@ -1867,7 +1915,12 @@ function loadConfigFromEnv() {
1867
1915
  for (const [envVar, configKey] of Object.entries(ENV_VAR_MAPPING)) {
1868
1916
  const value = process.env[envVar];
1869
1917
  if (value !== void 0) {
1870
- config[configKey] = parseEnvValue(value);
1918
+ let parsed = parseEnvValue(value);
1919
+ if (configKey === "verbose") {
1920
+ if (parsed === true) parsed = 1;
1921
+ else if (parsed === false) parsed = 0;
1922
+ }
1923
+ config[configKey] = parsed;
1871
1924
  }
1872
1925
  }
1873
1926
  return config;
@@ -2101,6 +2154,95 @@ function validateConfig(config) {
2101
2154
  throw error;
2102
2155
  }
2103
2156
  }
2157
+ function parseEnvFileContent(content) {
2158
+ const envVars = {};
2159
+ const lines = content.split(/\r?\n/);
2160
+ for (const rawLine of lines) {
2161
+ const trimmedLine = rawLine.trim();
2162
+ if (!trimmedLine || trimmedLine.startsWith("#")) {
2163
+ continue;
2164
+ }
2165
+ const line = trimmedLine.startsWith("export ") ? trimmedLine.slice(7).trim() : trimmedLine;
2166
+ const separatorIndex = line.indexOf("=");
2167
+ if (separatorIndex === -1) {
2168
+ continue;
2169
+ }
2170
+ const key = line.slice(0, separatorIndex).trim();
2171
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
2172
+ continue;
2173
+ }
2174
+ let value = line.slice(separatorIndex + 1).trim();
2175
+ if ((value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) && value.length >= 2) {
2176
+ const quote = value[0];
2177
+ value = value.slice(1, -1);
2178
+ if (quote === '"') {
2179
+ value = value.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
2180
+ }
2181
+ } else {
2182
+ const inlineCommentIndex = value.indexOf(" #");
2183
+ if (inlineCommentIndex >= 0) {
2184
+ value = value.slice(0, inlineCommentIndex).trimEnd();
2185
+ }
2186
+ }
2187
+ envVars[key] = value;
2188
+ }
2189
+ return envVars;
2190
+ }
2191
+ async function loadEnvFileIntoProcess(envFilePath) {
2192
+ try {
2193
+ const content = await fs3.promises.readFile(envFilePath, "utf-8");
2194
+ const parsed = parseEnvFileContent(content);
2195
+ for (const [key, value] of Object.entries(parsed)) {
2196
+ process.env[key] = value;
2197
+ }
2198
+ } catch (error) {
2199
+ console.warn(`Warning: Failed to load env file ${envFilePath}: ${error}`);
2200
+ }
2201
+ }
2202
+ async function ensureAndLoadProjectEnv(baseDir) {
2203
+ const configPath = path4__namespace.join(baseDir, PROJECT_CONFIG_FILE);
2204
+ const defaultEnvPath = resolvePath(DEFAULT_PROJECT_ENV_FILE, baseDir);
2205
+ await fs__default.default.ensureFile(defaultEnvPath);
2206
+ let existingConfig = null;
2207
+ if (await fs__default.default.pathExists(configPath)) {
2208
+ try {
2209
+ existingConfig = await fs__default.default.readJson(configPath);
2210
+ } catch (error) {
2211
+ console.warn(`Warning: Failed to read ${configPath} for env bootstrap: ${error}`);
2212
+ }
2213
+ }
2214
+ const configuredEnvPathRaw = existingConfig && typeof existingConfig.envFilePath === "string" && existingConfig.envFilePath ? existingConfig.envFilePath : DEFAULT_PROJECT_ENV_FILE;
2215
+ const configuredEnvPath = resolvePath(configuredEnvPathRaw, baseDir);
2216
+ let envFileCopied = existingConfig && typeof existingConfig.envFileCopied === "boolean" ? existingConfig.envFileCopied : false;
2217
+ let needsConfigUpdate = false;
2218
+ if (configuredEnvPath !== defaultEnvPath) {
2219
+ const configuredExists = await fs__default.default.pathExists(configuredEnvPath);
2220
+ if (!configuredExists) {
2221
+ await fs__default.default.ensureDir(path4__namespace.dirname(configuredEnvPath));
2222
+ if (!envFileCopied) {
2223
+ await fs3.promises.copyFile(defaultEnvPath, configuredEnvPath);
2224
+ } else {
2225
+ await fs__default.default.ensureFile(configuredEnvPath);
2226
+ }
2227
+ }
2228
+ if (!envFileCopied) {
2229
+ envFileCopied = true;
2230
+ needsConfigUpdate = true;
2231
+ }
2232
+ }
2233
+ if (existingConfig && (needsConfigUpdate || typeof existingConfig.envFilePath !== "string" || typeof existingConfig.envFileCopied !== "boolean")) {
2234
+ const updatedConfig = {
2235
+ ...existingConfig,
2236
+ envFilePath: configuredEnvPathRaw,
2237
+ envFileCopied
2238
+ };
2239
+ await fs__default.default.writeJson(configPath, updatedConfig, { spaces: 2 });
2240
+ }
2241
+ await loadEnvFileIntoProcess(defaultEnvPath);
2242
+ if (configuredEnvPath !== defaultEnvPath) {
2243
+ await loadEnvFileIntoProcess(configuredEnvPath);
2244
+ }
2245
+ }
2104
2246
  async function ensureHooksConfig(baseDir) {
2105
2247
  try {
2106
2248
  const configDir = path4__namespace.join(baseDir, ".juno_task");
@@ -2123,19 +2265,30 @@ async function ensureHooksConfig(baseDir) {
2123
2265
  }
2124
2266
  if (!existingConfig.defaultModel) {
2125
2267
  const subagent = existingConfig.defaultSubagent || "claude";
2126
- const modelDefaults = {
2127
- claude: ":sonnet",
2128
- codex: ":codex",
2129
- gemini: ":pro",
2130
- cursor: "auto"
2131
- };
2132
- existingConfig.defaultModel = modelDefaults[subagent] || ":sonnet";
2268
+ existingConfig.defaultModel = SUBAGENT_DEFAULT_MODELS[subagent] || SUBAGENT_DEFAULT_MODELS.claude;
2269
+ needsUpdate = true;
2270
+ }
2271
+ if (!existingConfig.defaultModels || typeof existingConfig.defaultModels !== "object" || Array.isArray(existingConfig.defaultModels)) {
2272
+ const baseDefaults = { ...SUBAGENT_DEFAULT_MODELS };
2273
+ const subagent = existingConfig.defaultSubagent || "claude";
2274
+ if (typeof existingConfig.defaultModel === "string") {
2275
+ baseDefaults[subagent] = existingConfig.defaultModel;
2276
+ }
2277
+ existingConfig.defaultModels = baseDefaults;
2133
2278
  needsUpdate = true;
2134
2279
  }
2135
2280
  if (existingConfig.defaultMaxIterations === 50) {
2136
2281
  existingConfig.defaultMaxIterations = DEFAULT_CONFIG.defaultMaxIterations;
2137
2282
  needsUpdate = true;
2138
2283
  }
2284
+ if (!existingConfig.envFilePath) {
2285
+ existingConfig.envFilePath = DEFAULT_PROJECT_ENV_FILE;
2286
+ needsUpdate = true;
2287
+ }
2288
+ if (typeof existingConfig.envFileCopied !== "boolean") {
2289
+ existingConfig.envFileCopied = false;
2290
+ needsUpdate = true;
2291
+ }
2139
2292
  if (needsUpdate) {
2140
2293
  await fs__default.default.writeJson(configPath, existingConfig, { spaces: 2 });
2141
2294
  }
@@ -2147,6 +2300,7 @@ async function ensureHooksConfig(baseDir) {
2147
2300
  async function loadConfig(options = {}) {
2148
2301
  const { baseDir = process.cwd(), configFile, cliConfig } = options;
2149
2302
  await ensureHooksConfig(baseDir);
2303
+ await ensureAndLoadProjectEnv(baseDir);
2150
2304
  const loader = new ConfigLoader(baseDir);
2151
2305
  loader.fromEnvironment();
2152
2306
  if (configFile) {
@@ -2179,7 +2333,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
2179
2333
  logContext = "SYSTEM" /* SYSTEM */
2180
2334
  } = options;
2181
2335
  const contextLogger = logger.child(logContext);
2182
- contextLogger.info(`Starting hook execution: ${hookType}`, {
2336
+ contextLogger.debug(`Starting hook execution: ${hookType}`, {
2183
2337
  context,
2184
2338
  workingDirectory: context.workingDirectory || process.cwd(),
2185
2339
  commandTimeout,
@@ -2208,13 +2362,13 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
2208
2362
  commandsFailed: 0
2209
2363
  };
2210
2364
  }
2211
- contextLogger.info(`Executing ${hook.commands.length} commands for hook ${hookType}`);
2365
+ contextLogger.debug(`Executing ${hook.commands.length} commands for hook ${hookType}`);
2212
2366
  const commandResults = [];
2213
2367
  let commandsFailed = 0;
2214
2368
  for (let i = 0; i < hook.commands.length; i++) {
2215
2369
  const command = hook.commands[i];
2216
2370
  const commandStartTime = Date.now();
2217
- contextLogger.info(`Executing command ${i + 1}/${hook.commands.length}: ${command}`, {
2371
+ contextLogger.debug(`Executing command ${i + 1}/${hook.commands.length}: ${command}`, {
2218
2372
  commandIndex: i,
2219
2373
  totalCommands: hook.commands.length
2220
2374
  });
@@ -2262,7 +2416,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
2262
2416
  };
2263
2417
  commandResults.push(commandResult);
2264
2418
  if (success2) {
2265
- contextLogger.info(`Command completed successfully`, {
2419
+ contextLogger.debug(`Command completed successfully`, {
2266
2420
  command,
2267
2421
  exitCode: result2.exitCode,
2268
2422
  duration,
@@ -2343,7 +2497,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
2343
2497
  commandsExecuted,
2344
2498
  commandsFailed
2345
2499
  };
2346
- contextLogger.info(`Hook execution completed`, {
2500
+ contextLogger.debug(`Hook execution completed`, {
2347
2501
  hookType,
2348
2502
  totalDuration,
2349
2503
  commandsExecuted,
@@ -2354,7 +2508,7 @@ async function executeHook(hookType, hooks, context = {}, options = {}) {
2354
2508
  }
2355
2509
  async function executeHooks(hookTypes, hooks, context = {}, options = {}) {
2356
2510
  const results = [];
2357
- hookLogger.info(`Starting batch hook execution`, {
2511
+ hookLogger.debug(`Starting batch hook execution`, {
2358
2512
  hookTypes,
2359
2513
  context
2360
2514
  });
@@ -2365,7 +2519,7 @@ async function executeHooks(hookTypes, hooks, context = {}, options = {}) {
2365
2519
  const totalSuccess = results.every((r) => r.success);
2366
2520
  const totalCommands = results.reduce((sum, r) => sum + r.commandsExecuted, 0);
2367
2521
  const totalFailed = results.reduce((sum, r) => sum + r.commandsFailed, 0);
2368
- hookLogger.info(`Batch hook execution completed`, {
2522
+ hookLogger.debug(`Batch hook execution completed`, {
2369
2523
  hookTypes,
2370
2524
  totalHooks: results.length,
2371
2525
  totalCommands,
@@ -2657,6 +2811,30 @@ var ExecutionEngine = class extends events.EventEmitter {
2657
2811
  */
2658
2812
  setupProgressTracking() {
2659
2813
  }
2814
+ /**
2815
+ * Display hook execution output to stderr at verbose level 2 (debug+hooks).
2816
+ * At level 2: shows hook name, command stdout/stderr output.
2817
+ * At level 0-1: output is suppressed (hooks still execute).
2818
+ */
2819
+ displayHookOutput(hookResult) {
2820
+ if (this.engineConfig.config.verbose < 2) return;
2821
+ for (const cmdResult of hookResult.commandResults) {
2822
+ const prefix = `[hook:${hookResult.hookType}]`;
2823
+ if (cmdResult.stdout) {
2824
+ for (const line of cmdResult.stdout.split("\n")) {
2825
+ if (line.trim()) console.error(`${prefix} ${line}`);
2826
+ }
2827
+ }
2828
+ if (cmdResult.stderr) {
2829
+ for (const line of cmdResult.stderr.split("\n")) {
2830
+ if (line.trim()) console.error(`${prefix} ${line}`);
2831
+ }
2832
+ }
2833
+ if (!cmdResult.success) {
2834
+ console.error(`${prefix} command failed (exit ${cmdResult.exitCode}): ${cmdResult.command}`);
2835
+ }
2836
+ }
2837
+ }
2660
2838
  /**
2661
2839
  * Initialize backend for execution request.
2662
2840
  * Directly creates and configures a ShellBackend (no factory indirection).
@@ -2671,11 +2849,14 @@ var ExecutionEngine = class extends events.EventEmitter {
2671
2849
  backend.configure({
2672
2850
  workingDirectory: request.workingDirectory,
2673
2851
  servicesPath: `${process.env.HOME || process.env.USERPROFILE}/.juno_code/services`,
2674
- debug: this.engineConfig.config.verbose,
2852
+ debug: this.engineConfig.config.verbose >= 2,
2675
2853
  timeout: request.timeoutMs || this.engineConfig.config.mcpTimeout || 432e5,
2676
2854
  enableJsonStreaming: true,
2677
- outputRawJson: this.engineConfig.config.verbose,
2678
- environment: process.env,
2855
+ outputRawJson: this.engineConfig.config.verbose >= 1,
2856
+ environment: {
2857
+ ...process.env,
2858
+ JUNO_TASK_ROOT: process.env.JUNO_TASK_ROOT || request.workingDirectory
2859
+ },
2679
2860
  sessionId: request.requestId
2680
2861
  });
2681
2862
  await backend.initialize();
@@ -2697,7 +2878,7 @@ var ExecutionEngine = class extends events.EventEmitter {
2697
2878
  this.emit("progress:error", { event, error });
2698
2879
  }
2699
2880
  });
2700
- engineLogger.info(`Initialized ${backend.name} backend for execution`);
2881
+ engineLogger.debug(`Initialized ${backend.name} backend for execution`);
2701
2882
  }
2702
2883
  /**
2703
2884
  * Validate execution request parameters
@@ -2800,10 +2981,13 @@ var ExecutionEngine = class extends events.EventEmitter {
2800
2981
  async executeInternal(context) {
2801
2982
  context.status = "running" /* RUNNING */;
2802
2983
  context.sessionContext = { ...context.sessionContext, state: "active" };
2984
+ if (!process.env.JUNO_TASK_ROOT) {
2985
+ process.env.JUNO_TASK_ROOT = context.request.workingDirectory;
2986
+ }
2803
2987
  await this.initializeBackend(context.request);
2804
2988
  try {
2805
2989
  if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
2806
- await executeHook(
2990
+ const hookResult = await executeHook(
2807
2991
  "START_RUN",
2808
2992
  this.engineConfig.config.hooks,
2809
2993
  {
@@ -2823,6 +3007,7 @@ var ExecutionEngine = class extends events.EventEmitter {
2823
3007
  commandTimeout: this.engineConfig.config.hookCommandTimeout
2824
3008
  }
2825
3009
  );
3010
+ this.displayHookOutput(hookResult);
2826
3011
  }
2827
3012
  } catch (error) {
2828
3013
  engineLogger.warn("Hook START_RUN failed", { error });
@@ -2846,7 +3031,7 @@ var ExecutionEngine = class extends events.EventEmitter {
2846
3031
  }
2847
3032
  try {
2848
3033
  if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
2849
- await executeHook(
3034
+ const hookResult = await executeHook(
2850
3035
  "END_RUN",
2851
3036
  this.engineConfig.config.hooks,
2852
3037
  {
@@ -2868,6 +3053,7 @@ var ExecutionEngine = class extends events.EventEmitter {
2868
3053
  commandTimeout: this.engineConfig.config.hookCommandTimeout
2869
3054
  }
2870
3055
  );
3056
+ this.displayHookOutput(hookResult);
2871
3057
  }
2872
3058
  } catch (error) {
2873
3059
  engineLogger.warn("Hook END_RUN failed", { error });
@@ -2912,7 +3098,7 @@ var ExecutionEngine = class extends events.EventEmitter {
2912
3098
  const iterationStart = /* @__PURE__ */ new Date();
2913
3099
  try {
2914
3100
  if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
2915
- await executeHook(
3101
+ const hookResult = await executeHook(
2916
3102
  "START_ITERATION",
2917
3103
  this.engineConfig.config.hooks,
2918
3104
  {
@@ -2933,6 +3119,7 @@ var ExecutionEngine = class extends events.EventEmitter {
2933
3119
  commandTimeout: this.engineConfig.config.hookCommandTimeout
2934
3120
  }
2935
3121
  );
3122
+ this.displayHookOutput(hookResult);
2936
3123
  }
2937
3124
  } catch (error) {
2938
3125
  engineLogger.warn("Hook START_ITERATION failed", { error, iterationNumber });
@@ -2959,6 +3146,8 @@ var ExecutionEngine = class extends events.EventEmitter {
2959
3146
  ...context.request.continueConversation !== void 0 && {
2960
3147
  continueConversation: context.request.continueConversation
2961
3148
  },
3149
+ ...context.request.thinking !== void 0 && { thinking: context.request.thinking },
3150
+ ...context.request.live !== void 0 && { live: context.request.live },
2962
3151
  iteration: iterationNumber
2963
3152
  },
2964
3153
  timeout: context.request.timeoutMs || this.engineConfig.config.mcpTimeout,
@@ -2995,7 +3184,7 @@ var ExecutionEngine = class extends events.EventEmitter {
2995
3184
  this.emit("iteration:complete", { context, iterationResult });
2996
3185
  try {
2997
3186
  if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
2998
- await executeHook(
3187
+ const hookResult = await executeHook(
2999
3188
  "END_ITERATION",
3000
3189
  this.engineConfig.config.hooks,
3001
3190
  {
@@ -3017,6 +3206,7 @@ var ExecutionEngine = class extends events.EventEmitter {
3017
3206
  commandTimeout: this.engineConfig.config.hookCommandTimeout
3018
3207
  }
3019
3208
  );
3209
+ this.displayHookOutput(hookResult);
3020
3210
  }
3021
3211
  } catch (error) {
3022
3212
  engineLogger.warn("Hook END_ITERATION failed", { error, iterationNumber });
@@ -3051,7 +3241,7 @@ var ExecutionEngine = class extends events.EventEmitter {
3051
3241
  this.emit("iteration:error", { context, iterationResult });
3052
3242
  try {
3053
3243
  if (this.engineConfig.config.hooks && !this.engineConfig.config.skipHooks) {
3054
- await executeHook(
3244
+ const hookResult = await executeHook(
3055
3245
  "END_ITERATION",
3056
3246
  this.engineConfig.config.hooks,
3057
3247
  {
@@ -3074,6 +3264,7 @@ var ExecutionEngine = class extends events.EventEmitter {
3074
3264
  commandTimeout: this.engineConfig.config.hookCommandTimeout
3075
3265
  }
3076
3266
  );
3267
+ this.displayHookOutput(hookResult);
3077
3268
  }
3078
3269
  } catch (hookError) {
3079
3270
  engineLogger.warn("Hook END_ITERATION failed", { error: hookError, iterationNumber });
@@ -3549,6 +3740,12 @@ function createExecutionRequest(options) {
3549
3740
  if (options.continueConversation !== void 0) {
3550
3741
  result.continueConversation = options.continueConversation;
3551
3742
  }
3743
+ if (options.thinking !== void 0) {
3744
+ result.thinking = options.thinking;
3745
+ }
3746
+ if (options.live !== void 0) {
3747
+ result.live = options.live;
3748
+ }
3552
3749
  return result;
3553
3750
  }
3554
3751
 
@@ -4228,9 +4425,16 @@ function validateEnvironmentVars(envVars) {
4228
4425
  case "JUNO_TASK_DEFAULT_MAX_ITERATIONS":
4229
4426
  config.defaultMaxIterations = validateIterations(parseInt(value, 10));
4230
4427
  break;
4231
- case "JUNO_TASK_VERBOSE":
4232
- config.verbose = value.toLowerCase() === "true";
4428
+ case "JUNO_TASK_VERBOSE": {
4429
+ const lower = value.toLowerCase();
4430
+ if (lower === "true" || lower === "yes") config.verbose = 1;
4431
+ else if (lower === "false" || lower === "no") config.verbose = 0;
4432
+ else {
4433
+ const n = Number(value);
4434
+ config.verbose = !isNaN(n) && n >= 0 && n <= 2 ? Math.floor(n) : 1;
4435
+ }
4233
4436
  break;
4437
+ }
4234
4438
  case "JUNO_TASK_QUIET":
4235
4439
  config.quiet = value.toLowerCase() === "true";
4236
4440
  break;