juno-code 1.0.51 → 1.0.53

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/dist/bin/cli.mjs CHANGED
@@ -14,9 +14,10 @@ import * as yaml from 'js-yaml';
14
14
  import { createHash } from 'crypto';
15
15
  import chalk21 from 'chalk';
16
16
  import { execa } from 'execa';
17
+ import * as childProcess from 'child_process';
17
18
  import { spawn } from 'child_process';
19
+ import { inspect, promisify } from 'util';
18
20
  import { EventEmitter } from 'events';
19
- import { inspect } from 'util';
20
21
  import { Command, Option } from 'commander';
21
22
  import * as readline from 'readline';
22
23
  import { createInterface } from 'readline';
@@ -259,14 +260,14 @@ function isModelCompatibleWithSubagent(model, subagent) {
259
260
  }
260
261
  }
261
262
  function getConfiguredDefaultModelForSubagent(config, subagent) {
262
- const legacyDefaultModel = config.defaultSubagent === subagent && typeof config.defaultModel === "string" && isModelCompatibleWithSubagent(config.defaultModel, subagent) ? config.defaultModel : void 0;
263
- if (legacyDefaultModel) {
264
- return legacyDefaultModel;
265
- }
266
263
  const modelFromMap = config.defaultModels?.[subagent];
267
264
  if (typeof modelFromMap === "string" && isModelCompatibleWithSubagent(modelFromMap, subagent)) {
268
265
  return modelFromMap;
269
266
  }
267
+ const legacyDefaultModel = config.defaultSubagent === subagent && typeof config.defaultModel === "string" && isModelCompatibleWithSubagent(config.defaultModel, subagent) ? config.defaultModel : void 0;
268
+ if (legacyDefaultModel) {
269
+ return legacyDefaultModel;
270
+ }
270
271
  return void 0;
271
272
  }
272
273
  var SUBAGENT_DEFAULT_MODELS;
@@ -834,6 +835,13 @@ async function ensureHooksConfig(baseDir) {
834
835
  existingConfig.defaultModels = baseDefaults;
835
836
  needsUpdate = true;
836
837
  }
838
+ const selectedSubagentRaw = typeof existingConfig.defaultSubagent === "string" ? existingConfig.defaultSubagent : "claude";
839
+ const selectedSubagent = selectedSubagentRaw in SUBAGENT_DEFAULT_MODELS ? selectedSubagentRaw : "claude";
840
+ const selectedMapModel = existingConfig.defaultModels && typeof existingConfig.defaultModels === "object" ? existingConfig.defaultModels[selectedSubagent] : void 0;
841
+ if (typeof selectedMapModel === "string" && isModelCompatibleWithSubagent(selectedMapModel, selectedSubagent) && existingConfig.defaultModel !== selectedMapModel) {
842
+ existingConfig.defaultModel = selectedMapModel;
843
+ needsUpdate = true;
844
+ }
837
845
  if (existingConfig.defaultMaxIterations === 50) {
838
846
  existingConfig.defaultMaxIterations = DEFAULT_CONFIG.defaultMaxIterations;
839
847
  needsUpdate = true;
@@ -2358,25 +2366,28 @@ var init_shell_backend = __esm({
2358
2366
  const scriptPath = await this.findScriptForSubagent(subagentType);
2359
2367
  const result = await this.executeScript(scriptPath, request, toolId, subagentType);
2360
2368
  const duration = Date.now() - startTime;
2369
+ const structuredResult = this.buildStructuredOutput(subagentType, result);
2370
+ const structuredPayload = this.parseStructuredResultPayload(structuredResult.content);
2371
+ const structuredIndicatesError = structuredPayload?.is_error === true || structuredPayload?.subtype === "error";
2372
+ const executionSucceeded = result.success && !structuredIndicatesError;
2361
2373
  await this.emitProgressEvent({
2362
2374
  sessionId: request.metadata?.sessionId || "unknown",
2363
2375
  timestamp: /* @__PURE__ */ new Date(),
2364
2376
  backend: "shell",
2365
2377
  count: ++this.eventCounter,
2366
2378
  type: "tool_result" /* TOOL_RESULT */,
2367
- content: `${request.toolName} completed successfully (${duration}ms)`,
2379
+ content: executionSucceeded ? `${request.toolName} completed successfully (${duration}ms)` : `${request.toolName} completed with error (${duration}ms)`,
2368
2380
  toolId,
2369
2381
  metadata: {
2370
2382
  toolName: request.toolName,
2371
2383
  duration,
2372
- success: result.success,
2384
+ success: executionSucceeded,
2373
2385
  phase: "completion"
2374
2386
  }
2375
2387
  });
2376
- const structuredResult = this.buildStructuredOutput(subagentType, result);
2377
2388
  const toolResult = {
2378
2389
  content: structuredResult.content,
2379
- status: result.success ? "completed" /* COMPLETED */ : "failed" /* FAILED */,
2390
+ status: executionSucceeded ? "completed" /* COMPLETED */ : "failed" /* FAILED */,
2380
2391
  startTime: new Date(startTime),
2381
2392
  endTime: /* @__PURE__ */ new Date(),
2382
2393
  duration,
@@ -2385,6 +2396,9 @@ var init_shell_backend = __esm({
2385
2396
  };
2386
2397
  if (result.error) {
2387
2398
  toolResult.error = new Error(result.error);
2399
+ } else if (!executionSucceeded) {
2400
+ const structuredErrorMessage = typeof structuredPayload?.error === "string" && structuredPayload.error || typeof structuredPayload?.result === "string" && structuredPayload.result || `${request.toolName} reported a structured error`;
2401
+ toolResult.error = new Error(structuredErrorMessage);
2388
2402
  }
2389
2403
  if (structuredResult.metadata) {
2390
2404
  toolResult.metadata = structuredResult.metadata;
@@ -2636,6 +2650,9 @@ var init_shell_backend = __esm({
2636
2650
  if (isPython && subagentType === "pi" && request.arguments?.live === true) {
2637
2651
  args.push("--live");
2638
2652
  }
2653
+ if (isPython && subagentType === "pi" && request.arguments?.liveInteractiveSession === true) {
2654
+ args.push("--live-manual");
2655
+ }
2639
2656
  if (isPython && this.config.debug) {
2640
2657
  args.push("--verbose");
2641
2658
  }
@@ -2918,8 +2935,19 @@ var init_shell_backend = __esm({
2918
2935
  if (subagentType === "pi") {
2919
2936
  const piEvent = result.subAgentResponse ?? this.extractLastJsonEvent(result.output);
2920
2937
  if (piEvent) {
2921
- let resultText = piEvent.result;
2922
- if (!resultText && Array.isArray(piEvent.messages)) {
2938
+ const piNestedEvent = typeof piEvent.sub_agent_response === "object" && piEvent.sub_agent_response ? piEvent.sub_agent_response : void 0;
2939
+ const piSessionId = typeof piEvent.session_id === "string" && piEvent.session_id ? piEvent.session_id : typeof piEvent.sessionId === "string" && piEvent.sessionId ? piEvent.sessionId : typeof piEvent.id === "string" && piEvent.type === "session" ? piEvent.id : typeof piNestedEvent?.session_id === "string" && piNestedEvent.session_id ? piNestedEvent.session_id : typeof piNestedEvent?.sessionId === "string" && piNestedEvent.sessionId ? piNestedEvent.sessionId : typeof piNestedEvent?.id === "string" && piNestedEvent.type === "session" ? piNestedEvent.id : typeof piEvent.sub_agent_response?.session_id === "string" && piEvent.sub_agent_response.session_id ? piEvent.sub_agent_response.session_id : void 0;
2940
+ const sanitizedPiEvent = { ...piEvent };
2941
+ delete sanitizedPiEvent.messages;
2942
+ if (sanitizedPiEvent.sub_agent_response && typeof sanitizedPiEvent.sub_agent_response === "object") {
2943
+ const inner = { ...sanitizedPiEvent.sub_agent_response };
2944
+ delete inner.messages;
2945
+ delete inner.type;
2946
+ sanitizedPiEvent.sub_agent_response = inner;
2947
+ }
2948
+ const hasDirectResultText = typeof piEvent.result === "string";
2949
+ let resultText = hasDirectResultText ? piEvent.result : void 0;
2950
+ if (resultText === void 0 && Array.isArray(piEvent.messages)) {
2923
2951
  for (let i = piEvent.messages.length - 1; i >= 0; i--) {
2924
2952
  const msg = piEvent.messages[i];
2925
2953
  if (msg?.role === "assistant") {
@@ -2939,16 +2967,11 @@ var init_shell_backend = __esm({
2939
2967
  }
2940
2968
  }
2941
2969
  }
2942
- if (resultText) {
2970
+ if (resultText === void 0 && typeof piEvent.error === "string") {
2971
+ resultText = piEvent.error;
2972
+ }
2973
+ if (resultText !== void 0) {
2943
2974
  const isError = piEvent.is_error ?? !result.success;
2944
- const sanitizedPiEvent = { ...piEvent };
2945
- delete sanitizedPiEvent.messages;
2946
- if (sanitizedPiEvent.sub_agent_response && typeof sanitizedPiEvent.sub_agent_response === "object") {
2947
- const inner = { ...sanitizedPiEvent.sub_agent_response };
2948
- delete inner.messages;
2949
- delete inner.type;
2950
- sanitizedPiEvent.sub_agent_response = inner;
2951
- }
2952
2975
  const usage = piEvent.usage;
2953
2976
  const totalCostUsd = typeof piEvent.total_cost_usd === "number" ? piEvent.total_cost_usd : typeof usage?.cost?.total === "number" ? usage.cost.total : void 0;
2954
2977
  const structuredPayload = {
@@ -2956,9 +2979,9 @@ var init_shell_backend = __esm({
2956
2979
  subtype: piEvent.subtype || (isError ? "error" : "success"),
2957
2980
  is_error: isError,
2958
2981
  result: resultText,
2959
- error: piEvent.error,
2982
+ error: isError ? piEvent.error ?? result.error ?? resultText : piEvent.error,
2960
2983
  stderr: result.error,
2961
- session_id: piEvent.session_id,
2984
+ session_id: piSessionId,
2962
2985
  exit_code: result.exitCode,
2963
2986
  duration_ms: piEvent.duration_ms ?? result.duration,
2964
2987
  total_cost_usd: totalCostUsd,
@@ -2976,6 +2999,57 @@ var init_shell_backend = __esm({
2976
2999
  metadata
2977
3000
  };
2978
3001
  }
3002
+ const isSessionSnapshotOnly = piEvent.type === "session" || piEvent.subtype === "session";
3003
+ if (isSessionSnapshotOnly) {
3004
+ const errorMessage = result.error?.trim() || "Pi exited before emitting a terminal result event (session snapshot only).";
3005
+ const structuredPayload = {
3006
+ type: "result",
3007
+ subtype: "error",
3008
+ is_error: true,
3009
+ result: errorMessage,
3010
+ error: errorMessage,
3011
+ stderr: result.error,
3012
+ session_id: piSessionId,
3013
+ exit_code: result.exitCode,
3014
+ duration_ms: result.duration,
3015
+ sub_agent_response: sanitizedPiEvent
3016
+ };
3017
+ const metadata = {
3018
+ ...piEvent ? { subAgentResponse: piEvent } : void 0,
3019
+ structuredOutput: true,
3020
+ contentType: "application/json",
3021
+ rawOutput: result.output
3022
+ };
3023
+ return {
3024
+ content: JSON.stringify(structuredPayload),
3025
+ metadata
3026
+ };
3027
+ }
3028
+ if (!result.success) {
3029
+ const errorMessage = result.error?.trim() || result.output?.trim() || "Unknown error";
3030
+ const structuredPayload = {
3031
+ type: "result",
3032
+ subtype: "error",
3033
+ is_error: true,
3034
+ result: errorMessage,
3035
+ error: errorMessage,
3036
+ stderr: result.error,
3037
+ session_id: piSessionId,
3038
+ exit_code: result.exitCode,
3039
+ duration_ms: result.duration,
3040
+ sub_agent_response: sanitizedPiEvent
3041
+ };
3042
+ const metadata = {
3043
+ ...piEvent ? { subAgentResponse: piEvent } : void 0,
3044
+ structuredOutput: true,
3045
+ contentType: "application/json",
3046
+ rawOutput: result.output
3047
+ };
3048
+ return {
3049
+ content: JSON.stringify(structuredPayload),
3050
+ metadata
3051
+ };
3052
+ }
2979
3053
  }
2980
3054
  }
2981
3055
  if (!result.success) {
@@ -3040,6 +3114,20 @@ var init_shell_backend = __esm({
3040
3114
  }
3041
3115
  return null;
3042
3116
  }
3117
+ /**
3118
+ * Parse JSON structured output payload emitted by shell service wrappers.
3119
+ */
3120
+ parseStructuredResultPayload(content) {
3121
+ try {
3122
+ const parsed = JSON.parse(content);
3123
+ if (!parsed || typeof parsed !== "object") {
3124
+ return null;
3125
+ }
3126
+ return parsed;
3127
+ } catch {
3128
+ return null;
3129
+ }
3130
+ }
3043
3131
  /**
3044
3132
  * Extract the last valid JSON object from a script's stdout to use as a structured payload fallback.
3045
3133
  */
@@ -3298,6 +3386,179 @@ var init_shell_backend = __esm({
3298
3386
  };
3299
3387
  }
3300
3388
  });
3389
+ function findPromptCommandSubstitutions(prompt) {
3390
+ const matches = [];
3391
+ let cursor = 0;
3392
+ while (cursor < prompt.length) {
3393
+ const singleQuotedStart = prompt.indexOf(SINGLE_QUOTED_MARKER, cursor);
3394
+ const tripleBacktickStart = prompt.indexOf(TRIPLE_BACKTICK_MARKER, cursor);
3395
+ const markerStart = chooseNearestMarker(singleQuotedStart, tripleBacktickStart);
3396
+ if (markerStart === null) {
3397
+ break;
3398
+ }
3399
+ if (markerStart === singleQuotedStart) {
3400
+ const parsedSingleQuoted = parseSingleQuotedSubstitution(prompt, markerStart);
3401
+ if (!parsedSingleQuoted) {
3402
+ cursor = markerStart + SINGLE_QUOTED_MARKER.length;
3403
+ continue;
3404
+ }
3405
+ matches.push(parsedSingleQuoted);
3406
+ cursor = parsedSingleQuoted.endIndex;
3407
+ continue;
3408
+ }
3409
+ const parsedTripleBacktick = parseTripleBacktickSubstitution(prompt, markerStart);
3410
+ if (!parsedTripleBacktick) {
3411
+ cursor = markerStart + TRIPLE_BACKTICK_MARKER.length;
3412
+ continue;
3413
+ }
3414
+ matches.push(parsedTripleBacktick);
3415
+ cursor = parsedTripleBacktick.endIndex;
3416
+ }
3417
+ return matches;
3418
+ }
3419
+ async function resolvePromptCommandSubstitutions(prompt, options) {
3420
+ const matches = findPromptCommandSubstitutions(prompt);
3421
+ if (matches.length === 0) {
3422
+ return prompt;
3423
+ }
3424
+ const executor = options.executor ?? createDefaultPromptCommandExecutor(options);
3425
+ let result = "";
3426
+ let cursor = 0;
3427
+ for (const match of matches) {
3428
+ result += prompt.slice(cursor, match.startIndex);
3429
+ const commandOutput = await executor(match.command);
3430
+ result += normalizeCommandOutput(commandOutput);
3431
+ cursor = match.endIndex;
3432
+ }
3433
+ result += prompt.slice(cursor);
3434
+ return result;
3435
+ }
3436
+ function chooseNearestMarker(singleQuotedStart, tripleBacktickStart) {
3437
+ const singleExists = singleQuotedStart >= 0;
3438
+ const tripleExists = tripleBacktickStart >= 0;
3439
+ if (!singleExists && !tripleExists) {
3440
+ return null;
3441
+ }
3442
+ if (!singleExists) {
3443
+ return tripleBacktickStart;
3444
+ }
3445
+ if (!tripleExists) {
3446
+ return singleQuotedStart;
3447
+ }
3448
+ return Math.min(singleQuotedStart, tripleBacktickStart);
3449
+ }
3450
+ function parseSingleQuotedSubstitution(prompt, markerStart) {
3451
+ const contentStart = markerStart + SINGLE_QUOTED_MARKER.length;
3452
+ const closingQuote = findClosingSingleQuote(prompt, contentStart);
3453
+ if (closingQuote < 0) {
3454
+ return null;
3455
+ }
3456
+ const raw = prompt.slice(markerStart, closingQuote + 1);
3457
+ const command = prompt.slice(contentStart, closingQuote);
3458
+ return {
3459
+ syntax: "single-quoted",
3460
+ startIndex: markerStart,
3461
+ endIndex: closingQuote + 1,
3462
+ command,
3463
+ raw
3464
+ };
3465
+ }
3466
+ function findClosingSingleQuote(prompt, startIndex) {
3467
+ let escaped = false;
3468
+ for (let index = startIndex; index < prompt.length; index++) {
3469
+ const char = prompt[index];
3470
+ if (char === "'" && !escaped) {
3471
+ return index;
3472
+ }
3473
+ if (char === "\\" && !escaped) {
3474
+ escaped = true;
3475
+ continue;
3476
+ }
3477
+ escaped = false;
3478
+ }
3479
+ return -1;
3480
+ }
3481
+ function parseTripleBacktickSubstitution(prompt, markerStart) {
3482
+ const contentStart = markerStart + TRIPLE_BACKTICK_MARKER.length;
3483
+ const closingBackticks = prompt.indexOf(TRIPLE_BACKTICK_CLOSER, contentStart);
3484
+ if (closingBackticks < 0) {
3485
+ return null;
3486
+ }
3487
+ const raw = prompt.slice(markerStart, closingBackticks + TRIPLE_BACKTICK_CLOSER.length);
3488
+ const command = prompt.slice(contentStart, closingBackticks);
3489
+ return {
3490
+ syntax: "triple-backtick",
3491
+ startIndex: markerStart,
3492
+ endIndex: closingBackticks + TRIPLE_BACKTICK_CLOSER.length,
3493
+ command,
3494
+ raw
3495
+ };
3496
+ }
3497
+ function createDefaultPromptCommandExecutor(options) {
3498
+ const execFile3 = promisify(childProcess.execFile);
3499
+ const maxBufferBytes = options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;
3500
+ const commandTimeoutMs = resolvePromptCommandTimeoutMs(options.commandTimeoutMs);
3501
+ const shell = process.env.SHELL || "/bin/bash";
3502
+ return async (command) => {
3503
+ const normalizedCommand = command.trim();
3504
+ if (!normalizedCommand) {
3505
+ return "";
3506
+ }
3507
+ const commandForExecution = wrapCommandForNonInteractiveExecution(normalizedCommand);
3508
+ try {
3509
+ const result = await execFile3(shell, ["-lc", commandForExecution], {
3510
+ cwd: options.workingDirectory,
3511
+ env: options.environment ?? process.env,
3512
+ maxBuffer: maxBufferBytes,
3513
+ timeout: commandTimeoutMs
3514
+ });
3515
+ const stdout2 = typeof result === "string" || Buffer.isBuffer(result) ? String(result) : String(result.stdout ?? "");
3516
+ return stdout2;
3517
+ } catch (error) {
3518
+ const failedCommand = normalizedCommand.replace(/\s+/g, " ").trim();
3519
+ const details = error && typeof error === "object" && "stderr" in error ? String(error.stderr ?? "").trim() : "";
3520
+ const timeoutDetected = error && typeof error === "object" && (("code" in error ? String(error.code ?? "").toUpperCase() === "ETIMEDOUT" : false) || "killed" in error && Boolean(error.killed) && String(error.signal ?? "").toUpperCase() === "SIGTERM" || "message" in error && /timed?\s*out/i.test(String(error.message ?? "")));
3521
+ if (timeoutDetected) {
3522
+ throw new Error(
3523
+ `Prompt command substitution timed out after ${commandTimeoutMs}ms for \`${failedCommand}\``
3524
+ );
3525
+ }
3526
+ const suffix = details ? `: ${details}` : "";
3527
+ throw new Error(`Prompt command substitution failed for \`${failedCommand}\`${suffix}`);
3528
+ }
3529
+ };
3530
+ }
3531
+ function resolvePromptCommandTimeoutMs(explicitTimeoutMs) {
3532
+ if (typeof explicitTimeoutMs === "number" && Number.isFinite(explicitTimeoutMs) && explicitTimeoutMs > 0) {
3533
+ return explicitTimeoutMs;
3534
+ }
3535
+ const envValue = process.env[COMMAND_TIMEOUT_ENV_KEY];
3536
+ if (envValue !== void 0) {
3537
+ const parsed = Number(envValue);
3538
+ if (Number.isFinite(parsed) && parsed > 0) {
3539
+ return parsed;
3540
+ }
3541
+ }
3542
+ return DEFAULT_COMMAND_TIMEOUT_MS;
3543
+ }
3544
+ function wrapCommandForNonInteractiveExecution(command) {
3545
+ return `(${command}) </dev/null`;
3546
+ }
3547
+ function normalizeCommandOutput(output) {
3548
+ return output.replace(/\r?\n$/, "");
3549
+ }
3550
+ var SINGLE_QUOTED_MARKER, TRIPLE_BACKTICK_MARKER, TRIPLE_BACKTICK_CLOSER, DEFAULT_MAX_BUFFER_BYTES, DEFAULT_COMMAND_TIMEOUT_MS, COMMAND_TIMEOUT_ENV_KEY;
3551
+ var init_prompt_command_substitution = __esm({
3552
+ "src/core/prompt-command-substitution.ts"() {
3553
+ init_version();
3554
+ SINGLE_QUOTED_MARKER = "!'";
3555
+ TRIPLE_BACKTICK_MARKER = "!```";
3556
+ TRIPLE_BACKTICK_CLOSER = "```";
3557
+ DEFAULT_MAX_BUFFER_BYTES = 1024 * 1024;
3558
+ DEFAULT_COMMAND_TIMEOUT_MS = 3e4;
3559
+ COMMAND_TIMEOUT_ENV_KEY = "JUNO_CODE_PROMPT_SUBSTITUTION_TIMEOUT_MS";
3560
+ }
3561
+ });
3301
3562
  function createExecutionEngine(config) {
3302
3563
  return new ExecutionEngine({
3303
3564
  config,
@@ -3348,6 +3609,9 @@ function createExecutionRequest(options) {
3348
3609
  if (options.live !== void 0) {
3349
3610
  result.live = options.live;
3350
3611
  }
3612
+ if (options.liveInteractiveSession !== void 0) {
3613
+ result.liveInteractiveSession = options.liveInteractiveSession;
3614
+ }
3351
3615
  return result;
3352
3616
  }
3353
3617
  var DEFAULT_ERROR_RECOVERY_CONFIG, DEFAULT_RATE_LIMIT_CONFIG, DEFAULT_PROGRESS_CONFIG, ExecutionEngine;
@@ -3358,6 +3622,7 @@ var init_engine = __esm({
3358
3622
  init_hooks();
3359
3623
  init_advanced_logger();
3360
3624
  init_shell_backend();
3625
+ init_prompt_command_substitution();
3361
3626
  DEFAULT_ERROR_RECOVERY_CONFIG = {
3362
3627
  maxAttempts: {
3363
3628
  connection: 3,
@@ -3654,7 +3919,8 @@ var init_engine = __esm({
3654
3919
  if (!request.requestId?.trim()) {
3655
3920
  throw new Error("Request ID is required");
3656
3921
  }
3657
- if (!request.instruction?.trim()) {
3922
+ const allowEmptyInstructionForPiLiveInteractiveSession = request.subagent === "pi" && request.live === true && request.liveInteractiveSession === true && typeof request.resume === "string" && request.resume.trim().length > 0;
3923
+ if (!request.instruction?.trim() && !allowEmptyInstructionForPiLiveInteractiveSession) {
3658
3924
  throw new Error("Instruction is required");
3659
3925
  }
3660
3926
  if (!request.subagent?.trim()) {
@@ -3892,44 +4158,62 @@ var init_engine = __esm({
3892
4158
  engineLogger.warn("Hook START_ITERATION failed", { error, iterationNumber });
3893
4159
  }
3894
4160
  this.emit("iteration:start", { context, iterationNumber });
3895
- const toolRequest = {
3896
- toolName: this.getToolNameForSubagent(context.request.subagent),
3897
- arguments: {
3898
- instruction: context.request.instruction,
3899
- project_path: context.request.workingDirectory,
3900
- ...context.request.model !== void 0 && { model: context.request.model },
3901
- ...context.request.agents !== void 0 && { agents: context.request.agents },
3902
- ...context.request.tools !== void 0 && { tools: context.request.tools },
3903
- ...context.request.allowedTools !== void 0 && {
3904
- allowedTools: context.request.allowedTools
3905
- },
3906
- ...context.request.appendAllowedTools !== void 0 && {
3907
- appendAllowedTools: context.request.appendAllowedTools
3908
- },
3909
- ...context.request.disallowedTools !== void 0 && {
3910
- disallowedTools: context.request.disallowedTools
4161
+ let toolRequest = null;
4162
+ try {
4163
+ const instructionTemplate = context.request.instruction;
4164
+ const resolvedInstruction = await resolvePromptCommandSubstitutions(instructionTemplate, {
4165
+ workingDirectory: context.request.workingDirectory,
4166
+ environment: {
4167
+ ...process.env,
4168
+ JUNO_TASK_ROOT: process.env.JUNO_TASK_ROOT || context.request.workingDirectory
4169
+ }
4170
+ });
4171
+ this.emit("iteration:instruction-resolved", {
4172
+ context,
4173
+ iterationNumber,
4174
+ instruction: resolvedInstruction,
4175
+ templateInstruction: instructionTemplate
4176
+ });
4177
+ toolRequest = {
4178
+ toolName: this.getToolNameForSubagent(context.request.subagent),
4179
+ arguments: {
4180
+ instruction: resolvedInstruction,
4181
+ project_path: context.request.workingDirectory,
4182
+ ...context.request.model !== void 0 && { model: context.request.model },
4183
+ ...context.request.agents !== void 0 && { agents: context.request.agents },
4184
+ ...context.request.tools !== void 0 && { tools: context.request.tools },
4185
+ ...context.request.allowedTools !== void 0 && {
4186
+ allowedTools: context.request.allowedTools
4187
+ },
4188
+ ...context.request.appendAllowedTools !== void 0 && {
4189
+ appendAllowedTools: context.request.appendAllowedTools
4190
+ },
4191
+ ...context.request.disallowedTools !== void 0 && {
4192
+ disallowedTools: context.request.disallowedTools
4193
+ },
4194
+ ...context.request.resume !== void 0 && { resume: context.request.resume },
4195
+ ...context.request.continueConversation !== void 0 && {
4196
+ continueConversation: context.request.continueConversation
4197
+ },
4198
+ ...context.request.thinking !== void 0 && { thinking: context.request.thinking },
4199
+ ...context.request.live !== void 0 && { live: context.request.live },
4200
+ ...context.request.liveInteractiveSession !== void 0 && {
4201
+ liveInteractiveSession: context.request.liveInteractiveSession
4202
+ },
4203
+ iteration: iterationNumber
3911
4204
  },
3912
- ...context.request.resume !== void 0 && { resume: context.request.resume },
3913
- ...context.request.continueConversation !== void 0 && {
3914
- continueConversation: context.request.continueConversation
4205
+ timeout: context.request.timeoutMs || this.engineConfig.config.mcpTimeout,
4206
+ priority: context.request.priority || "normal",
4207
+ metadata: {
4208
+ sessionId: context.sessionContext.sessionId,
4209
+ iterationNumber
3915
4210
  },
3916
- ...context.request.thinking !== void 0 && { thinking: context.request.thinking },
3917
- ...context.request.live !== void 0 && { live: context.request.live },
3918
- iteration: iterationNumber
3919
- },
3920
- timeout: context.request.timeoutMs || this.engineConfig.config.mcpTimeout,
3921
- priority: context.request.priority || "normal",
3922
- metadata: {
3923
- sessionId: context.sessionContext.sessionId,
3924
- iterationNumber
3925
- },
3926
- progressCallback: async (event) => {
3927
- context.progressEvents.push(event);
3928
- context.statistics.totalProgressEvents++;
3929
- await this.processProgressEvent(context, event);
3930
- }
3931
- };
3932
- try {
4211
+ progressCallback: async (event) => {
4212
+ context.progressEvents.push(event);
4213
+ context.statistics.totalProgressEvents++;
4214
+ await this.processProgressEvent(context, event);
4215
+ }
4216
+ };
3933
4217
  if (!this.currentBackend) {
3934
4218
  throw new Error("No backend initialized. Call initializeBackend() first.");
3935
4219
  }
@@ -3998,7 +4282,10 @@ var init_engine = __esm({
3998
4282
  duration,
3999
4283
  error: mcpError,
4000
4284
  progressEvents: [],
4001
- request: toolRequest
4285
+ request: toolRequest ?? {
4286
+ toolName: this.getToolNameForSubagent(context.request.subagent),
4287
+ arguments: {}
4288
+ }
4002
4289
  },
4003
4290
  progressEvents: [],
4004
4291
  error: mcpError
@@ -5070,10 +5357,13 @@ var init_terminal_progress_writer = __esm({
5070
5357
  // src/cli/commands/main.ts
5071
5358
  var main_exports = {};
5072
5359
  __export(main_exports, {
5360
+ expandKanbanTaskReferencesInPrompt: () => expandKanbanTaskReferencesInPrompt,
5073
5361
  getActiveSessionId: () => getActiveSessionId,
5074
5362
  getDefaultModelForSubagent: () => getDefaultModelForSubagent,
5075
5363
  isModelCompatibleWithSubagent: () => isModelCompatibleWithSubagent,
5076
- mainCommandHandler: () => mainCommandHandler
5364
+ mainCommandHandler: () => mainCommandHandler,
5365
+ normalizeLeadingPromptDirectiveArtifacts: () => normalizeLeadingPromptDirectiveArtifacts,
5366
+ rewriteLeadingPromptShortcut: () => rewriteLeadingPromptShortcut
5077
5367
  });
5078
5368
  function normalizeVerboseLevel(verbose, quiet) {
5079
5369
  if (quiet) return 0;
@@ -5140,6 +5430,169 @@ function toStringArray(value) {
5140
5430
  const normalized = value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter((entry) => entry.length > 0);
5141
5431
  return normalized.length > 0 ? normalized : void 0;
5142
5432
  }
5433
+ function extractReferencedKanbanTaskIds(prompt) {
5434
+ const taskIds = [];
5435
+ const seen = /* @__PURE__ */ new Set();
5436
+ for (const match of prompt.matchAll(KANBAN_TASK_REFERENCE_REGEX)) {
5437
+ const taskId = match[1];
5438
+ if (!taskId || seen.has(taskId)) {
5439
+ continue;
5440
+ }
5441
+ seen.add(taskId);
5442
+ taskIds.push(taskId);
5443
+ }
5444
+ return taskIds;
5445
+ }
5446
+ function normalizeKanbanTaskArray(payload) {
5447
+ if (Array.isArray(payload)) {
5448
+ return payload.filter((entry) => Boolean(entry) && typeof entry === "object");
5449
+ }
5450
+ if (payload && typeof payload === "object") {
5451
+ return [payload];
5452
+ }
5453
+ return [];
5454
+ }
5455
+ async function runKanbanGetCommand(command, args, workingDirectory) {
5456
+ try {
5457
+ const execFile3 = promisify(childProcess.execFile);
5458
+ const result = await execFile3(command, args, {
5459
+ cwd: workingDirectory,
5460
+ env: {
5461
+ ...process.env,
5462
+ JUNO_TASK_ROOT: process.env.JUNO_TASK_ROOT || workingDirectory
5463
+ },
5464
+ maxBuffer: 1024 * 1024
5465
+ });
5466
+ const stdout2 = typeof result === "string" || Buffer.isBuffer(result) ? String(result) : String(result.stdout ?? "");
5467
+ const parsed = JSON.parse(stdout2);
5468
+ return normalizeKanbanTaskArray(parsed);
5469
+ } catch {
5470
+ return null;
5471
+ }
5472
+ }
5473
+ async function fetchKanbanTasksForCommand(command, taskIds, workingDirectory) {
5474
+ const tasksById = /* @__PURE__ */ new Map();
5475
+ if (taskIds.length === 0) {
5476
+ return tasksById;
5477
+ }
5478
+ const requestedTaskIds = new Set(taskIds);
5479
+ const addFetchedTasks = (fetchedTasks) => {
5480
+ if (!fetchedTasks) {
5481
+ return;
5482
+ }
5483
+ for (const task of fetchedTasks) {
5484
+ const taskId = typeof task.id === "string" ? task.id : void 0;
5485
+ if (!taskId || !requestedTaskIds.has(taskId)) {
5486
+ continue;
5487
+ }
5488
+ tasksById.set(taskId, task);
5489
+ }
5490
+ };
5491
+ addFetchedTasks(await runKanbanGetCommand(command, ["get", ...taskIds], workingDirectory));
5492
+ const unresolvedTaskIds = taskIds.filter((taskId) => !tasksById.has(taskId));
5493
+ for (const taskId of unresolvedTaskIds) {
5494
+ addFetchedTasks(await runKanbanGetCommand(command, ["get", taskId], workingDirectory));
5495
+ }
5496
+ return tasksById;
5497
+ }
5498
+ async function fetchReferencedKanbanTasks(taskIds, workingDirectory) {
5499
+ const tasksById = /* @__PURE__ */ new Map();
5500
+ if (taskIds.length === 0) {
5501
+ return tasksById;
5502
+ }
5503
+ const kanbanScriptPath = path15.join(workingDirectory, KANBAN_TASK_SCRIPT_RELATIVE_PATH);
5504
+ const hasKanbanScript = await fs16.pathExists(kanbanScriptPath);
5505
+ const commandAttempts = [];
5506
+ if (hasKanbanScript) {
5507
+ commandAttempts.push(kanbanScriptPath);
5508
+ }
5509
+ commandAttempts.push("juno-kanban");
5510
+ let unresolvedTaskIds = [...taskIds];
5511
+ for (const command of commandAttempts) {
5512
+ if (unresolvedTaskIds.length === 0) {
5513
+ break;
5514
+ }
5515
+ const fetchedTasks = await fetchKanbanTasksForCommand(command, unresolvedTaskIds, workingDirectory);
5516
+ if (fetchedTasks.size === 0) {
5517
+ continue;
5518
+ }
5519
+ for (const [taskId, task] of fetchedTasks.entries()) {
5520
+ tasksById.set(taskId, task);
5521
+ }
5522
+ unresolvedTaskIds = unresolvedTaskIds.filter((taskId) => !tasksById.has(taskId));
5523
+ }
5524
+ return tasksById;
5525
+ }
5526
+ async function expandKanbanTaskReferencesInPrompt(prompt, workingDirectory) {
5527
+ const referencedTaskIds = extractReferencedKanbanTaskIds(prompt);
5528
+ if (referencedTaskIds.length === 0) {
5529
+ return prompt;
5530
+ }
5531
+ const tasksById = await fetchReferencedKanbanTasks(referencedTaskIds, workingDirectory);
5532
+ if (tasksById.size === 0) {
5533
+ return prompt;
5534
+ }
5535
+ return prompt.replace(KANBAN_TASK_REFERENCE_REGEX, (fullMatch, taskId) => {
5536
+ const task = tasksById.get(taskId);
5537
+ if (!task) {
5538
+ return fullMatch;
5539
+ }
5540
+ return `
5541
+ [kanban_task:${taskId}]
5542
+ ${JSON.stringify(task, null, 2)}
5543
+ [/kanban_task]`;
5544
+ });
5545
+ }
5546
+ function normalizeLeadingPromptDirectiveArtifacts(prompt) {
5547
+ const lines = prompt.split(/\r?\n/);
5548
+ if (lines.length === 0) {
5549
+ return prompt;
5550
+ }
5551
+ let index = 0;
5552
+ while (index < lines.length && lines[index].trim() === "") {
5553
+ index += 1;
5554
+ }
5555
+ if (index >= lines.length) {
5556
+ return prompt;
5557
+ }
5558
+ const firstMeaningfulLine = lines[index].trim();
5559
+ if (!LEADING_PROMPT_DELIMITER_MARKERS.has(firstMeaningfulLine)) {
5560
+ return prompt;
5561
+ }
5562
+ let directiveIndex = index + 1;
5563
+ while (directiveIndex < lines.length && lines[directiveIndex].trim() === "") {
5564
+ directiveIndex += 1;
5565
+ }
5566
+ if (directiveIndex >= lines.length) {
5567
+ return prompt;
5568
+ }
5569
+ const directiveCandidate = lines[directiveIndex].trimStart();
5570
+ if (!LEADING_DIRECTIVE_LINE_REGEX.test(directiveCandidate)) {
5571
+ return prompt;
5572
+ }
5573
+ return lines.slice(directiveIndex).join("\n");
5574
+ }
5575
+ function rewriteLeadingPromptShortcut(prompt, subagent) {
5576
+ const match = prompt.match(LEADING_PROMPT_SHORTCUT_REGEX);
5577
+ if (!match) {
5578
+ return prompt;
5579
+ }
5580
+ const shortcut = match[1] ?? match[2];
5581
+ if (!shortcut) {
5582
+ return prompt;
5583
+ }
5584
+ const remaining = match[3] ?? "";
5585
+ switch (subagent) {
5586
+ case "claude":
5587
+ return `/${shortcut}${remaining}`;
5588
+ case "pi":
5589
+ return `/skill:${shortcut}${remaining}`;
5590
+ case "codex":
5591
+ return `$${shortcut}${remaining}`;
5592
+ default:
5593
+ return prompt;
5594
+ }
5595
+ }
5143
5596
  function resolveContinueEnvFilePath(workingDirectory, configuredPath) {
5144
5597
  const candidate = configuredPath && configuredPath.trim() ? configuredPath.trim() : DEFAULT_ENV_FILE_NAME;
5145
5598
  return path15.isAbsolute(candidate) ? candidate : path15.join(workingDirectory, candidate);
@@ -5542,7 +5995,13 @@ async function mainCommandHandler(_args, options, _command) {
5542
5995
  ]);
5543
5996
  }
5544
5997
  const promptProcessor = new PromptProcessor(options);
5545
- const instruction = await promptProcessor.processPrompt();
5998
+ const rawInstruction = await promptProcessor.processPrompt();
5999
+ const normalizedInstruction = normalizeLeadingPromptDirectiveArtifacts(rawInstruction);
6000
+ const rewrittenInstruction = rewriteLeadingPromptShortcut(normalizedInstruction, options.subagent);
6001
+ const instruction = await expandKanbanTaskReferencesInPrompt(
6002
+ rewrittenInstruction,
6003
+ config.workingDirectory
6004
+ );
5546
6005
  const selectedBackend = "shell";
5547
6006
  if (options.allowedTools && options.appendAllowedTools) {
5548
6007
  console.error(
@@ -5561,6 +6020,7 @@ async function mainCommandHandler(_args, options, _command) {
5561
6020
  }
5562
6021
  const configuredModel = getConfiguredDefaultModelForSubagent(config, options.subagent);
5563
6022
  const resolvedModel = options.model || configuredModel || getDefaultModelForSubagent(options.subagent);
6023
+ const liveInteractiveSession = options.continueFromLatest === true && options.subagent === "pi" && options.live === true && instruction.length === 0 && typeof options.resume === "string" && options.resume.trim().length > 0;
5564
6024
  const executionRequest = createExecutionRequest({
5565
6025
  instruction,
5566
6026
  subagent: options.subagent,
@@ -5576,7 +6036,8 @@ async function mainCommandHandler(_args, options, _command) {
5576
6036
  ...options.resume !== void 0 ? { resume: options.resume } : {},
5577
6037
  ...options.continue !== void 0 ? { continueConversation: options.continue } : {},
5578
6038
  ...options.thinking !== void 0 ? { thinking: options.thinking } : {},
5579
- ...options.live !== void 0 ? { live: options.live } : {}
6039
+ ...options.live !== void 0 ? { live: options.live } : {},
6040
+ ...liveInteractiveSession ? { liveInteractiveSession: true } : {}
5580
6041
  });
5581
6042
  const coordinator = new MainExecutionCoordinator(
5582
6043
  config,
@@ -5654,7 +6115,7 @@ async function mainCommandHandler(_args, options, _command) {
5654
6115
  }
5655
6116
  }
5656
6117
  }
5657
- var SESSION_HISTORY_VERSION, SESSION_HISTORY_FILE_NAME, CONTINUE_SETTINGS_VERSION, DEFAULT_ENV_FILE_NAME, PromptProcessor, _activeSessionId, MainProgressDisplay, MainExecutionCoordinator;
6118
+ var SESSION_HISTORY_VERSION, SESSION_HISTORY_FILE_NAME, CONTINUE_SETTINGS_VERSION, DEFAULT_ENV_FILE_NAME, LEADING_PROMPT_SHORTCUT_REGEX, LEADING_PROMPT_DELIMITER_MARKERS, LEADING_DIRECTIVE_LINE_REGEX, KANBAN_TASK_REFERENCE_REGEX, KANBAN_TASK_SCRIPT_RELATIVE_PATH, PromptProcessor, _activeSessionId, MainProgressDisplay, MainExecutionCoordinator;
5658
6119
  var init_main = __esm({
5659
6120
  "src/cli/commands/main.ts"() {
5660
6121
  init_version();
@@ -5671,6 +6132,11 @@ var init_main = __esm({
5671
6132
  SESSION_HISTORY_FILE_NAME = "session_history.json";
5672
6133
  CONTINUE_SETTINGS_VERSION = 1;
5673
6134
  DEFAULT_ENV_FILE_NAME = ".env.juno";
6135
+ LEADING_PROMPT_SHORTCUT_REGEX = /^%(?:\{([^\s{}]+)\}|([^\s%][^\s]*))(.*)$/s;
6136
+ LEADING_PROMPT_DELIMITER_MARKERS = /* @__PURE__ */ new Set(["---", "***", "___"]);
6137
+ LEADING_DIRECTIVE_LINE_REGEX = /^(?:%(?:\{[^\s{}]+\}|[^\s%][^\s]*)|\/skill:[^\s]+|\/[^\s]+|\$[^\s]+)/;
6138
+ KANBAN_TASK_REFERENCE_REGEX = /(?<!#)##\s*\{?([A-Za-z0-9]{6})\}?(?![A-Za-z0-9])/g;
6139
+ KANBAN_TASK_SCRIPT_RELATIVE_PATH = path15.join(".juno_task", "scripts", "kanban.sh");
5674
6140
  PromptProcessor = class {
5675
6141
  constructor(options) {
5676
6142
  this.options = options;
@@ -5688,6 +6154,9 @@ var init_main = __esm({
5688
6154
  if (this.hasRedirectedStdin()) {
5689
6155
  return await this.readPipedStdin();
5690
6156
  }
6157
+ if (this.shouldOpenLiveContinueSessionWithoutPrompt()) {
6158
+ return "";
6159
+ }
5691
6160
  if (this.options.interactive) {
5692
6161
  return await this.collectInteractivePrompt();
5693
6162
  } else {
@@ -5726,6 +6195,9 @@ var init_main = __esm({
5726
6195
  return false;
5727
6196
  }
5728
6197
  }
6198
+ shouldOpenLiveContinueSessionWithoutPrompt() {
6199
+ return this.options.continueFromLatest === true && this.options.subagent === "pi" && this.options.live === true && !this.options.promptFile && this.options.prompt !== true && !this.options.interactive && !this.options.interactivePrompt;
6200
+ }
5729
6201
  async isFilePath(prompt) {
5730
6202
  if (prompt.includes("\n") || prompt.length > 500) {
5731
6203
  return false;
@@ -5834,6 +6306,7 @@ var init_main = __esm({
5834
6306
  // iteration# → sub-agent session_id
5835
6307
  latestSessionId = null;
5836
6308
  // most recent session_id seen
6309
+ lastResolvedInstructionByIteration = /* @__PURE__ */ new Map();
5837
6310
  constructor(verboseLevel = 1) {
5838
6311
  this.verboseLevel = verboseLevel;
5839
6312
  }
@@ -5860,9 +6333,18 @@ var init_main = __esm({
5860
6333
  console.error(chalk21.gray(` Request ID: ${request.requestId}`));
5861
6334
  console.error(chalk21.gray(` Working Directory: ${request.workingDirectory}`));
5862
6335
  }
5863
- console.error(chalk21.blue("\n\u{1F4CB} Task:"));
5864
- const preview = request.instruction.length > 200 ? request.instruction.substring(0, 200) + "..." : request.instruction;
5865
- console.error(chalk21.white(` ${preview}`));
6336
+ const hasPromptSubstitutionSyntax = this.hasPromptCommandSubstitutionSyntax(request.instruction);
6337
+ if (hasPromptSubstitutionSyntax) {
6338
+ console.error(chalk21.blue("\n\u{1F4CB} Task Template:"));
6339
+ } else {
6340
+ console.error(chalk21.blue("\n\u{1F4CB} Task:"));
6341
+ }
6342
+ console.error(chalk21.white(` ${this.buildInstructionPreview(request.instruction)}`));
6343
+ if (hasPromptSubstitutionSyntax) {
6344
+ console.error(
6345
+ chalk21.gray(" Prompt-time substitutions are resolved immediately before each subagent call.")
6346
+ );
6347
+ }
5866
6348
  console.error("");
5867
6349
  }
5868
6350
  getSelectedExecutionOptions(request) {
@@ -5896,6 +6378,32 @@ var init_main = __esm({
5896
6378
  if (value.length <= maxLength) return value;
5897
6379
  return `${value.substring(0, maxLength - 3)}...`;
5898
6380
  }
6381
+ buildInstructionPreview(instruction, maxLength = 200) {
6382
+ if (instruction.length <= maxLength) {
6383
+ return instruction;
6384
+ }
6385
+ return `${instruction.substring(0, maxLength)}...`;
6386
+ }
6387
+ hasPromptCommandSubstitutionSyntax(instruction) {
6388
+ return instruction.includes("!'") || instruction.includes("!```");
6389
+ }
6390
+ onInstructionResolved(iteration, resolvedInstruction, templateInstruction) {
6391
+ const previousInstruction = this.lastResolvedInstructionByIteration.get(iteration);
6392
+ if (previousInstruction === resolvedInstruction) {
6393
+ return;
6394
+ }
6395
+ this.lastResolvedInstructionByIteration.set(iteration, resolvedInstruction);
6396
+ if (this.verboseLevel === 0) {
6397
+ return;
6398
+ }
6399
+ if (templateInstruction !== void 0 && resolvedInstruction === templateInstruction) {
6400
+ return;
6401
+ }
6402
+ const heading = iteration === 1 ? "\n\u{1F9E9} Resolved Task (iteration 1):" : `
6403
+ \u{1F9E9} Resolved Task (iteration ${iteration}):`;
6404
+ console.error(chalk21.blue(heading));
6405
+ console.error(chalk21.white(` ${this.buildInstructionPreview(resolvedInstruction)}`));
6406
+ }
5899
6407
  onProgress(event) {
5900
6408
  const timestamp = event.timestamp.toLocaleTimeString();
5901
6409
  if (event.metadata?.sessionId && typeof event.metadata.sessionId === "string") {
@@ -5973,7 +6481,10 @@ var init_main = __esm({
5973
6481
  if (this.verboseLevel === 0) {
5974
6482
  const lastIteration2 = result.iterations[result.iterations.length - 1];
5975
6483
  if (lastIteration2?.toolResult.content && !this.hasStreamedJsonOutput) {
5976
- console.log(lastIteration2.toolResult.content);
6484
+ const displayContent = this.getDisplayResultContent(lastIteration2.toolResult.content);
6485
+ if (displayContent.trim().length > 0) {
6486
+ console.log(displayContent);
6487
+ }
5977
6488
  }
5978
6489
  return;
5979
6490
  }
@@ -5986,12 +6497,14 @@ var init_main = __esm({
5986
6497
  }
5987
6498
  const lastIteration = result.iterations[result.iterations.length - 1];
5988
6499
  const structuredOutput = lastIteration?.toolResult.metadata?.structuredOutput === true;
6500
+ const rawResultContent = lastIteration?.toolResult.content || "";
6501
+ const displayResultContent = rawResultContent ? this.getDisplayResultContent(rawResultContent) : "";
5989
6502
  const shouldPrintResult = Boolean(
5990
- lastIteration && lastIteration.toolResult.content && (!this.hasStreamedJsonOutput || structuredOutput)
6503
+ lastIteration && displayResultContent && (!this.hasStreamedJsonOutput || structuredOutput)
5991
6504
  );
5992
6505
  if (shouldPrintResult) {
5993
6506
  console.error(chalk21.blue("\n\u{1F4C4} Result:"));
5994
- console.log(lastIteration.toolResult.content);
6507
+ console.log(displayResultContent);
5995
6508
  }
5996
6509
  const iterationCosts = this.extractIterationCosts(result);
5997
6510
  const totalCostUsd = [...iterationCosts.values()].reduce((sum, cost) => sum + cost, 0);
@@ -6061,6 +6574,26 @@ var init_main = __esm({
6061
6574
  console.error(chalk21.gray("\n\u{1F511} Session ID: could not be extracted"));
6062
6575
  }
6063
6576
  }
6577
+ getDisplayResultContent(content) {
6578
+ if (this.verboseLevel >= 2) {
6579
+ return content;
6580
+ }
6581
+ try {
6582
+ const payload = JSON.parse(content);
6583
+ if (payload?.type === "result" && Object.prototype.hasOwnProperty.call(payload, "result")) {
6584
+ const resultValue = payload.result;
6585
+ if (typeof resultValue === "string") {
6586
+ return resultValue;
6587
+ }
6588
+ if (resultValue === null || resultValue === void 0) {
6589
+ return "";
6590
+ }
6591
+ return JSON.stringify(resultValue, null, 2);
6592
+ }
6593
+ } catch {
6594
+ }
6595
+ return content;
6596
+ }
6064
6597
  /**
6065
6598
  * Extract session IDs from iteration results' structured payloads
6066
6599
  */
@@ -6209,6 +6742,13 @@ var init_main = __esm({
6209
6742
  engine.on("iteration:start", ({ iterationNumber }) => {
6210
6743
  this.progressDisplay.onIterationStart(iterationNumber);
6211
6744
  });
6745
+ engine.on("iteration:instruction-resolved", ({ iterationNumber, instruction, templateInstruction }) => {
6746
+ this.progressDisplay.onInstructionResolved(
6747
+ iterationNumber,
6748
+ typeof instruction === "string" ? instruction : "",
6749
+ typeof templateInstruction === "string" ? templateInstruction : void 0
6750
+ );
6751
+ });
6212
6752
  engine.on("iteration:complete", ({ iterationResult }) => {
6213
6753
  this.progressDisplay.onIterationComplete(iterationResult.success, iterationResult.duration);
6214
6754
  });
@@ -14774,6 +15314,26 @@ function normalizeVerbose(value) {
14774
15314
  if (!isNaN(num) && num >= 0 && num <= 2) return Math.floor(num);
14775
15315
  return 1;
14776
15316
  }
15317
+ function extractOptionValueFromArgv(argv2, longOption, shortOption) {
15318
+ for (let i = 0; i < argv2.length; i++) {
15319
+ const token = argv2[i];
15320
+ if (!token) continue;
15321
+ if (token === longOption || token === shortOption) {
15322
+ const next = argv2[i + 1];
15323
+ if (next && !next.startsWith("-")) {
15324
+ return next;
15325
+ }
15326
+ continue;
15327
+ }
15328
+ if (token.startsWith(`${longOption}=`)) {
15329
+ return token.slice(longOption.length + 1);
15330
+ }
15331
+ if (token.startsWith(`${shortOption}=`)) {
15332
+ return token.slice(shortOption.length + 1);
15333
+ }
15334
+ }
15335
+ return void 0;
15336
+ }
14777
15337
  function isConnectionLikeError(err) {
14778
15338
  const msg = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
14779
15339
  const lower = msg.toLowerCase();
@@ -14904,6 +15464,15 @@ function getForwardedUntilCompletionArgs() {
14904
15464
  if (arg.startsWith("--pre-run-hook=")) {
14905
15465
  continue;
14906
15466
  }
15467
+ if (arg === "--cwd" || arg === "-w") {
15468
+ if (i + 1 < args.length && args[i + 1] && !args[i + 1].startsWith("-")) {
15469
+ i += 1;
15470
+ }
15471
+ continue;
15472
+ }
15473
+ if (arg.startsWith("--cwd=") || arg.startsWith("-w=")) {
15474
+ continue;
15475
+ }
14907
15476
  forwardedArgs.push(arg);
14908
15477
  }
14909
15478
  return forwardedArgs;
@@ -14915,7 +15484,9 @@ async function runUntilCompletionScriptIfRequested(options) {
14915
15484
  const { spawn: spawn3 } = await import('child_process');
14916
15485
  const path21 = await import('path');
14917
15486
  const fs22 = await import('fs-extra');
14918
- const scriptPath = path21.join(process.cwd(), ".juno_task", "scripts", "run_until_completion.sh");
15487
+ const optionCwd = typeof options.cwd === "string" && options.cwd.trim().length > 0 ? options.cwd.trim() : extractOptionValueFromArgv(process.argv.slice(2), "--cwd", "-w");
15488
+ const invocationCwd = typeof optionCwd === "string" && optionCwd.trim().length > 0 ? path21.resolve(process.cwd(), optionCwd) : process.cwd();
15489
+ const scriptPath = path21.join(invocationCwd, ".juno_task", "scripts", "run_until_completion.sh");
14919
15490
  if (!await fs22.pathExists(scriptPath)) {
14920
15491
  console.error(chalk21.red.bold("\n\u274C Error: run_until_completion.sh not found"));
14921
15492
  console.error(chalk21.red(` Expected location: ${scriptPath}`));
@@ -14932,7 +15503,7 @@ async function runUntilCompletionScriptIfRequested(options) {
14932
15503
  scriptArgs.push(...getForwardedUntilCompletionArgs());
14933
15504
  const child = spawn3(scriptPath, scriptArgs, {
14934
15505
  stdio: "inherit",
14935
- cwd: process.cwd()
15506
+ cwd: invocationCwd
14936
15507
  });
14937
15508
  process.removeAllListeners("SIGINT");
14938
15509
  process.removeAllListeners("SIGTERM");
@@ -14964,9 +15535,9 @@ async function runUntilCompletionScriptIfRequested(options) {
14964
15535
  return true;
14965
15536
  }
14966
15537
  function setupContinueCommand(program) {
14967
- const continueCommand = program.command("continue").description("Continue the most recent juno-code session with saved settings").argument("[prompt_text...]", "Prompt text (positional, alternative to -p)").option(
15538
+ const continueCommand = program.command("continue").alias("contiue").description("Continue the most recent juno-code session with saved settings").argument("[prompt_text...]", "Prompt text (positional, alternative to -p)").option(
14968
15539
  "-p, --prompt [text]",
14969
- "Prompt input (inline text, file path, or use with heredoc/stdin; prefer single quotes for shell metacharacters)"
15540
+ "Prompt input (inline text, file path, or heredoc/stdin; supports !'cmd' and !```cmd``` substitutions, prefer single quotes for shell metacharacters)"
14970
15541
  ).option("-f, --prompt-file <path>", "Read prompt from a file (shell-safe for backticks/$())").option("-w, --cwd <path>", "Working directory").option("-i, --max-iterations <number>", "Override max iterations for this continue run", parseInt).option("-m, --model <name>", "Override model for this continue run").option("-s, --subagent <name>", "Override subagent for this continue run").option("-I, --interactive", "Interactive mode for typing prompts").option("--live", "Run Pi subagent in interactive live TUI mode (pi only)").option("--thinking <level>", "Override thinking level for this continue run").action(async (promptArgs, options, command) => {
14971
15542
  if (promptArgs.length > 0 && options.prompt === void 0) {
14972
15543
  options.prompt = promptArgs.join(" ");
@@ -15041,7 +15612,7 @@ function setupContinueScopeCommand(program) {
15041
15612
  function setupMainCommand(program) {
15042
15613
  program.argument("[prompt_text...]", "Prompt text (positional, alternative to -p)").option(
15043
15614
  "-p, --prompt [text]",
15044
- "Prompt input (inline text, file path, or use with heredoc/stdin; prefer single quotes for shell metacharacters)"
15615
+ "Prompt input (inline text, file path, or heredoc/stdin; supports !'cmd' and !```cmd``` substitutions, prefer single quotes for shell metacharacters)"
15045
15616
  ).option("-f, --prompt-file <path>", "Read prompt from a file (shell-safe for backticks/$())").option("-w, --cwd <path>", "Working directory").option("-i, --max-iterations <number>", "Maximum iterations (-1 for unlimited)", parseInt).option("-I, --interactive", "Interactive mode for typing prompts").option("--live", "Run Pi subagent in interactive live TUI mode (pi only)").option("-ip, --interactive-prompt", "Launch interactive prompt editor").action(async (promptArgs, options, command) => {
15046
15617
  if (promptArgs.length > 0 && options.prompt === void 0) {
15047
15618
  options.prompt = promptArgs.join(" ");
@@ -15215,6 +15786,11 @@ ${chalk21.blue("Examples:")}
15215
15786
 
15216
15787
  ${chalk21.gray("# Shell safety")}
15217
15788
  ${chalk21.gray("Use single quotes (or -f/stdin) when prompts contain backticks or $()")}
15789
+
15790
+ ${chalk21.gray("# Prompt-time substitutions (refreshed each iteration)")}
15791
+ juno-code claude -p "Status: !'git status --short'"
15792
+ juno-code claude -i 3 -p "Recent commits:
15793
+ !\`\`\`git log -n 5 --oneline\`\`\`"
15218
15794
  `
15219
15795
  },
15220
15796
  pi: {
@@ -15403,7 +15979,7 @@ function setupAliases(program) {
15403
15979
  if (!help) continue;
15404
15980
  const cmd = program.command(subagent).description(help.description).argument("[prompt...]", "Prompt text or file path").option(
15405
15981
  "-p, --prompt [text]",
15406
- "Prompt input (inline text, or use with heredoc/stdin; prefer single quotes for shell metacharacters)"
15982
+ "Prompt input (inline text, or heredoc/stdin; supports !'cmd' and !```cmd``` substitutions, prefer single quotes for shell metacharacters)"
15407
15983
  ).option("-f, --prompt-file <path>", "Read prompt from a file (shell-safe for backticks/$())").option("-w, --cwd <path>", "Working directory").option("-i, --max-iterations <number>", "Maximum iterations (-1 for unlimited)", parseInt).option("-m, --model <name>", "Model to use (see model shorthands below)").option("-r, --resume <sessionId>", "Resume a conversation by session ID").option("--continue", "Continue the most recent conversation").option("-I, --interactive", "Interactive mode for typing prompts").option("--live", "Run Pi subagent in interactive live TUI mode (pi only)").addHelpText("after", help.helpText).action(async (promptArgs, options, command) => {
15408
15984
  if (promptArgs.length > 0 && options.prompt === void 0) {
15409
15985
  options.prompt = promptArgs.join(" ");
@@ -15428,9 +16004,24 @@ function setupAliases(program) {
15428
16004
  handleCLIError(error, normalizeVerbose(options.verbose));
15429
16005
  }
15430
16006
  });
15431
- cmd.command("set-default-model <model>").description(`Set default model for the ${subagent} subagent in .juno_task/config.json`).option("-w, --cwd <path>", "Working directory").action(async (model, commandOptions) => {
16007
+ cmd.command("set-default-model <model>").description(`Set default model for the ${subagent} subagent in .juno_task/config.json`).option("-w, --cwd <path>", "Working directory").action(async (model, commandOptions, command) => {
16008
+ const mergedOptions = (() => {
16009
+ const commandWithGlobals = command;
16010
+ if (typeof commandWithGlobals.optsWithGlobals === "function") {
16011
+ return {
16012
+ ...commandWithGlobals.optsWithGlobals(),
16013
+ ...commandOptions
16014
+ };
16015
+ }
16016
+ const parentOptions = command.parent?.opts ? command.parent.opts() : {};
16017
+ return {
16018
+ ...parentOptions,
16019
+ ...commandOptions
16020
+ };
16021
+ })();
15432
16022
  try {
15433
- const workingDirectory = typeof commandOptions.cwd === "string" && commandOptions.cwd.trim().length > 0 ? commandOptions.cwd.trim() : process.cwd();
16023
+ const optionCwd = typeof mergedOptions.cwd === "string" && mergedOptions.cwd.trim().length > 0 ? mergedOptions.cwd.trim() : extractOptionValueFromArgv(process.argv.slice(2), "--cwd", "-w");
16024
+ const workingDirectory = typeof optionCwd === "string" && optionCwd.trim().length > 0 ? optionCwd.trim() : process.cwd();
15434
16025
  const [{ loadConfig: loadConfig2 }, subagentModels, fsExtra2, nodePath] = await Promise.all([
15435
16026
  Promise.resolve().then(() => (init_config(), config_exports)),
15436
16027
  Promise.resolve().then(() => (init_subagent_models(), subagent_models_exports)),
@@ -15471,7 +16062,7 @@ function setupAliases(program) {
15471
16062
  )
15472
16063
  );
15473
16064
  } catch (error) {
15474
- handleCLIError(error, normalizeVerbose(commandOptions.verbose));
16065
+ handleCLIError(error, normalizeVerbose(mergedOptions.verbose));
15475
16066
  }
15476
16067
  });
15477
16068
  cmd.allowUnknownOption(true);
@@ -15668,6 +16259,11 @@ ${chalk21.blue.bold("Examples:")}
15668
16259
  ${chalk21.gray("# Shell safety")}
15669
16260
  ${chalk21.gray("Use single quotes or -f/stdin when prompts include backticks or $()")}
15670
16261
 
16262
+ ${chalk21.gray("# Prompt-time command substitution (per iteration)")}
16263
+ juno-code claude -p "Status: !'git status --short'"
16264
+ juno-code claude -i 3 -p "Recent commits:
16265
+ !\`\`\`git log -n 5 --oneline\`\`\`"
16266
+
15671
16267
  ${chalk21.gray("# Interactive project setup")}
15672
16268
  juno-code init --interactive
15673
16269