juno-code 1.0.33 → 1.0.35

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
@@ -2,8 +2,8 @@
2
2
  import fs2 from 'fs-extra';
3
3
  import * as path3 from 'path';
4
4
  import path3__default, { dirname, join } from 'path';
5
- import * as os3 from 'os';
6
- import os3__default, { homedir, EOL } from 'os';
5
+ import * as os4 from 'os';
6
+ import os4__default, { homedir, EOL } from 'os';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { createRequire } from 'module';
9
9
  import semver from 'semver';
@@ -3295,13 +3295,9 @@ var init_advanced_logger = __esm({
3295
3295
  /**
3296
3296
  * Output formatted log
3297
3297
  */
3298
- output(formatted, level) {
3298
+ output(formatted, _level) {
3299
3299
  if (this.options.output === "console" || this.options.output === "both") {
3300
- if (level >= 3 /* WARN */) {
3301
- console.error(formatted);
3302
- } else {
3303
- console.log(formatted);
3304
- }
3300
+ console.error(formatted);
3305
3301
  }
3306
3302
  if (this.options.output === "file" || this.options.output === "both") {
3307
3303
  this.writeToFile(formatted);
@@ -5997,7 +5993,7 @@ var init_client = __esm({
5997
5993
  `${serverName}`,
5998
5994
  // Assume it's in PATH
5999
5995
  `/usr/local/bin/${serverName}`,
6000
- path3__default.resolve(os3__default.homedir(), `.local/bin/${serverName}`),
5996
+ path3__default.resolve(os4__default.homedir(), `.local/bin/${serverName}`),
6001
5997
  path3__default.resolve(this.options.workingDirectory || process.cwd(), `${serverName}`)
6002
5998
  ];
6003
5999
  for (const serverPath of possiblePaths) {
@@ -7006,7 +7002,7 @@ var init_shell_backend = __esm({
7006
7002
  });
7007
7003
  try {
7008
7004
  const scriptPath = await this.findScriptForSubagent(subagentType);
7009
- const result = await this.executeScript(scriptPath, request, toolId);
7005
+ const result = await this.executeScript(scriptPath, request, toolId, subagentType);
7010
7006
  const duration = Date.now() - startTime;
7011
7007
  await this.emitProgressEvent({
7012
7008
  sessionId: request.metadata?.sessionId || "unknown",
@@ -7023,8 +7019,9 @@ var init_shell_backend = __esm({
7023
7019
  phase: "completion"
7024
7020
  }
7025
7021
  });
7022
+ const structuredResult = this.buildStructuredOutput(subagentType, result);
7026
7023
  return {
7027
- content: result.output,
7024
+ content: structuredResult.content,
7028
7025
  status: result.success ? "completed" : "failed",
7029
7026
  startTime: new Date(startTime),
7030
7027
  endTime: /* @__PURE__ */ new Date(),
@@ -7032,6 +7029,7 @@ var init_shell_backend = __esm({
7032
7029
  error: result.error ? { type: "shell_execution", message: result.error, timestamp: /* @__PURE__ */ new Date() } : void 0,
7033
7030
  progressEvents: [],
7034
7031
  // Progress events are handled via callbacks
7032
+ ...structuredResult.metadata ? { metadata: structuredResult.metadata } : void 0,
7035
7033
  request
7036
7034
  };
7037
7035
  } catch (error) {
@@ -7197,8 +7195,8 @@ var init_shell_backend = __esm({
7197
7195
  /**
7198
7196
  * Execute a shell script
7199
7197
  */
7200
- async executeScript(scriptPath, request, toolId) {
7201
- return new Promise((resolve9, reject) => {
7198
+ async executeScript(scriptPath, request, toolId, subagentType) {
7199
+ return new Promise(async (resolve9, reject) => {
7202
7200
  const startTime = Date.now();
7203
7201
  const isPython = scriptPath.endsWith(".py");
7204
7202
  const env2 = {
@@ -7211,6 +7209,19 @@ var init_shell_backend = __esm({
7211
7209
  JUNO_ITERATION: String(request.arguments?.iteration || 1),
7212
7210
  JUNO_TOOL_ID: toolId
7213
7211
  };
7212
+ let captureDir = null;
7213
+ let capturePath = null;
7214
+ if (subagentType === "claude") {
7215
+ try {
7216
+ captureDir = await promises.mkdtemp(path3.join(os4__default.tmpdir(), "juno-shell-"));
7217
+ capturePath = path3.join(captureDir, `subagent_${toolId}.json`);
7218
+ env2.JUNO_SUBAGENT_CAPTURE_PATH = capturePath;
7219
+ } catch (error) {
7220
+ if (this.config?.debug) {
7221
+ engineLogger.warn(`Failed to prepare subagent capture path: ${error instanceof Error ? error.message : String(error)}`);
7222
+ }
7223
+ }
7224
+ }
7214
7225
  const command = isPython ? "python3" : "bash";
7215
7226
  const args = [scriptPath];
7216
7227
  if (isPython && request.arguments?.instruction) {
@@ -7285,20 +7296,46 @@ var init_shell_backend = __esm({
7285
7296
  }
7286
7297
  });
7287
7298
  child.on("close", (exitCode) => {
7288
- if (isProcessKilled) return;
7289
- const duration = Date.now() - startTime;
7290
- const success = exitCode === 0;
7291
- if (this.config.debug) {
7292
- engineLogger.debug(`Script execution completed with exit code: ${exitCode}, duration: ${duration}ms`);
7293
- engineLogger.debug(`Stdout length: ${stdout2.length}, Stderr length: ${stderr.length}`);
7294
- }
7295
- resolve9({
7296
- success,
7297
- output: stdout2,
7298
- error: stderr || void 0,
7299
- exitCode: exitCode || 0,
7300
- duration
7301
- });
7299
+ void (async () => {
7300
+ if (isProcessKilled) return;
7301
+ const duration = Date.now() - startTime;
7302
+ const success = exitCode === 0;
7303
+ let subAgentResponse;
7304
+ if (capturePath) {
7305
+ try {
7306
+ const captured = await promises.readFile(capturePath, "utf-8");
7307
+ if (captured.trim()) {
7308
+ subAgentResponse = JSON.parse(captured);
7309
+ }
7310
+ } catch (error) {
7311
+ if (this.config?.debug) {
7312
+ engineLogger.warn(`Failed to read subagent capture: ${error instanceof Error ? error.message : String(error)}`);
7313
+ }
7314
+ } finally {
7315
+ if (captureDir) {
7316
+ try {
7317
+ await promises.rm(captureDir, { recursive: true, force: true });
7318
+ } catch (cleanupError) {
7319
+ if (this.config?.debug) {
7320
+ engineLogger.warn(`Failed to clean capture directory: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
7321
+ }
7322
+ }
7323
+ }
7324
+ }
7325
+ }
7326
+ if (this.config.debug) {
7327
+ engineLogger.debug(`Script execution completed with exit code: ${exitCode}, duration: ${duration}ms`);
7328
+ engineLogger.debug(`Stdout length: ${stdout2.length}, Stderr length: ${stderr.length}`);
7329
+ }
7330
+ resolve9({
7331
+ success,
7332
+ output: stdout2,
7333
+ error: stderr || void 0,
7334
+ exitCode: exitCode || 0,
7335
+ duration,
7336
+ ...subAgentResponse ? { subAgentResponse } : void 0
7337
+ });
7338
+ })();
7302
7339
  });
7303
7340
  child.on("error", (error) => {
7304
7341
  if (isProcessKilled) return;
@@ -7330,6 +7367,66 @@ var init_shell_backend = __esm({
7330
7367
  });
7331
7368
  });
7332
7369
  }
7370
+ /**
7371
+ * Build a structured, JSON-parsable result payload for programmatic capture while
7372
+ * preserving the shell backend's existing on-screen streaming behavior.
7373
+ */
7374
+ buildStructuredOutput(subagentType, result) {
7375
+ if (subagentType === "claude") {
7376
+ const claudeEvent = result.subAgentResponse ?? this.extractLastJsonEvent(result.output);
7377
+ const isError = claudeEvent?.is_error ?? claudeEvent?.subtype === "error" ?? !result.success;
7378
+ const structuredPayload = {
7379
+ type: "result",
7380
+ subtype: claudeEvent?.subtype || (isError ? "error" : "success"),
7381
+ is_error: isError,
7382
+ result: claudeEvent?.result ?? claudeEvent?.error ?? claudeEvent?.content ?? result.output,
7383
+ error: claudeEvent?.error,
7384
+ stderr: result.error,
7385
+ datetime: claudeEvent?.datetime,
7386
+ counter: claudeEvent?.counter,
7387
+ session_id: claudeEvent?.session_id,
7388
+ num_turns: claudeEvent?.num_turns,
7389
+ duration_ms: claudeEvent?.duration_ms ?? result.duration,
7390
+ exit_code: result.exitCode,
7391
+ total_cost_usd: claudeEvent?.total_cost_usd,
7392
+ usage: claudeEvent?.usage,
7393
+ modelUsage: claudeEvent?.modelUsage || claudeEvent?.model_usage || {},
7394
+ permission_denials: claudeEvent?.permission_denials || [],
7395
+ uuid: claudeEvent?.uuid,
7396
+ sub_agent_response: claudeEvent
7397
+ };
7398
+ const metadata = {
7399
+ ...claudeEvent ? { subAgentResponse: claudeEvent } : void 0,
7400
+ structuredOutput: true,
7401
+ contentType: "application/json",
7402
+ rawOutput: result.output
7403
+ };
7404
+ return {
7405
+ content: JSON.stringify(structuredPayload),
7406
+ metadata
7407
+ };
7408
+ }
7409
+ return { content: result.output, metadata: result.metadata };
7410
+ }
7411
+ /**
7412
+ * Extract the last valid JSON object from a script's stdout to use as a structured payload fallback.
7413
+ */
7414
+ extractLastJsonEvent(output) {
7415
+ if (!output) {
7416
+ return null;
7417
+ }
7418
+ const lines = output.split("\n").map((line) => line.trim()).filter(Boolean);
7419
+ for (let i = lines.length - 1; i >= 0; i--) {
7420
+ try {
7421
+ const parsed = JSON.parse(lines[i]);
7422
+ if (parsed && typeof parsed === "object") {
7423
+ return parsed;
7424
+ }
7425
+ } catch {
7426
+ }
7427
+ }
7428
+ return null;
7429
+ }
7333
7430
  /**
7334
7431
  * Parse streaming events from script output
7335
7432
  * Handles both JSON format (Claude) and TEXT format (Codex)
@@ -7337,7 +7434,7 @@ var init_shell_backend = __esm({
7337
7434
  * Strategy:
7338
7435
  * 1. Try to parse each line as JSON first (for Claude)
7339
7436
  * 2. If JSON parsing fails, treat as TEXT streaming (for Codex and other text-based subagents)
7340
- * 3. Emit all non-empty lines as progress events for real-time display
7437
+ * 3. Emit all text lines (including whitespace-only) as progress events for real-time display
7341
7438
  */
7342
7439
  parseAndEmitStreamingEvents(data, sessionId) {
7343
7440
  if (!this.jsonBuffer) {
@@ -7347,14 +7444,35 @@ var init_shell_backend = __esm({
7347
7444
  const lines = this.jsonBuffer.split("\n");
7348
7445
  this.jsonBuffer = lines.pop() || "";
7349
7446
  for (const line of lines) {
7350
- const trimmedLine = line.trim();
7351
- if (!trimmedLine) continue;
7447
+ const rawLine = line.endsWith("\r") ? line.slice(0, -1) : line;
7448
+ if (!rawLine) continue;
7449
+ const hasNonWhitespace = rawLine.trim().length > 0;
7450
+ if (!hasNonWhitespace) {
7451
+ this.emitProgressEvent({
7452
+ sessionId,
7453
+ timestamp: /* @__PURE__ */ new Date(),
7454
+ backend: "shell",
7455
+ count: ++this.eventCounter,
7456
+ type: "thinking",
7457
+ content: rawLine,
7458
+ metadata: {
7459
+ format: "text",
7460
+ raw: true
7461
+ }
7462
+ }).catch((error) => {
7463
+ if (this.config?.debug) {
7464
+ engineLogger.warn(`Failed to emit whitespace-only streaming event: ${error instanceof Error ? error.message : String(error)}`);
7465
+ }
7466
+ });
7467
+ continue;
7468
+ }
7469
+ const trimmedLine = rawLine.trim();
7352
7470
  let isJsonParsed = false;
7353
7471
  try {
7354
7472
  const jsonEvent = JSON.parse(trimmedLine);
7355
7473
  let progressEvent;
7356
7474
  if (this.isClaudeCliEvent(jsonEvent)) {
7357
- progressEvent = this.convertClaudeEventToProgress(jsonEvent, sessionId, trimmedLine);
7475
+ progressEvent = this.convertClaudeEventToProgress(jsonEvent, sessionId, rawLine);
7358
7476
  isJsonParsed = true;
7359
7477
  } else if (this.isGenericStreamingEvent(jsonEvent)) {
7360
7478
  progressEvent = {
@@ -7389,7 +7507,7 @@ var init_shell_backend = __esm({
7389
7507
  backend: "shell",
7390
7508
  count: ++this.eventCounter,
7391
7509
  type: "thinking",
7392
- content: trimmedLine,
7510
+ content: rawLine,
7393
7511
  metadata: {
7394
7512
  format: "text",
7395
7513
  raw: true
@@ -13069,7 +13187,11 @@ var init_main = __esm({
13069
13187
  \u274C Execution failed (${elapsed})`));
13070
13188
  }
13071
13189
  const lastIteration = result.iterations[result.iterations.length - 1];
13072
- if (lastIteration && lastIteration.toolResult.content && !this.hasStreamedJsonOutput) {
13190
+ const structuredOutput = lastIteration?.toolResult.metadata?.structuredOutput === true;
13191
+ const shouldPrintResult = Boolean(
13192
+ lastIteration && lastIteration.toolResult.content && (!this.hasStreamedJsonOutput || structuredOutput)
13193
+ );
13194
+ if (shouldPrintResult) {
13073
13195
  console.error(chalk15.blue("\n\u{1F4C4} Result:"));
13074
13196
  console.log(lastIteration.toolResult.content);
13075
13197
  }
@@ -13407,7 +13529,7 @@ async function validateJSONConfigs(baseDir = process.cwd(), backendType = "mcp")
13407
13529
  }
13408
13530
  function displayValidationResults(result) {
13409
13531
  if (result.isValid && result.warnings.length === 0) {
13410
- console.log(chalk15.green("\u2705 All configuration files are valid\n"));
13532
+ console.error(chalk15.green("\u2705 All configuration files are valid\n"));
13411
13533
  return;
13412
13534
  }
13413
13535
  if (result.errors.length > 0) {
@@ -21833,6 +21955,11 @@ var QUICK_REFERENCE = [
21833
21955
  description: "Shell completion setup",
21834
21956
  usage: "juno-code completion <install|uninstall>"
21835
21957
  },
21958
+ {
21959
+ name: "services",
21960
+ description: "Manage service scripts (use --force to refresh codex.py/claude.py)",
21961
+ usage: "juno-code services install --force"
21962
+ },
21836
21963
  {
21837
21964
  name: "help",
21838
21965
  description: "Show help information",
@@ -22401,11 +22528,12 @@ init_service_installer();
22401
22528
  function createServicesCommand() {
22402
22529
  const servicesCmd = new Command("services").description("Manage juno-code service scripts").addHelpText("after", `
22403
22530
  Examples:
22404
- $ juno-code services install Install service scripts to ~/.juno_code/services/
22405
- $ juno-code services list List installed service scripts
22406
- $ juno-code services status Check installation status
22407
- $ juno-code services uninstall Remove all service scripts
22408
- $ juno-code services path Show services directory path
22531
+ $ juno-code services install Install service scripts to ~/.juno_code/services/
22532
+ $ juno-code services install --force Reinstall/refresh service scripts (codex.py/claude.py)
22533
+ $ juno-code services list List installed service scripts
22534
+ $ juno-code services status Check installation status
22535
+ $ juno-code services uninstall Remove all service scripts
22536
+ $ juno-code services path Show services directory path
22409
22537
 
22410
22538
  Service scripts are Python/shell scripts that provide additional functionality
22411
22539
  and can be customized by users. They are installed to ~/.juno_code/services/
@@ -22579,7 +22707,7 @@ var ShellDetector = class _ShellDetector {
22579
22707
  * Get shell configuration file path
22580
22708
  */
22581
22709
  getConfigPath(shell) {
22582
- const homeDir = os3.homedir();
22710
+ const homeDir = os4.homedir();
22583
22711
  switch (shell) {
22584
22712
  case "bash":
22585
22713
  if (process.platform === "darwin") {
@@ -22603,7 +22731,7 @@ var ShellDetector = class _ShellDetector {
22603
22731
  * Get shell completion script installation path
22604
22732
  */
22605
22733
  getCompletionPath(shell) {
22606
- const homeDir = os3.homedir();
22734
+ const homeDir = os4.homedir();
22607
22735
  switch (shell) {
22608
22736
  case "bash":
22609
22737
  if (process.platform === "darwin") {
@@ -22881,7 +23009,7 @@ var ContextAwareCompletion = class {
22881
23009
  */
22882
23010
  expandPath(inputPath) {
22883
23011
  if (inputPath.startsWith("~/")) {
22884
- return path3.join(os3.homedir(), inputPath.slice(2));
23012
+ return path3.join(os4.homedir(), inputPath.slice(2));
22885
23013
  }
22886
23014
  if (inputPath.startsWith("./") || inputPath.startsWith("../")) {
22887
23015
  return path3.resolve(inputPath);