juno-code 1.0.32 → 1.0.34

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
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import fs3 from 'fs-extra';
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';
@@ -315,12 +315,12 @@ var init_service_installer = __esm({
315
315
  const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
316
316
  const require3 = createRequire(import.meta.url);
317
317
  let packageJsonPath = path3.join(__dirname2, "..", "..", "package.json");
318
- if (fs3.existsSync(packageJsonPath)) {
318
+ if (fs2.existsSync(packageJsonPath)) {
319
319
  const packageJson2 = require3(packageJsonPath);
320
320
  return packageJson2.version;
321
321
  }
322
322
  packageJsonPath = path3.join(__dirname2, "..", "..", "..", "package.json");
323
- if (fs3.existsSync(packageJsonPath)) {
323
+ if (fs2.existsSync(packageJsonPath)) {
324
324
  const packageJson2 = require3(packageJsonPath);
325
325
  return packageJson2.version;
326
326
  }
@@ -334,11 +334,11 @@ var init_service_installer = __esm({
334
334
  */
335
335
  static async getInstalledVersion() {
336
336
  try {
337
- const exists = await fs3.pathExists(this.VERSION_FILE);
337
+ const exists = await fs2.pathExists(this.VERSION_FILE);
338
338
  if (!exists) {
339
339
  return null;
340
340
  }
341
- const version3 = await fs3.readFile(this.VERSION_FILE, "utf-8");
341
+ const version3 = await fs2.readFile(this.VERSION_FILE, "utf-8");
342
342
  return version3.trim();
343
343
  } catch {
344
344
  return null;
@@ -349,7 +349,7 @@ var init_service_installer = __esm({
349
349
  */
350
350
  static async saveVersion() {
351
351
  const version3 = this.getPackageVersion();
352
- await fs3.writeFile(this.VERSION_FILE, version3, "utf-8");
352
+ await fs2.writeFile(this.VERSION_FILE, version3, "utf-8");
353
353
  }
354
354
  /**
355
355
  * Check if services need to be updated based on version
@@ -361,7 +361,7 @@ var init_service_installer = __esm({
361
361
  if (!installedVersion) {
362
362
  return true;
363
363
  }
364
- const exists = await fs3.pathExists(this.SERVICES_DIR);
364
+ const exists = await fs2.pathExists(this.SERVICES_DIR);
365
365
  if (!exists) {
366
366
  return true;
367
367
  }
@@ -369,8 +369,10 @@ var init_service_installer = __esm({
369
369
  return true;
370
370
  }
371
371
  if (semver.eq(packageVersion, installedVersion)) {
372
- const codexExists = await fs3.pathExists(path3.join(this.SERVICES_DIR, "codex.py"));
373
- const claudeExists = await fs3.pathExists(path3.join(this.SERVICES_DIR, "claude.py"));
372
+ const installedCodex = path3.join(this.SERVICES_DIR, "codex.py");
373
+ const installedClaude = path3.join(this.SERVICES_DIR, "claude.py");
374
+ const codexExists = await fs2.pathExists(installedCodex);
375
+ const claudeExists = await fs2.pathExists(installedClaude);
374
376
  if (!codexExists || !claudeExists) {
375
377
  return true;
376
378
  }
@@ -378,15 +380,32 @@ var init_service_installer = __esm({
378
380
  const packageServicesDir = this.getPackageServicesDir();
379
381
  const packageCodex = path3.join(packageServicesDir, "codex.py");
380
382
  const packageClaude = path3.join(packageServicesDir, "claude.py");
381
- const packageCodexExists = await fs3.pathExists(packageCodex);
382
- const packageClaudeExists = await fs3.pathExists(packageClaude);
383
- if (packageCodexExists || packageClaudeExists) {
384
- const isDevelopment2 = packageServicesDir.includes("/src/");
385
- if (isDevelopment2) {
383
+ const packageCodexExists = await fs2.pathExists(packageCodex);
384
+ const packageClaudeExists = await fs2.pathExists(packageClaude);
385
+ if (packageCodexExists) {
386
+ const [pkg, inst] = await Promise.all([
387
+ fs2.readFile(packageCodex, "utf-8"),
388
+ fs2.readFile(installedCodex, "utf-8")
389
+ ]);
390
+ if (pkg !== inst) {
386
391
  return true;
387
392
  }
388
393
  }
394
+ if (packageClaudeExists) {
395
+ const [pkg, inst] = await Promise.all([
396
+ fs2.readFile(packageClaude, "utf-8"),
397
+ fs2.readFile(installedClaude, "utf-8")
398
+ ]);
399
+ if (pkg !== inst) {
400
+ return true;
401
+ }
402
+ }
403
+ const isDevelopment2 = packageServicesDir.includes("/src/");
404
+ if (isDevelopment2) {
405
+ return true;
406
+ }
389
407
  } catch {
408
+ return true;
390
409
  }
391
410
  }
392
411
  return false;
@@ -400,11 +419,11 @@ var init_service_installer = __esm({
400
419
  static getPackageServicesDir() {
401
420
  const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
402
421
  let servicesPath = path3.join(__dirname2, "..", "..", "templates", "services");
403
- if (fs3.existsSync(servicesPath)) {
422
+ if (fs2.existsSync(servicesPath)) {
404
423
  return servicesPath;
405
424
  }
406
425
  servicesPath = path3.join(__dirname2, "..", "templates", "services");
407
- if (fs3.existsSync(servicesPath)) {
426
+ if (fs2.existsSync(servicesPath)) {
408
427
  return servicesPath;
409
428
  }
410
429
  throw new Error("Could not find services directory in package");
@@ -415,18 +434,18 @@ var init_service_installer = __esm({
415
434
  */
416
435
  static async install(silent = false) {
417
436
  try {
418
- await fs3.ensureDir(this.SERVICES_DIR);
437
+ await fs2.ensureDir(this.SERVICES_DIR);
419
438
  const packageServicesDir = this.getPackageServicesDir();
420
- await fs3.copy(packageServicesDir, this.SERVICES_DIR, {
439
+ await fs2.copy(packageServicesDir, this.SERVICES_DIR, {
421
440
  overwrite: true,
422
441
  preserveTimestamps: true
423
442
  });
424
- const files = await fs3.readdir(this.SERVICES_DIR);
443
+ const files = await fs2.readdir(this.SERVICES_DIR);
425
444
  for (const file of files) {
426
445
  const filePath = path3.join(this.SERVICES_DIR, file);
427
- const stat = await fs3.stat(filePath);
446
+ const stat = await fs2.stat(filePath);
428
447
  if (stat.isFile() && (file.endsWith(".py") || file.endsWith(".sh"))) {
429
- await fs3.chmod(filePath, 493);
448
+ await fs2.chmod(filePath, 493);
430
449
  }
431
450
  }
432
451
  await this.saveVersion();
@@ -473,11 +492,11 @@ var init_service_installer = __esm({
473
492
  */
474
493
  static async isInstalled() {
475
494
  try {
476
- const exists = await fs3.pathExists(this.SERVICES_DIR);
495
+ const exists = await fs2.pathExists(this.SERVICES_DIR);
477
496
  if (!exists) {
478
497
  return false;
479
498
  }
480
- const files = await fs3.readdir(this.SERVICES_DIR);
499
+ const files = await fs2.readdir(this.SERVICES_DIR);
481
500
  return files.length > 0;
482
501
  } catch {
483
502
  return false;
@@ -500,11 +519,11 @@ var init_service_installer = __esm({
500
519
  */
501
520
  static async listServices() {
502
521
  try {
503
- const exists = await fs3.pathExists(this.SERVICES_DIR);
522
+ const exists = await fs2.pathExists(this.SERVICES_DIR);
504
523
  if (!exists) {
505
524
  return [];
506
525
  }
507
- const files = await fs3.readdir(this.SERVICES_DIR);
526
+ const files = await fs2.readdir(this.SERVICES_DIR);
508
527
  return files.filter((file) => file.endsWith(".py") || file.endsWith(".sh"));
509
528
  } catch {
510
529
  return [];
@@ -515,9 +534,9 @@ var init_service_installer = __esm({
515
534
  */
516
535
  static async uninstall() {
517
536
  try {
518
- const exists = await fs3.pathExists(this.SERVICES_DIR);
537
+ const exists = await fs2.pathExists(this.SERVICES_DIR);
519
538
  if (exists) {
520
- await fs3.remove(this.SERVICES_DIR);
539
+ await fs2.remove(this.SERVICES_DIR);
521
540
  console.log("\u2713 Services uninstalled");
522
541
  }
523
542
  } catch (error) {
@@ -1093,17 +1112,17 @@ async function ensureHooksConfig(baseDir) {
1093
1112
  try {
1094
1113
  const configDir = path3.join(baseDir, ".juno_task");
1095
1114
  const configPath = path3.join(configDir, "config.json");
1096
- await fs3.ensureDir(configDir);
1097
- const configExists = await fs3.pathExists(configPath);
1115
+ await fs2.ensureDir(configDir);
1116
+ const configExists = await fs2.pathExists(configPath);
1098
1117
  const allHookTypes = getDefaultHooks();
1099
1118
  if (!configExists) {
1100
1119
  const defaultConfig = {
1101
1120
  ...DEFAULT_CONFIG,
1102
1121
  hooks: allHookTypes
1103
1122
  };
1104
- await fs3.writeJson(configPath, defaultConfig, { spaces: 2 });
1123
+ await fs2.writeJson(configPath, defaultConfig, { spaces: 2 });
1105
1124
  } else {
1106
- const existingConfig = await fs3.readJson(configPath);
1125
+ const existingConfig = await fs2.readJson(configPath);
1107
1126
  let needsUpdate = false;
1108
1127
  if (!existingConfig.hooks) {
1109
1128
  existingConfig.hooks = allHookTypes;
@@ -1121,7 +1140,7 @@ async function ensureHooksConfig(baseDir) {
1121
1140
  needsUpdate = true;
1122
1141
  }
1123
1142
  if (needsUpdate) {
1124
- await fs3.writeJson(configPath, existingConfig, { spaces: 2 });
1143
+ await fs2.writeJson(configPath, existingConfig, { spaces: 2 });
1125
1144
  }
1126
1145
  }
1127
1146
  } catch (error) {
@@ -3276,13 +3295,9 @@ var init_advanced_logger = __esm({
3276
3295
  /**
3277
3296
  * Output formatted log
3278
3297
  */
3279
- output(formatted, level) {
3298
+ output(formatted, _level) {
3280
3299
  if (this.options.output === "console" || this.options.output === "both") {
3281
- if (level >= 3 /* WARN */) {
3282
- console.error(formatted);
3283
- } else {
3284
- console.log(formatted);
3285
- }
3300
+ console.error(formatted);
3286
3301
  }
3287
3302
  if (this.options.output === "file" || this.options.output === "both") {
3288
3303
  this.writeToFile(formatted);
@@ -4839,7 +4854,7 @@ var init_config2 = __esm({
4839
4854
  return cached;
4840
4855
  }
4841
4856
  try {
4842
- const configContent = await fs3.readFile(configPath, "utf-8");
4857
+ const configContent = await fs2.readFile(configPath, "utf-8");
4843
4858
  const config = JSON.parse(configContent);
4844
4859
  this.validateConfig(config);
4845
4860
  const resolvedConfig = this.resolveConfigPaths(config, path3.dirname(configPath));
@@ -4861,7 +4876,7 @@ var init_config2 = __esm({
4861
4876
  const rootDir = path3.parse(currentDir).root;
4862
4877
  while (currentDir !== rootDir) {
4863
4878
  const configPath = path3.join(currentDir, ".juno_task", "mcp.json");
4864
- if (await fs3.pathExists(configPath)) {
4879
+ if (await fs2.pathExists(configPath)) {
4865
4880
  return configPath;
4866
4881
  }
4867
4882
  currentDir = path3.dirname(currentDir);
@@ -5047,7 +5062,7 @@ var init_logger = __esm({
5047
5062
  * Matches Python's generate_log_file function
5048
5063
  */
5049
5064
  async initialize(subagent = "mcp") {
5050
- await fs3.ensureDir(this.logDirectory);
5065
+ await fs2.ensureDir(this.logDirectory);
5051
5066
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T");
5052
5067
  const dateStr = timestamp[0];
5053
5068
  const timeStr = timestamp[1].split("-")[0].substring(0, 6);
@@ -5056,7 +5071,7 @@ var init_logger = __esm({
5056
5071
  this.logFilePath = path3__default.join(this.logDirectory, logFileName);
5057
5072
  const header = `# Juno-Task TypeScript Log - Started ${(/* @__PURE__ */ new Date()).toISOString()}
5058
5073
  `;
5059
- await fs3.writeFile(this.logFilePath, header);
5074
+ await fs2.writeFile(this.logFilePath, header);
5060
5075
  }
5061
5076
  /**
5062
5077
  * Format log message matching Python version format
@@ -5074,7 +5089,7 @@ var init_logger = __esm({
5074
5089
  if (!this.logFilePath) {
5075
5090
  throw new Error("Logger not initialized. Call initialize() first.");
5076
5091
  }
5077
- await fs3.appendFile(this.logFilePath, message);
5092
+ await fs2.appendFile(this.logFilePath, message);
5078
5093
  }
5079
5094
  /**
5080
5095
  * Log message at specified level
@@ -5978,7 +5993,7 @@ var init_client = __esm({
5978
5993
  `${serverName}`,
5979
5994
  // Assume it's in PATH
5980
5995
  `/usr/local/bin/${serverName}`,
5981
- path3__default.resolve(os3__default.homedir(), `.local/bin/${serverName}`),
5996
+ path3__default.resolve(os4__default.homedir(), `.local/bin/${serverName}`),
5982
5997
  path3__default.resolve(this.options.workingDirectory || process.cwd(), `${serverName}`)
5983
5998
  ];
5984
5999
  for (const serverPath of possiblePaths) {
@@ -6987,7 +7002,7 @@ var init_shell_backend = __esm({
6987
7002
  });
6988
7003
  try {
6989
7004
  const scriptPath = await this.findScriptForSubagent(subagentType);
6990
- const result = await this.executeScript(scriptPath, request, toolId);
7005
+ const result = await this.executeScript(scriptPath, request, toolId, subagentType);
6991
7006
  const duration = Date.now() - startTime;
6992
7007
  await this.emitProgressEvent({
6993
7008
  sessionId: request.metadata?.sessionId || "unknown",
@@ -7004,8 +7019,9 @@ var init_shell_backend = __esm({
7004
7019
  phase: "completion"
7005
7020
  }
7006
7021
  });
7022
+ const structuredResult = this.buildStructuredOutput(subagentType, result);
7007
7023
  return {
7008
- content: result.output,
7024
+ content: structuredResult.content,
7009
7025
  status: result.success ? "completed" : "failed",
7010
7026
  startTime: new Date(startTime),
7011
7027
  endTime: /* @__PURE__ */ new Date(),
@@ -7013,6 +7029,7 @@ var init_shell_backend = __esm({
7013
7029
  error: result.error ? { type: "shell_execution", message: result.error, timestamp: /* @__PURE__ */ new Date() } : void 0,
7014
7030
  progressEvents: [],
7015
7031
  // Progress events are handled via callbacks
7032
+ ...structuredResult.metadata ? { metadata: structuredResult.metadata } : void 0,
7016
7033
  request
7017
7034
  };
7018
7035
  } catch (error) {
@@ -7083,7 +7100,7 @@ var init_shell_backend = __esm({
7083
7100
  const timestamp = now.getFullYear().toString() + (now.getMonth() + 1).toString().padStart(2, "0") + now.getDate().toString().padStart(2, "0") + "_" + now.getHours().toString().padStart(2, "0") + now.getMinutes().toString().padStart(2, "0") + now.getSeconds().toString().padStart(2, "0");
7084
7101
  const logDir = path3.join(this.config.workingDirectory, ".juno_task", "logs");
7085
7102
  try {
7086
- await fs3.ensureDir(logDir);
7103
+ await fs2.ensureDir(logDir);
7087
7104
  } catch (error) {
7088
7105
  if (this.config?.debug) {
7089
7106
  engineLogger.warn(`Failed to create log directory: ${error instanceof Error ? error.message : String(error)}`);
@@ -7108,7 +7125,7 @@ var init_shell_backend = __esm({
7108
7125
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
7109
7126
  const logEntry = `[${timestamp}] ${message}
7110
7127
  `;
7111
- await fs3.appendFile(this.logFilePath, logEntry, "utf-8");
7128
+ await fs2.appendFile(this.logFilePath, logEntry, "utf-8");
7112
7129
  } catch (error) {
7113
7130
  if (this.config?.debug) {
7114
7131
  engineLogger.warn(`Failed to write to log file: ${error instanceof Error ? error.message : String(error)}`);
@@ -7178,8 +7195,8 @@ var init_shell_backend = __esm({
7178
7195
  /**
7179
7196
  * Execute a shell script
7180
7197
  */
7181
- async executeScript(scriptPath, request, toolId) {
7182
- return new Promise((resolve9, reject) => {
7198
+ async executeScript(scriptPath, request, toolId, subagentType) {
7199
+ return new Promise(async (resolve9, reject) => {
7183
7200
  const startTime = Date.now();
7184
7201
  const isPython = scriptPath.endsWith(".py");
7185
7202
  const env2 = {
@@ -7192,6 +7209,19 @@ var init_shell_backend = __esm({
7192
7209
  JUNO_ITERATION: String(request.arguments?.iteration || 1),
7193
7210
  JUNO_TOOL_ID: toolId
7194
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
+ }
7195
7225
  const command = isPython ? "python3" : "bash";
7196
7226
  const args = [scriptPath];
7197
7227
  if (isPython && request.arguments?.instruction) {
@@ -7266,20 +7296,46 @@ var init_shell_backend = __esm({
7266
7296
  }
7267
7297
  });
7268
7298
  child.on("close", (exitCode) => {
7269
- if (isProcessKilled) return;
7270
- const duration = Date.now() - startTime;
7271
- const success = exitCode === 0;
7272
- if (this.config.debug) {
7273
- engineLogger.debug(`Script execution completed with exit code: ${exitCode}, duration: ${duration}ms`);
7274
- engineLogger.debug(`Stdout length: ${stdout2.length}, Stderr length: ${stderr.length}`);
7275
- }
7276
- resolve9({
7277
- success,
7278
- output: stdout2,
7279
- error: stderr || void 0,
7280
- exitCode: exitCode || 0,
7281
- duration
7282
- });
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
+ })();
7283
7339
  });
7284
7340
  child.on("error", (error) => {
7285
7341
  if (isProcessKilled) return;
@@ -7311,6 +7367,66 @@ var init_shell_backend = __esm({
7311
7367
  });
7312
7368
  });
7313
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
+ }
7314
7430
  /**
7315
7431
  * Parse streaming events from script output
7316
7432
  * Handles both JSON format (Claude) and TEXT format (Codex)
@@ -12807,7 +12923,7 @@ var init_main = __esm({
12807
12923
  return await this.collectInteractivePrompt();
12808
12924
  } else {
12809
12925
  const defaultPromptPath = path3.join(process.cwd(), ".juno_task", "prompt.md");
12810
- if (await fs3.pathExists(defaultPromptPath)) {
12926
+ if (await fs2.pathExists(defaultPromptPath)) {
12811
12927
  console.error(chalk15.blue(`\u{1F4C4} Using default prompt: ${chalk15.cyan(".juno_task/prompt.md")}`));
12812
12928
  return await this.loadPromptFromFile(defaultPromptPath);
12813
12929
  } else {
@@ -12835,7 +12951,7 @@ var init_main = __esm({
12835
12951
  }
12836
12952
  try {
12837
12953
  const resolvedPath = path3.resolve(prompt);
12838
- return await fs3.pathExists(resolvedPath);
12954
+ return await fs2.pathExists(resolvedPath);
12839
12955
  } catch {
12840
12956
  return false;
12841
12957
  }
@@ -12843,7 +12959,7 @@ var init_main = __esm({
12843
12959
  async loadPromptFromFile(filePath) {
12844
12960
  try {
12845
12961
  const resolvedPath = path3.resolve(filePath);
12846
- const content = await fs3.readFile(resolvedPath, "utf-8");
12962
+ const content = await fs2.readFile(resolvedPath, "utf-8");
12847
12963
  if (!content.trim()) {
12848
12964
  throw new FileSystemError(
12849
12965
  "Prompt file is empty",
@@ -13050,7 +13166,11 @@ var init_main = __esm({
13050
13166
  \u274C Execution failed (${elapsed})`));
13051
13167
  }
13052
13168
  const lastIteration = result.iterations[result.iterations.length - 1];
13053
- if (lastIteration && lastIteration.toolResult.content && !this.hasStreamedJsonOutput) {
13169
+ const structuredOutput = lastIteration?.toolResult.metadata?.structuredOutput === true;
13170
+ const shouldPrintResult = Boolean(
13171
+ lastIteration && lastIteration.toolResult.content && (!this.hasStreamedJsonOutput || structuredOutput)
13172
+ );
13173
+ if (shouldPrintResult) {
13054
13174
  console.error(chalk15.blue("\n\u{1F4C4} Result:"));
13055
13175
  console.log(lastIteration.toolResult.content);
13056
13176
  }
@@ -13251,7 +13371,7 @@ async function validateJSONFile(configSchema, baseDir) {
13251
13371
  const warnings = [];
13252
13372
  const filePath = path3__default.join(baseDir, configSchema.file);
13253
13373
  try {
13254
- const exists = await fs3.pathExists(filePath);
13374
+ const exists = await fs2.pathExists(filePath);
13255
13375
  if (!exists) {
13256
13376
  if (configSchema.required) {
13257
13377
  errors.push({
@@ -13274,7 +13394,7 @@ async function validateJSONFile(configSchema, baseDir) {
13274
13394
  return { isValid: !configSchema.required, errors, warnings };
13275
13395
  }
13276
13396
  try {
13277
- await fs3.access(filePath, fs3.constants.R_OK);
13397
+ await fs2.access(filePath, fs2.constants.R_OK);
13278
13398
  } catch (accessError) {
13279
13399
  errors.push({
13280
13400
  file: configSchema.file,
@@ -13290,7 +13410,7 @@ async function validateJSONFile(configSchema, baseDir) {
13290
13410
  }
13291
13411
  let jsonData;
13292
13412
  try {
13293
- const fileContent = await fs3.readFile(filePath, "utf8");
13413
+ const fileContent = await fs2.readFile(filePath, "utf8");
13294
13414
  jsonData = JSON.parse(fileContent);
13295
13415
  } catch (parseError) {
13296
13416
  const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
@@ -13388,7 +13508,7 @@ async function validateJSONConfigs(baseDir = process.cwd(), backendType = "mcp")
13388
13508
  }
13389
13509
  function displayValidationResults(result) {
13390
13510
  if (result.isValid && result.warnings.length === 0) {
13391
- console.log(chalk15.green("\u2705 All configuration files are valid\n"));
13511
+ console.error(chalk15.green("\u2705 All configuration files are valid\n"));
13392
13512
  return;
13393
13513
  }
13394
13514
  if (result.errors.length > 0) {
@@ -13425,7 +13545,7 @@ function displayValidationResults(result) {
13425
13545
  }
13426
13546
  async function logValidationResults(result, baseDir = process.cwd()) {
13427
13547
  const logDir = path3__default.join(baseDir, ".juno_task", "logs");
13428
- await fs3.ensureDir(logDir);
13548
+ await fs2.ensureDir(logDir);
13429
13549
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
13430
13550
  const logFile = path3__default.join(logDir, `startup-validation-${timestamp}.log`);
13431
13551
  const logContent = [
@@ -13468,7 +13588,7 @@ async function logValidationResults(result, baseDir = process.cwd()) {
13468
13588
  logContent.push(``);
13469
13589
  }
13470
13590
  }
13471
- await fs3.writeFile(logFile, logContent.join("\n"));
13591
+ await fs2.writeFile(logFile, logContent.join("\n"));
13472
13592
  return logFile;
13473
13593
  }
13474
13594
  async function validateStartupConfigs(baseDir = process.cwd(), verbose = false) {
@@ -13802,7 +13922,7 @@ var TemplateEngine = class {
13802
13922
  let overallError;
13803
13923
  try {
13804
13924
  if (!options.dryRun) {
13805
- await fs3.ensureDir(targetDirectory);
13925
+ await fs2.ensureDir(targetDirectory);
13806
13926
  }
13807
13927
  for (const template of templates) {
13808
13928
  try {
@@ -15152,7 +15272,7 @@ This directory contains specification documents for your project.
15152
15272
  const fileName = this.getTemplateFileName(template);
15153
15273
  const targetPath = path3.join(targetDirectory, fileName);
15154
15274
  try {
15155
- const fileExists = await fs3.pathExists(targetPath);
15275
+ const fileExists = await fs2.pathExists(targetPath);
15156
15276
  if (fileExists && !options.force && options.onConflict !== "overwrite") {
15157
15277
  return {
15158
15278
  path: targetPath,
@@ -15174,10 +15294,10 @@ This directory contains specification documents for your project.
15174
15294
  }
15175
15295
  if (options.createBackup && fileExists) {
15176
15296
  const backupPath = `${targetPath}.backup.${Date.now()}`;
15177
- await fs3.copy(targetPath, backupPath);
15297
+ await fs2.copy(targetPath, backupPath);
15178
15298
  }
15179
- await fs3.ensureDir(path3.dirname(targetPath));
15180
- await fs3.writeFile(targetPath, renderedContent, "utf8");
15299
+ await fs2.ensureDir(path3.dirname(targetPath));
15300
+ await fs2.writeFile(targetPath, renderedContent, "utf8");
15181
15301
  return {
15182
15302
  path: targetPath,
15183
15303
  content: renderedContent,
@@ -15441,7 +15561,7 @@ var SimpleInitTUI = class {
15441
15561
  }
15442
15562
  async confirmSave(targetDirectory) {
15443
15563
  const junoTaskPath = path3.join(targetDirectory, ".juno_task");
15444
- if (await fs3.pathExists(junoTaskPath)) {
15564
+ if (await fs2.pathExists(junoTaskPath)) {
15445
15565
  console.log(chalk15.yellow(" \u26A0\uFE0F .juno_task directory already exists"));
15446
15566
  console.log(chalk15.gray(" Would you like to:"));
15447
15567
  console.log(chalk15.gray(" 1) Override existing files"));
@@ -15486,16 +15606,16 @@ var SimpleProjectGenerator = class {
15486
15606
  async generate() {
15487
15607
  const { targetDirectory, variables, force } = this.context;
15488
15608
  console.log(chalk15.blue("\u{1F4C1} Creating project directory..."));
15489
- await fs3.ensureDir(targetDirectory);
15609
+ await fs2.ensureDir(targetDirectory);
15490
15610
  const junoTaskDir = path3.join(targetDirectory, ".juno_task");
15491
- const junoTaskExists = await fs3.pathExists(junoTaskDir);
15611
+ const junoTaskExists = await fs2.pathExists(junoTaskDir);
15492
15612
  if (junoTaskExists && !force) {
15493
15613
  throw new ValidationError(
15494
15614
  "Project already initialized. Directory .juno_task already exists.",
15495
15615
  ["Use --force flag to overwrite existing files", "Choose a different directory"]
15496
15616
  );
15497
15617
  }
15498
- await fs3.ensureDir(junoTaskDir);
15618
+ await fs2.ensureDir(junoTaskDir);
15499
15619
  console.log(chalk15.blue("\u2699\uFE0F Creating project configuration..."));
15500
15620
  await this.createConfigFile(junoTaskDir, targetDirectory);
15501
15621
  console.log(chalk15.blue("\u{1F527} Setting up MCP configuration..."));
@@ -15529,21 +15649,21 @@ var SimpleProjectGenerator = class {
15529
15649
  const promptContent = await templateEngine.render(promptTemplate, templateContext);
15530
15650
  const initContent = await templateEngine.render(initTemplate, templateContext);
15531
15651
  const implementContent = await templateEngine.render(implementTemplate, templateContext);
15532
- await fs3.writeFile(path3.join(junoTaskDir, "prompt.md"), promptContent);
15533
- await fs3.writeFile(path3.join(junoTaskDir, "init.md"), initContent);
15534
- await fs3.writeFile(path3.join(junoTaskDir, "implement.md"), implementContent);
15652
+ await fs2.writeFile(path3.join(junoTaskDir, "prompt.md"), promptContent);
15653
+ await fs2.writeFile(path3.join(junoTaskDir, "init.md"), initContent);
15654
+ await fs2.writeFile(path3.join(junoTaskDir, "implement.md"), implementContent);
15535
15655
  const userFeedbackTemplate = templateEngine.getBuiltInTemplate("USER_FEEDBACK.md");
15536
15656
  if (userFeedbackTemplate) {
15537
15657
  const userFeedbackContent = await templateEngine.render(userFeedbackTemplate, templateContext);
15538
- await fs3.writeFile(path3.join(junoTaskDir, "USER_FEEDBACK.md"), userFeedbackContent);
15658
+ await fs2.writeFile(path3.join(junoTaskDir, "USER_FEEDBACK.md"), userFeedbackContent);
15539
15659
  }
15540
15660
  const planTemplate = templateEngine.getBuiltInTemplate("plan.md");
15541
15661
  if (planTemplate) {
15542
15662
  const planContent = await templateEngine.render(planTemplate, templateContext);
15543
- await fs3.writeFile(path3.join(junoTaskDir, "plan.md"), planContent);
15663
+ await fs2.writeFile(path3.join(junoTaskDir, "plan.md"), planContent);
15544
15664
  }
15545
15665
  const specsDir = path3.join(junoTaskDir, "specs");
15546
- await fs3.ensureDir(specsDir);
15666
+ await fs2.ensureDir(specsDir);
15547
15667
  const specsReadmeContent = `# Project Specifications
15548
15668
 
15549
15669
  This directory contains detailed specifications for the project components.
@@ -15560,7 +15680,7 @@ This directory contains detailed specifications for the project components.
15560
15680
  - Avoid conflicts with existing file names
15561
15681
  - Use \`.md\` extension for all specification files
15562
15682
  `;
15563
- await fs3.writeFile(path3.join(specsDir, "README.md"), specsReadmeContent);
15683
+ await fs2.writeFile(path3.join(specsDir, "README.md"), specsReadmeContent);
15564
15684
  const requirementsContent = `# Requirements Specification
15565
15685
 
15566
15686
  ## Functional Requirements
@@ -15607,7 +15727,7 @@ This directory contains detailed specifications for the project components.
15607
15727
  - Code quality: Clean, maintainable codebase
15608
15728
  - Documentation: Complete and accurate documentation
15609
15729
  `;
15610
- await fs3.writeFile(path3.join(specsDir, "requirements.md"), requirementsContent);
15730
+ await fs2.writeFile(path3.join(specsDir, "requirements.md"), requirementsContent);
15611
15731
  const architectureContent = `# Architecture Specification
15612
15732
 
15613
15733
  ## System Overview
@@ -15689,7 +15809,7 @@ This project uses AI-assisted development with juno-code to achieve: ${variables
15689
15809
  - Performance monitoring
15690
15810
  - Security best practices
15691
15811
  `;
15692
- await fs3.writeFile(path3.join(specsDir, "architecture.md"), architectureContent);
15812
+ await fs2.writeFile(path3.join(specsDir, "architecture.md"), architectureContent);
15693
15813
  const claudeContent = `# Claude Development Session Learnings
15694
15814
 
15695
15815
  ## Project Overview
@@ -15761,7 +15881,7 @@ This file will be updated as development progresses to track:
15761
15881
  - Solutions to complex problems
15762
15882
  - Performance improvements and optimizations
15763
15883
  `;
15764
- await fs3.writeFile(path3.join(targetDirectory, "CLAUDE.md"), claudeContent);
15884
+ await fs2.writeFile(path3.join(targetDirectory, "CLAUDE.md"), claudeContent);
15765
15885
  const agentsContent = `# AI Agent Selection and Performance
15766
15886
 
15767
15887
  ## Available Agents
@@ -15825,7 +15945,7 @@ Track agent performance for:
15825
15945
  4. **Feedback Loop**: Provide feedback to improve agent performance
15826
15946
  5. **Performance Monitoring**: Track and optimize agent usage
15827
15947
  `;
15828
- await fs3.writeFile(path3.join(targetDirectory, "AGENTS.md"), agentsContent);
15948
+ await fs2.writeFile(path3.join(targetDirectory, "AGENTS.md"), agentsContent);
15829
15949
  const readmeContent = `# ${variables.PROJECT_NAME}
15830
15950
 
15831
15951
  ${variables.DESCRIPTION}
@@ -15922,7 +16042,7 @@ ${variables.GIT_URL}` : ""}
15922
16042
  Created with juno-code on ${variables.CURRENT_DATE}
15923
16043
  ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
15924
16044
  `;
15925
- await fs3.writeFile(path3.join(targetDirectory, "README.md"), readmeContent);
16045
+ await fs2.writeFile(path3.join(targetDirectory, "README.md"), readmeContent);
15926
16046
  console.log(chalk15.blue("\u{1F4E6} Installing utility scripts..."));
15927
16047
  await this.copyScriptsFromTemplates(junoTaskDir);
15928
16048
  console.log(chalk15.blue("\u{1F40D} Installing Python requirements..."));
@@ -15974,7 +16094,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
15974
16094
  hooks: getDefaultHooks()
15975
16095
  };
15976
16096
  const configPath = path3.join(junoTaskDir, "config.json");
15977
- await fs3.writeFile(configPath, JSON.stringify(configContent, null, 2));
16097
+ await fs2.writeFile(configPath, JSON.stringify(configContent, null, 2));
15978
16098
  console.log(chalk15.green(` \u2713 Created .juno_task/config.json with ${this.context.subagent} as default subagent`));
15979
16099
  }
15980
16100
  async createMcpFile(junoTaskDir, targetDirectory) {
@@ -16028,7 +16148,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16028
16148
  }
16029
16149
  };
16030
16150
  const mcpPath = path3.join(junoTaskDir, "mcp.json");
16031
- await fs3.writeFile(mcpPath, JSON.stringify(mcpContent, null, 2));
16151
+ await fs2.writeFile(mcpPath, JSON.stringify(mcpContent, null, 2));
16032
16152
  console.log(chalk15.green(` \u2713 Created .juno_task/mcp.json with roundtable-ai server configuration`));
16033
16153
  }
16034
16154
  getDefaultModelForSubagent(subagent) {
@@ -16047,7 +16167,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16047
16167
  async copyScriptsFromTemplates(junoTaskDir) {
16048
16168
  try {
16049
16169
  const scriptsDir = path3.join(junoTaskDir, "scripts");
16050
- await fs3.ensureDir(scriptsDir);
16170
+ await fs2.ensureDir(scriptsDir);
16051
16171
  const __filename2 = fileURLToPath(import.meta.url);
16052
16172
  const __dirname2 = path3.dirname(__filename2);
16053
16173
  let templatesScriptsDir;
@@ -16058,11 +16178,11 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16058
16178
  } else {
16059
16179
  templatesScriptsDir = path3.join(__dirname2, "../../templates/scripts");
16060
16180
  }
16061
- if (!await fs3.pathExists(templatesScriptsDir)) {
16181
+ if (!await fs2.pathExists(templatesScriptsDir)) {
16062
16182
  console.log(chalk15.yellow(" \u26A0\uFE0F Template scripts directory not found, skipping script installation"));
16063
16183
  return;
16064
16184
  }
16065
- const scriptFiles = await fs3.readdir(templatesScriptsDir);
16185
+ const scriptFiles = await fs2.readdir(templatesScriptsDir);
16066
16186
  if (scriptFiles.length === 0) {
16067
16187
  console.log(chalk15.gray(" \u2139\uFE0F No template scripts found to install"));
16068
16188
  return;
@@ -16071,11 +16191,11 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16071
16191
  for (const scriptFile of scriptFiles) {
16072
16192
  const sourcePath = path3.join(templatesScriptsDir, scriptFile);
16073
16193
  const destPath = path3.join(scriptsDir, scriptFile);
16074
- const stats = await fs3.stat(sourcePath);
16194
+ const stats = await fs2.stat(sourcePath);
16075
16195
  if (stats.isFile()) {
16076
- await fs3.copy(sourcePath, destPath);
16196
+ await fs2.copy(sourcePath, destPath);
16077
16197
  if (scriptFile.endsWith(".sh")) {
16078
- await fs3.chmod(destPath, 493);
16198
+ await fs2.chmod(destPath, 493);
16079
16199
  }
16080
16200
  copiedCount++;
16081
16201
  console.log(chalk15.green(` \u2713 Installed script: ${scriptFile}`));
@@ -16098,7 +16218,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16098
16218
  try {
16099
16219
  const scriptsDir = path3.join(junoTaskDir, "scripts");
16100
16220
  const installScript = path3.join(scriptsDir, "install_requirements.sh");
16101
- if (!await fs3.pathExists(installScript)) {
16221
+ if (!await fs2.pathExists(installScript)) {
16102
16222
  console.log(chalk15.yellow(" \u26A0\uFE0F install_requirements.sh not found, skipping Python dependencies installation"));
16103
16223
  console.log(chalk15.gray(" You can install dependencies manually: juno-kanban, roundtable-ai"));
16104
16224
  return;
@@ -16395,20 +16515,20 @@ init_types();
16395
16515
  async function loadInitPrompt(directory) {
16396
16516
  const junoTaskDir = path3.join(directory, ".juno_task");
16397
16517
  const initFile = path3.join(junoTaskDir, "init.md");
16398
- if (!await fs3.pathExists(junoTaskDir)) {
16518
+ if (!await fs2.pathExists(junoTaskDir)) {
16399
16519
  throw new FileSystemError(
16400
16520
  'No .juno_task directory found. Run "juno-code init" first.',
16401
16521
  junoTaskDir
16402
16522
  );
16403
16523
  }
16404
- if (!await fs3.pathExists(initFile)) {
16524
+ if (!await fs2.pathExists(initFile)) {
16405
16525
  throw new FileSystemError(
16406
16526
  "No init.md file found in .juno_task directory",
16407
16527
  initFile
16408
16528
  );
16409
16529
  }
16410
16530
  try {
16411
- const content = await fs3.readFile(initFile, "utf-8");
16531
+ const content = await fs2.readFile(initFile, "utf-8");
16412
16532
  if (!content.trim()) {
16413
16533
  throw new FileSystemError(
16414
16534
  "init.md file is empty. Please add task instructions.",
@@ -17440,8 +17560,8 @@ Focus on ${request.intelligence === "basic" ? "basic functionality" : request.in
17440
17560
  for (const scenario of scenarios) {
17441
17561
  const testContent = await this.generateTestContent(request, scenario, template);
17442
17562
  const testFilePath = this.resolveTestFilePath(request, scenario);
17443
- await fs3.ensureDir(path3.dirname(testFilePath));
17444
- await fs3.writeFile(testFilePath, testContent);
17563
+ await fs2.ensureDir(path3.dirname(testFilePath));
17564
+ await fs2.writeFile(testFilePath, testContent);
17445
17565
  testFiles.push(testFilePath);
17446
17566
  }
17447
17567
  return testFiles;
@@ -17693,8 +17813,8 @@ var TestExecutionEngine = class {
17693
17813
  async collectCoverage(request) {
17694
17814
  const coverageFile = path3.join(request.workingDirectory, "coverage", "coverage-summary.json");
17695
17815
  try {
17696
- if (await fs3.pathExists(coverageFile)) {
17697
- const coverageData = await fs3.readJson(coverageFile);
17816
+ if (await fs2.pathExists(coverageFile)) {
17817
+ const coverageData = await fs2.readJson(coverageFile);
17698
17818
  return {
17699
17819
  lines: coverageData.total?.lines?.pct || 0,
17700
17820
  functions: coverageData.total?.functions?.pct || 0,
@@ -17877,8 +17997,8 @@ var TestReportEngine = class {
17877
17997
  suggestions: analysis.suggestions,
17878
17998
  recommendations: analysis.recommendations
17879
17999
  };
17880
- await fs3.ensureDir(path3.dirname(outputPath));
17881
- await fs3.writeJson(outputPath, report, { spaces: 2 });
18000
+ await fs2.ensureDir(path3.dirname(outputPath));
18001
+ await fs2.writeJson(outputPath, report, { spaces: 2 });
17882
18002
  return outputPath;
17883
18003
  }
17884
18004
  async generateHTMLReport(analysis, outputPath, includeVisualizations) {
@@ -17941,8 +18061,8 @@ var TestReportEngine = class {
17941
18061
  </body>
17942
18062
  </html>
17943
18063
  `.trim();
17944
- await fs3.ensureDir(path3.dirname(outputPath));
17945
- await fs3.writeFile(outputPath, html);
18064
+ await fs2.ensureDir(path3.dirname(outputPath));
18065
+ await fs2.writeFile(outputPath, html);
17946
18066
  return outputPath;
17947
18067
  }
17948
18068
  generateCharts(analysis) {
@@ -17999,8 +18119,8 @@ ${analysis.suggestions.map((suggestion) => `- ${suggestion}`).join("\n")}
17999
18119
 
18000
18120
  ${analysis.recommendations.map((rec) => `- ${rec}`).join("\n")}
18001
18121
  `.trim();
18002
- await fs3.ensureDir(path3.dirname(outputPath));
18003
- await fs3.writeFile(outputPath, markdown);
18122
+ await fs2.ensureDir(path3.dirname(outputPath));
18123
+ await fs2.writeFile(outputPath, markdown);
18004
18124
  return outputPath;
18005
18125
  }
18006
18126
  async displayConsoleReport(analysis) {
@@ -18340,7 +18460,7 @@ async function compactConfigFile(filePath, options = {}) {
18340
18460
  preserveDays = 30,
18341
18461
  preservePatterns = []
18342
18462
  } = options;
18343
- const originalContent = await fs3.readFile(filePath, "utf-8");
18463
+ const originalContent = await fs2.readFile(filePath, "utf-8");
18344
18464
  const originalSize = originalContent.length;
18345
18465
  let backupPath = "";
18346
18466
  if (createBackup && !dryRun) {
@@ -18349,7 +18469,7 @@ async function compactConfigFile(filePath, options = {}) {
18349
18469
  const basename11 = path3.basename(filePath, ext);
18350
18470
  const dirname12 = path3.dirname(filePath);
18351
18471
  backupPath = path3.join(dirname12, `${basename11}.backup.${timestamp}${ext}`);
18352
- await fs3.writeFile(backupPath, originalContent, "utf-8");
18472
+ await fs2.writeFile(backupPath, originalContent, "utf-8");
18353
18473
  }
18354
18474
  const compactionAnalysis = analyzeMarkdownStructure(originalContent);
18355
18475
  const compactedContent = compactMarkdownContent(
@@ -18364,7 +18484,7 @@ async function compactConfigFile(filePath, options = {}) {
18364
18484
  const compactedSize = finalContent.length;
18365
18485
  const reductionPercentage = Math.round((originalSize - compactedSize) / originalSize * 100);
18366
18486
  if (!dryRun) {
18367
- await fs3.writeFile(filePath, finalContent, "utf-8");
18487
+ await fs2.writeFile(filePath, finalContent, "utf-8");
18368
18488
  }
18369
18489
  return {
18370
18490
  originalSize,
@@ -18494,7 +18614,7 @@ function formatFileSize(bytes) {
18494
18614
  }
18495
18615
  async function shouldCompactFile(filePath, sizeThresholdKB = 50, ageThresholdDays = 30) {
18496
18616
  try {
18497
- const stats = await fs3.stat(filePath);
18617
+ const stats = await fs2.stat(filePath);
18498
18618
  const sizeKB = stats.size / 1024;
18499
18619
  if (sizeKB > sizeThresholdKB) {
18500
18620
  return true;
@@ -18516,19 +18636,19 @@ async function archiveResolvedIssues(options) {
18516
18636
  feedbackFile,
18517
18637
  archiveDir = path3.join(path3.dirname(feedbackFile), "archives"),
18518
18638
  openIssuesThreshold = 10} = options;
18519
- if (!await fs3.pathExists(feedbackFile)) {
18639
+ if (!await fs2.pathExists(feedbackFile)) {
18520
18640
  throw new Error(`Feedback file does not exist: ${feedbackFile}`);
18521
18641
  }
18522
- const content = await fs3.readFile(feedbackFile, "utf-8");
18642
+ const content = await fs2.readFile(feedbackFile, "utf-8");
18523
18643
  const parsed = parseUserFeedback(content);
18524
18644
  const warningsGenerated = [];
18525
18645
  if (parsed.openIssues.length > openIssuesThreshold) {
18526
18646
  const warning = `Found ${parsed.openIssues.length} open issues (threshold: ${openIssuesThreshold}). Consider reviewing and prioritizing.`;
18527
18647
  warningsGenerated.push(warning);
18528
18648
  const logFile = path3.join(path3.dirname(feedbackFile), "logs", "feedback-warnings.log");
18529
- await fs3.ensureDir(path3.dirname(logFile));
18649
+ await fs2.ensureDir(path3.dirname(logFile));
18530
18650
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
18531
- await fs3.appendFile(logFile, `[${timestamp}] ${warning}
18651
+ await fs2.appendFile(logFile, `[${timestamp}] ${warning}
18532
18652
  `);
18533
18653
  }
18534
18654
  if (parsed.resolvedIssues.length === 0) {
@@ -18545,10 +18665,10 @@ async function archiveResolvedIssues(options) {
18545
18665
  const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
18546
18666
  const archiveFile = path3.join(archiveDir, `USER_FEEDBACK_archive_${currentYear}.md`);
18547
18667
  {
18548
- await fs3.ensureDir(archiveDir);
18668
+ await fs2.ensureDir(archiveDir);
18549
18669
  await appendToArchive(archiveFile, parsed.resolvedIssues);
18550
18670
  const compactedContent = generateCompactedFeedback(parsed.openIssues, parsed.metadata);
18551
- await fs3.writeFile(feedbackFile, compactedContent, "utf-8");
18671
+ await fs2.writeFile(feedbackFile, compactedContent, "utf-8");
18552
18672
  }
18553
18673
  {
18554
18674
  console.log(chalk15.green(`\u2705 Archived ${parsed.resolvedIssues.length} resolved issues`));
@@ -18595,8 +18715,8 @@ function parseUserFeedback(content) {
18595
18715
  async function appendToArchive(archiveFile, resolvedIssues) {
18596
18716
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
18597
18717
  let archiveContent = "";
18598
- if (await fs3.pathExists(archiveFile)) {
18599
- archiveContent = await fs3.readFile(archiveFile, "utf-8");
18718
+ if (await fs2.pathExists(archiveFile)) {
18719
+ archiveContent = await fs2.readFile(archiveFile, "utf-8");
18600
18720
  } else {
18601
18721
  const year = path3.basename(archiveFile).match(/(\d{4})/)?.[1] || (/* @__PURE__ */ new Date()).getFullYear();
18602
18722
  archiveContent = `# User Feedback Archive ${year}
@@ -18630,7 +18750,7 @@ ${resolvedIssue}
18630
18750
  `- Last updated: ${timestamp}`
18631
18751
  );
18632
18752
  }
18633
- await fs3.writeFile(archiveFile, archiveContent, "utf-8");
18753
+ await fs2.writeFile(archiveFile, archiveContent, "utf-8");
18634
18754
  }
18635
18755
  function generateCompactedFeedback(openIssues, metadata) {
18636
18756
  let content = metadata.trim() + "\n";
@@ -18655,15 +18775,15 @@ async function shouldArchive(feedbackFile, options = {}) {
18655
18775
  // 50KB
18656
18776
  lineCountThreshold = 500
18657
18777
  } = options;
18658
- if (!await fs3.pathExists(feedbackFile)) {
18778
+ if (!await fs2.pathExists(feedbackFile)) {
18659
18779
  return {
18660
18780
  shouldArchive: false,
18661
18781
  reasons: [],
18662
18782
  stats: { openIssuesCount: 0, resolvedIssuesCount: 0, fileSizeBytes: 0, lineCount: 0 }
18663
18783
  };
18664
18784
  }
18665
- const content = await fs3.readFile(feedbackFile, "utf-8");
18666
- const stats = await fs3.stat(feedbackFile);
18785
+ const content = await fs2.readFile(feedbackFile, "utf-8");
18786
+ const stats = await fs2.stat(feedbackFile);
18667
18787
  const parsed = parseUserFeedback(content);
18668
18788
  const lineCount = content.split("\n").length;
18669
18789
  const reasons = [];
@@ -18737,16 +18857,16 @@ var EnhancedFeedbackFileManager = class {
18737
18857
  this.feedbackFile = feedbackFile;
18738
18858
  }
18739
18859
  async ensureExists() {
18740
- if (!await fs3.pathExists(this.feedbackFile)) {
18860
+ if (!await fs2.pathExists(this.feedbackFile)) {
18741
18861
  await this.createInitialFile();
18742
18862
  }
18743
18863
  }
18744
18864
  async addFeedback(issue, testCriteria) {
18745
18865
  await this.ensureExists();
18746
18866
  try {
18747
- const content = await fs3.readFile(this.feedbackFile, "utf-8");
18867
+ const content = await fs2.readFile(this.feedbackFile, "utf-8");
18748
18868
  const updatedContent = this.addIssueToContent(content, issue, testCriteria);
18749
- await fs3.writeFile(this.feedbackFile, updatedContent, "utf-8");
18869
+ await fs2.writeFile(this.feedbackFile, updatedContent, "utf-8");
18750
18870
  } catch (error) {
18751
18871
  throw new ValidationError(
18752
18872
  `Failed to save feedback: ${error}`,
@@ -18759,12 +18879,12 @@ var EnhancedFeedbackFileManager = class {
18759
18879
  */
18760
18880
  async repairMalformedFile() {
18761
18881
  try {
18762
- const content = await fs3.readFile(this.feedbackFile, "utf-8");
18882
+ const content = await fs2.readFile(this.feedbackFile, "utf-8");
18763
18883
  const hasOpenIssues = content.includes("<OPEN_ISSUES>");
18764
18884
  const hasClosingTag = content.includes("</OPEN_ISSUES>");
18765
18885
  if (!hasOpenIssues || !hasClosingTag) {
18766
18886
  const backupPath = this.feedbackFile + ".backup." + Date.now();
18767
- await fs3.writeFile(backupPath, content, "utf-8");
18887
+ await fs2.writeFile(backupPath, content, "utf-8");
18768
18888
  const existingIssues = this.extractIssuesFromMalformedContent(content);
18769
18889
  await this.createInitialFile(existingIssues);
18770
18890
  }
@@ -18796,8 +18916,8 @@ List any features you'd like to see added or bugs you've encountered.
18796
18916
 
18797
18917
  <!-- Resolved issues will be moved here -->
18798
18918
  `;
18799
- await fs3.ensureDir(path3.dirname(this.feedbackFile));
18800
- await fs3.writeFile(this.feedbackFile, initialContent, "utf-8");
18919
+ await fs2.ensureDir(path3.dirname(this.feedbackFile));
18920
+ await fs2.writeFile(this.feedbackFile, initialContent, "utf-8");
18801
18921
  }
18802
18922
  addIssueToContent(content, issue, testCriteria) {
18803
18923
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -18913,17 +19033,17 @@ async function handleCompactCommand(subArgs, options) {
18913
19033
  if (subArgs.length > 0) {
18914
19034
  for (const filePath of subArgs) {
18915
19035
  const resolvedPath = path3.resolve(filePath);
18916
- if (await fs3.pathExists(resolvedPath)) {
19036
+ if (await fs2.pathExists(resolvedPath)) {
18917
19037
  filesToCompact.push(resolvedPath);
18918
19038
  } else {
18919
19039
  console.warn(chalk15.yellow(`\u26A0\uFE0F File not found: ${filePath}`));
18920
19040
  }
18921
19041
  }
18922
19042
  } else {
18923
- if (await fs3.pathExists(defaultClaudeFile)) {
19043
+ if (await fs2.pathExists(defaultClaudeFile)) {
18924
19044
  filesToCompact.push(defaultClaudeFile);
18925
19045
  }
18926
- if (await fs3.pathExists(defaultAgentsFile)) {
19046
+ if (await fs2.pathExists(defaultAgentsFile)) {
18927
19047
  filesToCompact.push(defaultAgentsFile);
18928
19048
  }
18929
19049
  }
@@ -19857,11 +19977,11 @@ var GitManager = class {
19857
19977
  */
19858
19978
  async updateJunoTaskConfig(gitUrl) {
19859
19979
  const configPath = path3.join(this.workingDirectory, ".juno_task", "init.md");
19860
- if (!await fs3.pathExists(configPath)) {
19980
+ if (!await fs2.pathExists(configPath)) {
19861
19981
  return;
19862
19982
  }
19863
19983
  try {
19864
- let content = await fs3.readFile(configPath, "utf-8");
19984
+ let content = await fs2.readFile(configPath, "utf-8");
19865
19985
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
19866
19986
  if (frontmatterMatch) {
19867
19987
  let frontmatter = frontmatterMatch[1];
@@ -19882,7 +20002,7 @@ GIT_URL: ${gitUrl}
19882
20002
  `;
19883
20003
  content = frontmatter + content;
19884
20004
  }
19885
- await fs3.writeFile(configPath, content, "utf-8");
20005
+ await fs2.writeFile(configPath, content, "utf-8");
19886
20006
  } catch (error) {
19887
20007
  console.warn(`Warning: Failed to update juno-code configuration: ${error}`);
19888
20008
  }
@@ -21814,6 +21934,11 @@ var QUICK_REFERENCE = [
21814
21934
  description: "Shell completion setup",
21815
21935
  usage: "juno-code completion <install|uninstall>"
21816
21936
  },
21937
+ {
21938
+ name: "services",
21939
+ description: "Manage service scripts (use --force to refresh codex.py/claude.py)",
21940
+ usage: "juno-code services install --force"
21941
+ },
21817
21942
  {
21818
21943
  name: "help",
21819
21944
  description: "Show help information",
@@ -22382,11 +22507,12 @@ init_service_installer();
22382
22507
  function createServicesCommand() {
22383
22508
  const servicesCmd = new Command("services").description("Manage juno-code service scripts").addHelpText("after", `
22384
22509
  Examples:
22385
- $ juno-code services install Install service scripts to ~/.juno_code/services/
22386
- $ juno-code services list List installed service scripts
22387
- $ juno-code services status Check installation status
22388
- $ juno-code services uninstall Remove all service scripts
22389
- $ juno-code services path Show services directory path
22510
+ $ juno-code services install Install service scripts to ~/.juno_code/services/
22511
+ $ juno-code services install --force Reinstall/refresh service scripts (codex.py/claude.py)
22512
+ $ juno-code services list List installed service scripts
22513
+ $ juno-code services status Check installation status
22514
+ $ juno-code services uninstall Remove all service scripts
22515
+ $ juno-code services path Show services directory path
22390
22516
 
22391
22517
  Service scripts are Python/shell scripts that provide additional functionality
22392
22518
  and can be customized by users. They are installed to ~/.juno_code/services/
@@ -22560,7 +22686,7 @@ var ShellDetector = class _ShellDetector {
22560
22686
  * Get shell configuration file path
22561
22687
  */
22562
22688
  getConfigPath(shell) {
22563
- const homeDir = os3.homedir();
22689
+ const homeDir = os4.homedir();
22564
22690
  switch (shell) {
22565
22691
  case "bash":
22566
22692
  if (process.platform === "darwin") {
@@ -22584,7 +22710,7 @@ var ShellDetector = class _ShellDetector {
22584
22710
  * Get shell completion script installation path
22585
22711
  */
22586
22712
  getCompletionPath(shell) {
22587
- const homeDir = os3.homedir();
22713
+ const homeDir = os4.homedir();
22588
22714
  switch (shell) {
22589
22715
  case "bash":
22590
22716
  if (process.platform === "darwin") {
@@ -22639,10 +22765,10 @@ autoload -U compinit && compinit`;
22639
22765
  */
22640
22766
  async isSourceCommandPresent(configPath, sourceCommand) {
22641
22767
  try {
22642
- if (!await fs3.pathExists(configPath)) {
22768
+ if (!await fs2.pathExists(configPath)) {
22643
22769
  return false;
22644
22770
  }
22645
- const content = await fs3.readFile(configPath, "utf-8");
22771
+ const content = await fs2.readFile(configPath, "utf-8");
22646
22772
  return content.includes("juno-code completion");
22647
22773
  } catch {
22648
22774
  return false;
@@ -22664,15 +22790,15 @@ autoload -U compinit && compinit`;
22664
22790
  continue;
22665
22791
  }
22666
22792
  try {
22667
- const completionExists = await fs3.pathExists(shell.completionPath);
22668
- const configExists = await fs3.pathExists(shell.configPath);
22793
+ const completionExists = await fs2.pathExists(shell.completionPath);
22794
+ const configExists = await fs2.pathExists(shell.configPath);
22669
22795
  const isSourced = configExists && await this.isSourceCommandPresent(
22670
22796
  shell.configPath,
22671
22797
  this.getSourceCommand(shell.name, shell.completionPath)
22672
22798
  );
22673
22799
  let lastInstalled;
22674
22800
  if (completionExists) {
22675
- const stats = await fs3.stat(shell.completionPath);
22801
+ const stats = await fs2.stat(shell.completionPath);
22676
22802
  lastInstalled = stats.mtime;
22677
22803
  }
22678
22804
  statuses.push({
@@ -22698,7 +22824,7 @@ autoload -U compinit && compinit`;
22698
22824
  async ensureCompletionDirectory(shell) {
22699
22825
  const completionPath = this.getCompletionPath(shell);
22700
22826
  const completionDir = path3.dirname(completionPath);
22701
- await fs3.ensureDir(completionDir);
22827
+ await fs2.ensureDir(completionDir);
22702
22828
  }
22703
22829
  /**
22704
22830
  * Ensure directory exists for shell configuration
@@ -22706,7 +22832,7 @@ autoload -U compinit && compinit`;
22706
22832
  async ensureConfigDirectory(shell) {
22707
22833
  const configPath = this.getConfigPath(shell);
22708
22834
  const configDir = path3.dirname(configPath);
22709
- await fs3.ensureDir(configDir);
22835
+ await fs2.ensureDir(configDir);
22710
22836
  }
22711
22837
  /**
22712
22838
  * Get shell version information
@@ -22730,15 +22856,15 @@ autoload -U compinit && compinit`;
22730
22856
  try {
22731
22857
  const configPath = this.getConfigPath(shell);
22732
22858
  const configDir = path3.dirname(configPath);
22733
- await fs3.access(configDir, fs3.constants.W_OK);
22859
+ await fs2.access(configDir, fs2.constants.W_OK);
22734
22860
  } catch {
22735
22861
  issues.push(`Cannot write to ${shell} configuration directory`);
22736
22862
  }
22737
22863
  try {
22738
22864
  const completionPath = this.getCompletionPath(shell);
22739
22865
  const completionDir = path3.dirname(completionPath);
22740
- await fs3.ensureDir(completionDir);
22741
- await fs3.access(completionDir, fs3.constants.W_OK);
22866
+ await fs2.ensureDir(completionDir);
22867
+ await fs2.access(completionDir, fs2.constants.W_OK);
22742
22868
  } catch {
22743
22869
  issues.push(`Cannot write to ${shell} completion directory`);
22744
22870
  }
@@ -22809,10 +22935,10 @@ var ContextAwareCompletion = class {
22809
22935
  async getSessionIds() {
22810
22936
  try {
22811
22937
  const sessionDir = path3.join(process.cwd(), ".juno_task", "sessions");
22812
- if (!await fs3.pathExists(sessionDir)) {
22938
+ if (!await fs2.pathExists(sessionDir)) {
22813
22939
  return [];
22814
22940
  }
22815
- const sessions = await fs3.readdir(sessionDir);
22941
+ const sessions = await fs2.readdir(sessionDir);
22816
22942
  return sessions.filter((name) => name.match(/^session_\d+$/)).map((name) => name.replace("session_", "")).sort((a, b) => parseInt(b) - parseInt(a));
22817
22943
  } catch {
22818
22944
  return [];
@@ -22826,8 +22952,8 @@ var ContextAwareCompletion = class {
22826
22952
  const builtinTemplates = ["basic", "advanced", "research", "development"];
22827
22953
  const customTemplatesDir = path3.join(process.cwd(), ".juno_task", "templates");
22828
22954
  let customTemplates = [];
22829
- if (await fs3.pathExists(customTemplatesDir)) {
22830
- const files = await fs3.readdir(customTemplatesDir);
22955
+ if (await fs2.pathExists(customTemplatesDir)) {
22956
+ const files = await fs2.readdir(customTemplatesDir);
22831
22957
  customTemplates = files.filter((name) => name.endsWith(".md") || name.endsWith(".hbs")).map((name) => path3.basename(name, path3.extname(name)));
22832
22958
  }
22833
22959
  return [...builtinTemplates, ...customTemplates].sort();
@@ -22862,7 +22988,7 @@ var ContextAwareCompletion = class {
22862
22988
  */
22863
22989
  expandPath(inputPath) {
22864
22990
  if (inputPath.startsWith("~/")) {
22865
- return path3.join(os3.homedir(), inputPath.slice(2));
22991
+ return path3.join(os4.homedir(), inputPath.slice(2));
22866
22992
  }
22867
22993
  if (inputPath.startsWith("./") || inputPath.startsWith("../")) {
22868
22994
  return path3.resolve(inputPath);
@@ -22904,7 +23030,7 @@ var CompletionInstaller = class {
22904
23030
  await this.shellDetector.ensureConfigDirectory(shell);
22905
23031
  const script = this.generateEnhancedCompletion(shell, "juno-code");
22906
23032
  const completionPath = this.shellDetector.getCompletionPath(shell);
22907
- await fs3.writeFile(completionPath, script, "utf-8");
23033
+ await fs2.writeFile(completionPath, script, "utf-8");
22908
23034
  const configPath = this.shellDetector.getConfigPath(shell);
22909
23035
  const sourceCommand = this.shellDetector.getSourceCommand(shell, completionPath);
22910
23036
  const warnings = [];
@@ -22912,7 +23038,7 @@ var CompletionInstaller = class {
22912
23038
  const isPresent = await this.shellDetector.isSourceCommandPresent(configPath, sourceCommand);
22913
23039
  if (!isPresent) {
22914
23040
  try {
22915
- await fs3.appendFile(configPath, `
23041
+ await fs2.appendFile(configPath, `
22916
23042
 
22917
23043
  ${sourceCommand}
22918
23044
  `);
@@ -22943,8 +23069,8 @@ ${sourceCommand}
22943
23069
  async uninstall(shell) {
22944
23070
  try {
22945
23071
  const completionPath = this.shellDetector.getCompletionPath(shell);
22946
- if (await fs3.pathExists(completionPath)) {
22947
- await fs3.remove(completionPath);
23072
+ if (await fs2.pathExists(completionPath)) {
23073
+ await fs2.remove(completionPath);
22948
23074
  return true;
22949
23075
  }
22950
23076
  return false;
@@ -22958,7 +23084,7 @@ ${sourceCommand}
22958
23084
  async isInstalled(shell) {
22959
23085
  try {
22960
23086
  const completionPath = this.shellDetector.getCompletionPath(shell);
22961
- return await fs3.pathExists(completionPath);
23087
+ return await fs2.pathExists(completionPath);
22962
23088
  } catch {
22963
23089
  return false;
22964
23090
  }