juno-code 1.0.34 → 1.0.36

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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import fs2 from 'fs-extra';
2
+ import fs3 from 'fs-extra';
3
3
  import * as path3 from 'path';
4
4
  import path3__default, { dirname, join } from 'path';
5
5
  import * as os4 from 'os';
@@ -142,8 +142,8 @@ var init_types = __esm({
142
142
  };
143
143
  FileSystemError = class extends CLIError {
144
144
  code = "FILESYSTEM_ERROR";
145
- constructor(message, path23) {
146
- super(path23 ? `${message}: ${path23}` : message);
145
+ constructor(message, path24) {
146
+ super(path24 ? `${message}: ${path24}` : message);
147
147
  this.suggestions = [
148
148
  "Check file/directory permissions",
149
149
  "Verify path exists and is accessible",
@@ -305,8 +305,21 @@ var init_service_installer = __esm({
305
305
  "src/utils/service-installer.ts"() {
306
306
  init_version();
307
307
  ServiceInstaller = class {
308
+ static REQUIRED_SCRIPTS = ["codex.py", "claude.py", "gemini.py"];
308
309
  static SERVICES_DIR = path3.join(homedir(), ".juno_code", "services");
309
310
  static VERSION_FILE = path3.join(homedir(), ".juno_code", "services", ".version");
311
+ static missingScripts(baseDir) {
312
+ return this.REQUIRED_SCRIPTS.filter((file) => !fs3.existsSync(path3.join(baseDir, file)));
313
+ }
314
+ static async missingScriptsAsync(baseDir) {
315
+ const results = await Promise.all(
316
+ this.REQUIRED_SCRIPTS.map(async (file) => ({
317
+ file,
318
+ exists: await fs3.pathExists(path3.join(baseDir, file))
319
+ }))
320
+ );
321
+ return results.filter((result) => !result.exists).map((result) => result.file);
322
+ }
310
323
  /**
311
324
  * Get the current package version
312
325
  */
@@ -315,12 +328,12 @@ var init_service_installer = __esm({
315
328
  const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
316
329
  const require3 = createRequire(import.meta.url);
317
330
  let packageJsonPath = path3.join(__dirname2, "..", "..", "package.json");
318
- if (fs2.existsSync(packageJsonPath)) {
331
+ if (fs3.existsSync(packageJsonPath)) {
319
332
  const packageJson2 = require3(packageJsonPath);
320
333
  return packageJson2.version;
321
334
  }
322
335
  packageJsonPath = path3.join(__dirname2, "..", "..", "..", "package.json");
323
- if (fs2.existsSync(packageJsonPath)) {
336
+ if (fs3.existsSync(packageJsonPath)) {
324
337
  const packageJson2 = require3(packageJsonPath);
325
338
  return packageJson2.version;
326
339
  }
@@ -334,11 +347,11 @@ var init_service_installer = __esm({
334
347
  */
335
348
  static async getInstalledVersion() {
336
349
  try {
337
- const exists = await fs2.pathExists(this.VERSION_FILE);
350
+ const exists = await fs3.pathExists(this.VERSION_FILE);
338
351
  if (!exists) {
339
352
  return null;
340
353
  }
341
- const version3 = await fs2.readFile(this.VERSION_FILE, "utf-8");
354
+ const version3 = await fs3.readFile(this.VERSION_FILE, "utf-8");
342
355
  return version3.trim();
343
356
  } catch {
344
357
  return null;
@@ -349,7 +362,7 @@ var init_service_installer = __esm({
349
362
  */
350
363
  static async saveVersion() {
351
364
  const version3 = this.getPackageVersion();
352
- await fs2.writeFile(this.VERSION_FILE, version3, "utf-8");
365
+ await fs3.writeFile(this.VERSION_FILE, version3, "utf-8");
353
366
  }
354
367
  /**
355
368
  * Check if services need to be updated based on version
@@ -361,7 +374,7 @@ var init_service_installer = __esm({
361
374
  if (!installedVersion) {
362
375
  return true;
363
376
  }
364
- const exists = await fs2.pathExists(this.SERVICES_DIR);
377
+ const exists = await fs3.pathExists(this.SERVICES_DIR);
365
378
  if (!exists) {
366
379
  return true;
367
380
  }
@@ -369,32 +382,22 @@ var init_service_installer = __esm({
369
382
  return true;
370
383
  }
371
384
  if (semver.eq(packageVersion, installedVersion)) {
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);
376
- if (!codexExists || !claudeExists) {
385
+ const missingInstalled = await this.missingScriptsAsync(this.SERVICES_DIR);
386
+ if (missingInstalled.length > 0) {
377
387
  return true;
378
388
  }
379
389
  try {
380
390
  const packageServicesDir = this.getPackageServicesDir();
381
- const packageCodex = path3.join(packageServicesDir, "codex.py");
382
- const packageClaude = path3.join(packageServicesDir, "claude.py");
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) {
391
- return true;
392
- }
391
+ const missingPackageScripts = this.missingScripts(packageServicesDir);
392
+ if (missingPackageScripts.length > 0) {
393
+ throw new Error(`Missing required service scripts: ${missingPackageScripts.join(", ")}`);
393
394
  }
394
- if (packageClaudeExists) {
395
+ for (const script of this.REQUIRED_SCRIPTS) {
396
+ const packageScript = path3.join(packageServicesDir, script);
397
+ const installedScript = path3.join(this.SERVICES_DIR, script);
395
398
  const [pkg, inst] = await Promise.all([
396
- fs2.readFile(packageClaude, "utf-8"),
397
- fs2.readFile(installedClaude, "utf-8")
399
+ fs3.readFile(packageScript, "utf-8"),
400
+ fs3.readFile(installedScript, "utf-8")
398
401
  ]);
399
402
  if (pkg !== inst) {
400
403
  return true;
@@ -418,15 +421,27 @@ var init_service_installer = __esm({
418
421
  */
419
422
  static getPackageServicesDir() {
420
423
  const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
421
- let servicesPath = path3.join(__dirname2, "..", "..", "templates", "services");
422
- if (fs2.existsSync(servicesPath)) {
423
- return servicesPath;
424
- }
425
- servicesPath = path3.join(__dirname2, "..", "templates", "services");
426
- if (fs2.existsSync(servicesPath)) {
427
- return servicesPath;
424
+ const candidates = [
425
+ path3.join(__dirname2, "..", "..", "templates", "services"),
426
+ // dist (production)
427
+ path3.join(__dirname2, "..", "templates", "services")
428
+ // src (development)
429
+ ];
430
+ for (const servicesPath of candidates) {
431
+ if (!fs3.existsSync(servicesPath)) {
432
+ continue;
433
+ }
434
+ const missing = this.missingScripts(servicesPath);
435
+ if (missing.length === 0) {
436
+ return servicesPath;
437
+ }
438
+ if (process.env.JUNO_CODE_DEBUG === "1") {
439
+ console.error(`[DEBUG] Services path missing required scripts (${servicesPath}): ${missing.join(", ")}`);
440
+ }
428
441
  }
429
- throw new Error("Could not find services directory in package");
442
+ throw new Error(
443
+ "Could not find services directory in package containing codex.py, claude.py, and gemini.py. Try reinstalling juno-code or re-running npm run build to refresh service scripts."
444
+ );
430
445
  }
431
446
  /**
432
447
  * Install all service scripts to ~/.juno_code/services/
@@ -434,18 +449,22 @@ var init_service_installer = __esm({
434
449
  */
435
450
  static async install(silent = false) {
436
451
  try {
437
- await fs2.ensureDir(this.SERVICES_DIR);
452
+ await fs3.ensureDir(this.SERVICES_DIR);
438
453
  const packageServicesDir = this.getPackageServicesDir();
439
- await fs2.copy(packageServicesDir, this.SERVICES_DIR, {
454
+ await fs3.copy(packageServicesDir, this.SERVICES_DIR, {
440
455
  overwrite: true,
441
456
  preserveTimestamps: true
442
457
  });
443
- const files = await fs2.readdir(this.SERVICES_DIR);
458
+ const missingAfterCopy = await this.missingScriptsAsync(this.SERVICES_DIR);
459
+ if (missingAfterCopy.length > 0) {
460
+ throw new Error(`Installed services missing required service scripts: ${missingAfterCopy.join(", ")}`);
461
+ }
462
+ const files = await fs3.readdir(this.SERVICES_DIR);
444
463
  for (const file of files) {
445
464
  const filePath = path3.join(this.SERVICES_DIR, file);
446
- const stat = await fs2.stat(filePath);
465
+ const stat = await fs3.stat(filePath);
447
466
  if (stat.isFile() && (file.endsWith(".py") || file.endsWith(".sh"))) {
448
- await fs2.chmod(filePath, 493);
467
+ await fs3.chmod(filePath, 493);
449
468
  }
450
469
  }
451
470
  await this.saveVersion();
@@ -492,11 +511,11 @@ var init_service_installer = __esm({
492
511
  */
493
512
  static async isInstalled() {
494
513
  try {
495
- const exists = await fs2.pathExists(this.SERVICES_DIR);
514
+ const exists = await fs3.pathExists(this.SERVICES_DIR);
496
515
  if (!exists) {
497
516
  return false;
498
517
  }
499
- const files = await fs2.readdir(this.SERVICES_DIR);
518
+ const files = await fs3.readdir(this.SERVICES_DIR);
500
519
  return files.length > 0;
501
520
  } catch {
502
521
  return false;
@@ -519,11 +538,11 @@ var init_service_installer = __esm({
519
538
  */
520
539
  static async listServices() {
521
540
  try {
522
- const exists = await fs2.pathExists(this.SERVICES_DIR);
541
+ const exists = await fs3.pathExists(this.SERVICES_DIR);
523
542
  if (!exists) {
524
543
  return [];
525
544
  }
526
- const files = await fs2.readdir(this.SERVICES_DIR);
545
+ const files = await fs3.readdir(this.SERVICES_DIR);
527
546
  return files.filter((file) => file.endsWith(".py") || file.endsWith(".sh"));
528
547
  } catch {
529
548
  return [];
@@ -534,9 +553,9 @@ var init_service_installer = __esm({
534
553
  */
535
554
  static async uninstall() {
536
555
  try {
537
- const exists = await fs2.pathExists(this.SERVICES_DIR);
556
+ const exists = await fs3.pathExists(this.SERVICES_DIR);
538
557
  if (exists) {
539
- await fs2.remove(this.SERVICES_DIR);
558
+ await fs3.remove(this.SERVICES_DIR);
540
559
  console.log("\u2713 Services uninstalled");
541
560
  }
542
561
  } catch (error) {
@@ -1112,17 +1131,17 @@ async function ensureHooksConfig(baseDir) {
1112
1131
  try {
1113
1132
  const configDir = path3.join(baseDir, ".juno_task");
1114
1133
  const configPath = path3.join(configDir, "config.json");
1115
- await fs2.ensureDir(configDir);
1116
- const configExists = await fs2.pathExists(configPath);
1134
+ await fs3.ensureDir(configDir);
1135
+ const configExists = await fs3.pathExists(configPath);
1117
1136
  const allHookTypes = getDefaultHooks();
1118
1137
  if (!configExists) {
1119
1138
  const defaultConfig = {
1120
1139
  ...DEFAULT_CONFIG,
1121
1140
  hooks: allHookTypes
1122
1141
  };
1123
- await fs2.writeJson(configPath, defaultConfig, { spaces: 2 });
1142
+ await fs3.writeJson(configPath, defaultConfig, { spaces: 2 });
1124
1143
  } else {
1125
- const existingConfig = await fs2.readJson(configPath);
1144
+ const existingConfig = await fs3.readJson(configPath);
1126
1145
  let needsUpdate = false;
1127
1146
  if (!existingConfig.hooks) {
1128
1147
  existingConfig.hooks = allHookTypes;
@@ -1140,7 +1159,7 @@ async function ensureHooksConfig(baseDir) {
1140
1159
  needsUpdate = true;
1141
1160
  }
1142
1161
  if (needsUpdate) {
1143
- await fs2.writeJson(configPath, existingConfig, { spaces: 2 });
1162
+ await fs3.writeJson(configPath, existingConfig, { spaces: 2 });
1144
1163
  }
1145
1164
  }
1146
1165
  } catch (error) {
@@ -1750,6 +1769,12 @@ ${helpText}
1750
1769
  }
1751
1770
  }
1752
1771
  if (options.maxIterations !== void 0) {
1772
+ if (Number.isNaN(options.maxIterations)) {
1773
+ throw new ValidationError(
1774
+ "Max iterations must be a valid number",
1775
+ ["Use -1 for unlimited iterations", "Use positive integers like 1, 5, or 10", "Example: -i 5"]
1776
+ );
1777
+ }
1753
1778
  if (options.maxIterations !== -1 && options.maxIterations < 1) {
1754
1779
  throw new ValidationError(
1755
1780
  "Max iterations must be -1 (unlimited) or a positive number",
@@ -1758,8 +1783,8 @@ ${helpText}
1758
1783
  }
1759
1784
  }
1760
1785
  if (options.cwd) {
1761
- const fs21 = await import('fs-extra');
1762
- if (!await fs21.pathExists(options.cwd)) {
1786
+ const fs22 = await import('fs-extra');
1787
+ if (!await fs22.pathExists(options.cwd)) {
1763
1788
  throw new ValidationError(
1764
1789
  `Working directory does not exist: ${options.cwd}`,
1765
1790
  ["Verify the path exists", "Use absolute paths to avoid ambiguity"]
@@ -3843,8 +3868,8 @@ var init_engine = __esm({
3843
3868
  if (!request.workingDirectory?.trim()) {
3844
3869
  throw new Error("Working directory is required");
3845
3870
  }
3846
- if (request.maxIterations < -1 || request.maxIterations === 0) {
3847
- throw new Error("Max iterations must be positive or -1 for unlimited");
3871
+ if (Number.isNaN(request.maxIterations) || request.maxIterations < -1 || request.maxIterations === 0) {
3872
+ throw new Error("Max iterations must be a positive number or -1 for unlimited");
3848
3873
  }
3849
3874
  }
3850
3875
  /**
@@ -4854,7 +4879,7 @@ var init_config2 = __esm({
4854
4879
  return cached;
4855
4880
  }
4856
4881
  try {
4857
- const configContent = await fs2.readFile(configPath, "utf-8");
4882
+ const configContent = await fs3.readFile(configPath, "utf-8");
4858
4883
  const config = JSON.parse(configContent);
4859
4884
  this.validateConfig(config);
4860
4885
  const resolvedConfig = this.resolveConfigPaths(config, path3.dirname(configPath));
@@ -4876,7 +4901,7 @@ var init_config2 = __esm({
4876
4901
  const rootDir = path3.parse(currentDir).root;
4877
4902
  while (currentDir !== rootDir) {
4878
4903
  const configPath = path3.join(currentDir, ".juno_task", "mcp.json");
4879
- if (await fs2.pathExists(configPath)) {
4904
+ if (await fs3.pathExists(configPath)) {
4880
4905
  return configPath;
4881
4906
  }
4882
4907
  currentDir = path3.dirname(currentDir);
@@ -5062,7 +5087,7 @@ var init_logger = __esm({
5062
5087
  * Matches Python's generate_log_file function
5063
5088
  */
5064
5089
  async initialize(subagent = "mcp") {
5065
- await fs2.ensureDir(this.logDirectory);
5090
+ await fs3.ensureDir(this.logDirectory);
5066
5091
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").split("T");
5067
5092
  const dateStr = timestamp[0];
5068
5093
  const timeStr = timestamp[1].split("-")[0].substring(0, 6);
@@ -5071,7 +5096,7 @@ var init_logger = __esm({
5071
5096
  this.logFilePath = path3__default.join(this.logDirectory, logFileName);
5072
5097
  const header = `# Juno-Task TypeScript Log - Started ${(/* @__PURE__ */ new Date()).toISOString()}
5073
5098
  `;
5074
- await fs2.writeFile(this.logFilePath, header);
5099
+ await fs3.writeFile(this.logFilePath, header);
5075
5100
  }
5076
5101
  /**
5077
5102
  * Format log message matching Python version format
@@ -5089,7 +5114,7 @@ var init_logger = __esm({
5089
5114
  if (!this.logFilePath) {
5090
5115
  throw new Error("Logger not initialized. Call initialize() first.");
5091
5116
  }
5092
- await fs2.appendFile(this.logFilePath, message);
5117
+ await fs3.appendFile(this.logFilePath, message);
5093
5118
  }
5094
5119
  /**
5095
5120
  * Log message at specified level
@@ -7100,7 +7125,7 @@ var init_shell_backend = __esm({
7100
7125
  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");
7101
7126
  const logDir = path3.join(this.config.workingDirectory, ".juno_task", "logs");
7102
7127
  try {
7103
- await fs2.ensureDir(logDir);
7128
+ await fs3.ensureDir(logDir);
7104
7129
  } catch (error) {
7105
7130
  if (this.config?.debug) {
7106
7131
  engineLogger.warn(`Failed to create log directory: ${error instanceof Error ? error.message : String(error)}`);
@@ -7125,7 +7150,7 @@ var init_shell_backend = __esm({
7125
7150
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
7126
7151
  const logEntry = `[${timestamp}] ${message}
7127
7152
  `;
7128
- await fs2.appendFile(this.logFilePath, logEntry, "utf-8");
7153
+ await fs3.appendFile(this.logFilePath, logEntry, "utf-8");
7129
7154
  } catch (error) {
7130
7155
  if (this.config?.debug) {
7131
7156
  engineLogger.warn(`Failed to write to log file: ${error instanceof Error ? error.message : String(error)}`);
@@ -7199,6 +7224,7 @@ var init_shell_backend = __esm({
7199
7224
  return new Promise(async (resolve9, reject) => {
7200
7225
  const startTime = Date.now();
7201
7226
  const isPython = scriptPath.endsWith(".py");
7227
+ const isGemini = subagentType === "gemini";
7202
7228
  const env2 = {
7203
7229
  ...process.env,
7204
7230
  ...this.config.environment,
@@ -7209,6 +7235,9 @@ var init_shell_backend = __esm({
7209
7235
  JUNO_ITERATION: String(request.arguments?.iteration || 1),
7210
7236
  JUNO_TOOL_ID: toolId
7211
7237
  };
7238
+ if (isGemini) {
7239
+ env2.GEMINI_OUTPUT_FORMAT = env2.GEMINI_OUTPUT_FORMAT || "stream-json";
7240
+ }
7212
7241
  let captureDir = null;
7213
7242
  let capturePath = null;
7214
7243
  if (subagentType === "claude") {
@@ -7230,6 +7259,9 @@ var init_shell_backend = __esm({
7230
7259
  if (isPython && request.arguments?.model) {
7231
7260
  args.push("-m", request.arguments.model);
7232
7261
  }
7262
+ if (isPython && isGemini) {
7263
+ args.push("--output-format", env2.GEMINI_OUTPUT_FORMAT || "stream-json");
7264
+ }
7233
7265
  if (isPython && request.arguments?.agents) {
7234
7266
  args.push("--agents", request.arguments.agents);
7235
7267
  }
@@ -7434,7 +7466,7 @@ var init_shell_backend = __esm({
7434
7466
  * Strategy:
7435
7467
  * 1. Try to parse each line as JSON first (for Claude)
7436
7468
  * 2. If JSON parsing fails, treat as TEXT streaming (for Codex and other text-based subagents)
7437
- * 3. Emit all non-empty lines as progress events for real-time display
7469
+ * 3. Emit all text lines (including whitespace-only) as progress events for real-time display
7438
7470
  */
7439
7471
  parseAndEmitStreamingEvents(data, sessionId) {
7440
7472
  if (!this.jsonBuffer) {
@@ -7444,14 +7476,35 @@ var init_shell_backend = __esm({
7444
7476
  const lines = this.jsonBuffer.split("\n");
7445
7477
  this.jsonBuffer = lines.pop() || "";
7446
7478
  for (const line of lines) {
7447
- const trimmedLine = line.trim();
7448
- if (!trimmedLine) continue;
7479
+ const rawLine = line.endsWith("\r") ? line.slice(0, -1) : line;
7480
+ if (!rawLine) continue;
7481
+ const hasNonWhitespace = rawLine.trim().length > 0;
7482
+ if (!hasNonWhitespace) {
7483
+ this.emitProgressEvent({
7484
+ sessionId,
7485
+ timestamp: /* @__PURE__ */ new Date(),
7486
+ backend: "shell",
7487
+ count: ++this.eventCounter,
7488
+ type: "thinking",
7489
+ content: rawLine,
7490
+ metadata: {
7491
+ format: "text",
7492
+ raw: true
7493
+ }
7494
+ }).catch((error) => {
7495
+ if (this.config?.debug) {
7496
+ engineLogger.warn(`Failed to emit whitespace-only streaming event: ${error instanceof Error ? error.message : String(error)}`);
7497
+ }
7498
+ });
7499
+ continue;
7500
+ }
7501
+ const trimmedLine = rawLine.trim();
7449
7502
  let isJsonParsed = false;
7450
7503
  try {
7451
7504
  const jsonEvent = JSON.parse(trimmedLine);
7452
7505
  let progressEvent;
7453
7506
  if (this.isClaudeCliEvent(jsonEvent)) {
7454
- progressEvent = this.convertClaudeEventToProgress(jsonEvent, sessionId, trimmedLine);
7507
+ progressEvent = this.convertClaudeEventToProgress(jsonEvent, sessionId, rawLine);
7455
7508
  isJsonParsed = true;
7456
7509
  } else if (this.isGenericStreamingEvent(jsonEvent)) {
7457
7510
  progressEvent = {
@@ -7486,7 +7539,7 @@ var init_shell_backend = __esm({
7486
7539
  backend: "shell",
7487
7540
  count: ++this.eventCounter,
7488
7541
  type: "thinking",
7489
- content: trimmedLine,
7542
+ content: rawLine,
7490
7543
  metadata: {
7491
7544
  format: "text",
7492
7545
  raw: true
@@ -12753,12 +12806,18 @@ async function mainCommandHandler(args, options, command) {
12753
12806
  console.error(chalk15.red("\n\u274C Error: --allowed-tools and --append-allowed-tools are mutually exclusive. Use one or the other."));
12754
12807
  process.exit(1);
12755
12808
  }
12809
+ if (options.maxIterations !== void 0 && Number.isNaN(options.maxIterations)) {
12810
+ throw new ValidationError(
12811
+ "Max iterations must be a valid number",
12812
+ ["Use -1 for unlimited iterations", "Use positive integers like 1, 5, or 10", "Example: -i 5"]
12813
+ );
12814
+ }
12756
12815
  const executionRequest = createExecutionRequest({
12757
12816
  instruction,
12758
12817
  subagent: options.subagent,
12759
12818
  backend: selectedBackend,
12760
12819
  workingDirectory: config.workingDirectory,
12761
- maxIterations: options.maxIterations || config.defaultMaxIterations,
12820
+ maxIterations: options.maxIterations ?? config.defaultMaxIterations,
12762
12821
  model: options.model || config.defaultModel,
12763
12822
  agents: options.agents,
12764
12823
  tools: options.tools,
@@ -12923,7 +12982,7 @@ var init_main = __esm({
12923
12982
  return await this.collectInteractivePrompt();
12924
12983
  } else {
12925
12984
  const defaultPromptPath = path3.join(process.cwd(), ".juno_task", "prompt.md");
12926
- if (await fs2.pathExists(defaultPromptPath)) {
12985
+ if (await fs3.pathExists(defaultPromptPath)) {
12927
12986
  console.error(chalk15.blue(`\u{1F4C4} Using default prompt: ${chalk15.cyan(".juno_task/prompt.md")}`));
12928
12987
  return await this.loadPromptFromFile(defaultPromptPath);
12929
12988
  } else {
@@ -12951,7 +13010,7 @@ var init_main = __esm({
12951
13010
  }
12952
13011
  try {
12953
13012
  const resolvedPath = path3.resolve(prompt);
12954
- return await fs2.pathExists(resolvedPath);
13013
+ return await fs3.pathExists(resolvedPath);
12955
13014
  } catch {
12956
13015
  return false;
12957
13016
  }
@@ -12959,7 +13018,7 @@ var init_main = __esm({
12959
13018
  async loadPromptFromFile(filePath) {
12960
13019
  try {
12961
13020
  const resolvedPath = path3.resolve(filePath);
12962
- const content = await fs2.readFile(resolvedPath, "utf-8");
13021
+ const content = await fs3.readFile(resolvedPath, "utf-8");
12963
13022
  if (!content.trim()) {
12964
13023
  throw new FileSystemError(
12965
13024
  "Prompt file is empty",
@@ -13281,6 +13340,221 @@ var init_main = __esm({
13281
13340
  }
13282
13341
  });
13283
13342
 
13343
+ // src/utils/script-installer.ts
13344
+ var script_installer_exports = {};
13345
+ __export(script_installer_exports, {
13346
+ ScriptInstaller: () => ScriptInstaller
13347
+ });
13348
+ var ScriptInstaller;
13349
+ var init_script_installer = __esm({
13350
+ "src/utils/script-installer.ts"() {
13351
+ init_version();
13352
+ ScriptInstaller = class {
13353
+ /**
13354
+ * Scripts that should be auto-installed if missing
13355
+ * These are critical scripts that users expect to be available
13356
+ */
13357
+ /**
13358
+ * Required scripts include both standalone scripts and their dependencies.
13359
+ * kanban.sh depends on install_requirements.sh for Python venv setup.
13360
+ */
13361
+ static REQUIRED_SCRIPTS = [
13362
+ "run_until_completion.sh",
13363
+ "kanban.sh",
13364
+ "install_requirements.sh"
13365
+ // Required by kanban.sh for Python venv creation
13366
+ ];
13367
+ /**
13368
+ * Get the templates scripts directory from the package
13369
+ */
13370
+ static getPackageScriptsDir() {
13371
+ const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
13372
+ const candidates = [
13373
+ path3.join(__dirname2, "..", "..", "templates", "scripts"),
13374
+ // dist (production)
13375
+ path3.join(__dirname2, "..", "templates", "scripts")
13376
+ // src (development)
13377
+ ];
13378
+ for (const scriptsPath of candidates) {
13379
+ if (fs3.existsSync(scriptsPath)) {
13380
+ return scriptsPath;
13381
+ }
13382
+ }
13383
+ if (process.env.JUNO_CODE_DEBUG === "1") {
13384
+ console.error("[DEBUG] ScriptInstaller: Could not find templates/scripts directory");
13385
+ console.error("[DEBUG] Tried:", candidates);
13386
+ }
13387
+ return null;
13388
+ }
13389
+ /**
13390
+ * Check if a specific script exists in the project's .juno_task/scripts/ directory
13391
+ */
13392
+ static async scriptExists(projectDir, scriptName) {
13393
+ const scriptPath = path3.join(projectDir, ".juno_task", "scripts", scriptName);
13394
+ return fs3.pathExists(scriptPath);
13395
+ }
13396
+ /**
13397
+ * Install a specific script to the project's .juno_task/scripts/ directory
13398
+ * @param projectDir - The project root directory
13399
+ * @param scriptName - Name of the script to install (e.g., 'run_until_completion.sh')
13400
+ * @param silent - If true, suppresses console output
13401
+ * @returns true if script was installed, false if installation was skipped or failed
13402
+ */
13403
+ static async installScript(projectDir, scriptName, silent = false) {
13404
+ try {
13405
+ const packageScriptsDir = this.getPackageScriptsDir();
13406
+ if (!packageScriptsDir) {
13407
+ if (!silent && process.env.JUNO_CODE_DEBUG === "1") {
13408
+ console.error("[DEBUG] ScriptInstaller: Package scripts directory not found");
13409
+ }
13410
+ return false;
13411
+ }
13412
+ const sourcePath = path3.join(packageScriptsDir, scriptName);
13413
+ if (!await fs3.pathExists(sourcePath)) {
13414
+ if (!silent && process.env.JUNO_CODE_DEBUG === "1") {
13415
+ console.error(`[DEBUG] ScriptInstaller: Source script not found: ${sourcePath}`);
13416
+ }
13417
+ return false;
13418
+ }
13419
+ const destDir = path3.join(projectDir, ".juno_task", "scripts");
13420
+ await fs3.ensureDir(destDir);
13421
+ const destPath = path3.join(destDir, scriptName);
13422
+ await fs3.copy(sourcePath, destPath, { overwrite: true });
13423
+ if (scriptName.endsWith(".sh") || scriptName.endsWith(".py")) {
13424
+ await fs3.chmod(destPath, 493);
13425
+ }
13426
+ if (!silent) {
13427
+ console.log(`\u2713 Installed script: ${scriptName} to .juno_task/scripts/`);
13428
+ }
13429
+ if (process.env.JUNO_CODE_DEBUG === "1") {
13430
+ console.error(`[DEBUG] ScriptInstaller: Installed ${scriptName} to ${destPath}`);
13431
+ }
13432
+ return true;
13433
+ } catch (error) {
13434
+ if (!silent && process.env.JUNO_CODE_DEBUG === "1") {
13435
+ console.error(`[DEBUG] ScriptInstaller: Failed to install ${scriptName}:`, error);
13436
+ }
13437
+ return false;
13438
+ }
13439
+ }
13440
+ /**
13441
+ * Check which required scripts are missing from the project
13442
+ * @param projectDir - The project root directory
13443
+ * @returns Array of missing script names
13444
+ */
13445
+ static async getMissingScripts(projectDir) {
13446
+ const missing = [];
13447
+ for (const script of this.REQUIRED_SCRIPTS) {
13448
+ if (!await this.scriptExists(projectDir, script)) {
13449
+ missing.push(script);
13450
+ }
13451
+ }
13452
+ return missing;
13453
+ }
13454
+ /**
13455
+ * Auto-install any missing required scripts
13456
+ * This should be called on CLI startup for initialized projects
13457
+ * @param projectDir - The project root directory
13458
+ * @param silent - If true, suppresses console output
13459
+ * @returns true if any scripts were installed
13460
+ */
13461
+ static async autoInstallMissing(projectDir, silent = true) {
13462
+ try {
13463
+ const junoTaskDir = path3.join(projectDir, ".juno_task");
13464
+ if (!await fs3.pathExists(junoTaskDir)) {
13465
+ return false;
13466
+ }
13467
+ const missing = await this.getMissingScripts(projectDir);
13468
+ if (missing.length === 0) {
13469
+ return false;
13470
+ }
13471
+ if (process.env.JUNO_CODE_DEBUG === "1") {
13472
+ console.error(`[DEBUG] ScriptInstaller: Missing scripts: ${missing.join(", ")}`);
13473
+ }
13474
+ let installedAny = false;
13475
+ for (const script of missing) {
13476
+ const installed = await this.installScript(projectDir, script, silent);
13477
+ if (installed) {
13478
+ installedAny = true;
13479
+ }
13480
+ }
13481
+ if (installedAny && !silent) {
13482
+ console.log(`\u2713 Auto-installed ${missing.length} missing script(s)`);
13483
+ }
13484
+ return installedAny;
13485
+ } catch (error) {
13486
+ if (process.env.JUNO_CODE_DEBUG === "1") {
13487
+ console.error("[DEBUG] ScriptInstaller: autoInstallMissing error:", error);
13488
+ }
13489
+ return false;
13490
+ }
13491
+ }
13492
+ /**
13493
+ * Update a script if the package version is newer (by content comparison)
13494
+ * @param projectDir - The project root directory
13495
+ * @param scriptName - Name of the script to update
13496
+ * @param silent - If true, suppresses console output
13497
+ * @returns true if script was updated
13498
+ */
13499
+ static async updateScriptIfNewer(projectDir, scriptName, silent = true) {
13500
+ try {
13501
+ const packageScriptsDir = this.getPackageScriptsDir();
13502
+ if (!packageScriptsDir) {
13503
+ return false;
13504
+ }
13505
+ const sourcePath = path3.join(packageScriptsDir, scriptName);
13506
+ const destPath = path3.join(projectDir, ".juno_task", "scripts", scriptName);
13507
+ if (!await fs3.pathExists(destPath)) {
13508
+ return this.installScript(projectDir, scriptName, silent);
13509
+ }
13510
+ const [sourceContent, destContent] = await Promise.all([
13511
+ fs3.readFile(sourcePath, "utf-8"),
13512
+ fs3.readFile(destPath, "utf-8")
13513
+ ]);
13514
+ if (sourceContent !== destContent) {
13515
+ await fs3.copy(sourcePath, destPath, { overwrite: true });
13516
+ if (scriptName.endsWith(".sh") || scriptName.endsWith(".py")) {
13517
+ await fs3.chmod(destPath, 493);
13518
+ }
13519
+ if (!silent) {
13520
+ console.log(`\u2713 Updated script: ${scriptName}`);
13521
+ }
13522
+ if (process.env.JUNO_CODE_DEBUG === "1") {
13523
+ console.error(`[DEBUG] ScriptInstaller: Updated ${scriptName} (content changed)`);
13524
+ }
13525
+ return true;
13526
+ }
13527
+ return false;
13528
+ } catch (error) {
13529
+ if (process.env.JUNO_CODE_DEBUG === "1") {
13530
+ console.error(`[DEBUG] ScriptInstaller: updateScriptIfNewer error for ${scriptName}:`, error);
13531
+ }
13532
+ return false;
13533
+ }
13534
+ }
13535
+ /**
13536
+ * Get the path to a script in the project's .juno_task/scripts/ directory
13537
+ */
13538
+ static getScriptPath(projectDir, scriptName) {
13539
+ return path3.join(projectDir, ".juno_task", "scripts", scriptName);
13540
+ }
13541
+ /**
13542
+ * List all required scripts and their installation status
13543
+ */
13544
+ static async listRequiredScripts(projectDir) {
13545
+ const results = [];
13546
+ for (const script of this.REQUIRED_SCRIPTS) {
13547
+ results.push({
13548
+ name: script,
13549
+ installed: await this.scriptExists(projectDir, script)
13550
+ });
13551
+ }
13552
+ return results;
13553
+ }
13554
+ };
13555
+ }
13556
+ });
13557
+
13284
13558
  // src/utils/startup-validation.ts
13285
13559
  var startup_validation_exports = {};
13286
13560
  __export(startup_validation_exports, {
@@ -13371,7 +13645,7 @@ async function validateJSONFile(configSchema, baseDir) {
13371
13645
  const warnings = [];
13372
13646
  const filePath = path3__default.join(baseDir, configSchema.file);
13373
13647
  try {
13374
- const exists = await fs2.pathExists(filePath);
13648
+ const exists = await fs3.pathExists(filePath);
13375
13649
  if (!exists) {
13376
13650
  if (configSchema.required) {
13377
13651
  errors.push({
@@ -13394,7 +13668,7 @@ async function validateJSONFile(configSchema, baseDir) {
13394
13668
  return { isValid: !configSchema.required, errors, warnings };
13395
13669
  }
13396
13670
  try {
13397
- await fs2.access(filePath, fs2.constants.R_OK);
13671
+ await fs3.access(filePath, fs3.constants.R_OK);
13398
13672
  } catch (accessError) {
13399
13673
  errors.push({
13400
13674
  file: configSchema.file,
@@ -13410,7 +13684,7 @@ async function validateJSONFile(configSchema, baseDir) {
13410
13684
  }
13411
13685
  let jsonData;
13412
13686
  try {
13413
- const fileContent = await fs2.readFile(filePath, "utf8");
13687
+ const fileContent = await fs3.readFile(filePath, "utf8");
13414
13688
  jsonData = JSON.parse(fileContent);
13415
13689
  } catch (parseError) {
13416
13690
  const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
@@ -13545,7 +13819,7 @@ function displayValidationResults(result) {
13545
13819
  }
13546
13820
  async function logValidationResults(result, baseDir = process.cwd()) {
13547
13821
  const logDir = path3__default.join(baseDir, ".juno_task", "logs");
13548
- await fs2.ensureDir(logDir);
13822
+ await fs3.ensureDir(logDir);
13549
13823
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
13550
13824
  const logFile = path3__default.join(logDir, `startup-validation-${timestamp}.log`);
13551
13825
  const logContent = [
@@ -13588,7 +13862,7 @@ async function logValidationResults(result, baseDir = process.cwd()) {
13588
13862
  logContent.push(``);
13589
13863
  }
13590
13864
  }
13591
- await fs2.writeFile(logFile, logContent.join("\n"));
13865
+ await fs3.writeFile(logFile, logContent.join("\n"));
13592
13866
  return logFile;
13593
13867
  }
13594
13868
  async function validateStartupConfigs(baseDir = process.cwd(), verbose = false) {
@@ -13922,7 +14196,7 @@ var TemplateEngine = class {
13922
14196
  let overallError;
13923
14197
  try {
13924
14198
  if (!options.dryRun) {
13925
- await fs2.ensureDir(targetDirectory);
14199
+ await fs3.ensureDir(targetDirectory);
13926
14200
  }
13927
14201
  for (const template of templates) {
13928
14202
  try {
@@ -15272,7 +15546,7 @@ This directory contains specification documents for your project.
15272
15546
  const fileName = this.getTemplateFileName(template);
15273
15547
  const targetPath = path3.join(targetDirectory, fileName);
15274
15548
  try {
15275
- const fileExists = await fs2.pathExists(targetPath);
15549
+ const fileExists = await fs3.pathExists(targetPath);
15276
15550
  if (fileExists && !options.force && options.onConflict !== "overwrite") {
15277
15551
  return {
15278
15552
  path: targetPath,
@@ -15294,10 +15568,10 @@ This directory contains specification documents for your project.
15294
15568
  }
15295
15569
  if (options.createBackup && fileExists) {
15296
15570
  const backupPath = `${targetPath}.backup.${Date.now()}`;
15297
- await fs2.copy(targetPath, backupPath);
15571
+ await fs3.copy(targetPath, backupPath);
15298
15572
  }
15299
- await fs2.ensureDir(path3.dirname(targetPath));
15300
- await fs2.writeFile(targetPath, renderedContent, "utf8");
15573
+ await fs3.ensureDir(path3.dirname(targetPath));
15574
+ await fs3.writeFile(targetPath, renderedContent, "utf8");
15301
15575
  return {
15302
15576
  path: targetPath,
15303
15577
  content: renderedContent,
@@ -15561,7 +15835,7 @@ var SimpleInitTUI = class {
15561
15835
  }
15562
15836
  async confirmSave(targetDirectory) {
15563
15837
  const junoTaskPath = path3.join(targetDirectory, ".juno_task");
15564
- if (await fs2.pathExists(junoTaskPath)) {
15838
+ if (await fs3.pathExists(junoTaskPath)) {
15565
15839
  console.log(chalk15.yellow(" \u26A0\uFE0F .juno_task directory already exists"));
15566
15840
  console.log(chalk15.gray(" Would you like to:"));
15567
15841
  console.log(chalk15.gray(" 1) Override existing files"));
@@ -15606,16 +15880,16 @@ var SimpleProjectGenerator = class {
15606
15880
  async generate() {
15607
15881
  const { targetDirectory, variables, force } = this.context;
15608
15882
  console.log(chalk15.blue("\u{1F4C1} Creating project directory..."));
15609
- await fs2.ensureDir(targetDirectory);
15883
+ await fs3.ensureDir(targetDirectory);
15610
15884
  const junoTaskDir = path3.join(targetDirectory, ".juno_task");
15611
- const junoTaskExists = await fs2.pathExists(junoTaskDir);
15885
+ const junoTaskExists = await fs3.pathExists(junoTaskDir);
15612
15886
  if (junoTaskExists && !force) {
15613
15887
  throw new ValidationError(
15614
15888
  "Project already initialized. Directory .juno_task already exists.",
15615
15889
  ["Use --force flag to overwrite existing files", "Choose a different directory"]
15616
15890
  );
15617
15891
  }
15618
- await fs2.ensureDir(junoTaskDir);
15892
+ await fs3.ensureDir(junoTaskDir);
15619
15893
  console.log(chalk15.blue("\u2699\uFE0F Creating project configuration..."));
15620
15894
  await this.createConfigFile(junoTaskDir, targetDirectory);
15621
15895
  console.log(chalk15.blue("\u{1F527} Setting up MCP configuration..."));
@@ -15649,21 +15923,21 @@ var SimpleProjectGenerator = class {
15649
15923
  const promptContent = await templateEngine.render(promptTemplate, templateContext);
15650
15924
  const initContent = await templateEngine.render(initTemplate, templateContext);
15651
15925
  const implementContent = await templateEngine.render(implementTemplate, templateContext);
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);
15926
+ await fs3.writeFile(path3.join(junoTaskDir, "prompt.md"), promptContent);
15927
+ await fs3.writeFile(path3.join(junoTaskDir, "init.md"), initContent);
15928
+ await fs3.writeFile(path3.join(junoTaskDir, "implement.md"), implementContent);
15655
15929
  const userFeedbackTemplate = templateEngine.getBuiltInTemplate("USER_FEEDBACK.md");
15656
15930
  if (userFeedbackTemplate) {
15657
15931
  const userFeedbackContent = await templateEngine.render(userFeedbackTemplate, templateContext);
15658
- await fs2.writeFile(path3.join(junoTaskDir, "USER_FEEDBACK.md"), userFeedbackContent);
15932
+ await fs3.writeFile(path3.join(junoTaskDir, "USER_FEEDBACK.md"), userFeedbackContent);
15659
15933
  }
15660
15934
  const planTemplate = templateEngine.getBuiltInTemplate("plan.md");
15661
15935
  if (planTemplate) {
15662
15936
  const planContent = await templateEngine.render(planTemplate, templateContext);
15663
- await fs2.writeFile(path3.join(junoTaskDir, "plan.md"), planContent);
15937
+ await fs3.writeFile(path3.join(junoTaskDir, "plan.md"), planContent);
15664
15938
  }
15665
15939
  const specsDir = path3.join(junoTaskDir, "specs");
15666
- await fs2.ensureDir(specsDir);
15940
+ await fs3.ensureDir(specsDir);
15667
15941
  const specsReadmeContent = `# Project Specifications
15668
15942
 
15669
15943
  This directory contains detailed specifications for the project components.
@@ -15680,7 +15954,7 @@ This directory contains detailed specifications for the project components.
15680
15954
  - Avoid conflicts with existing file names
15681
15955
  - Use \`.md\` extension for all specification files
15682
15956
  `;
15683
- await fs2.writeFile(path3.join(specsDir, "README.md"), specsReadmeContent);
15957
+ await fs3.writeFile(path3.join(specsDir, "README.md"), specsReadmeContent);
15684
15958
  const requirementsContent = `# Requirements Specification
15685
15959
 
15686
15960
  ## Functional Requirements
@@ -15727,7 +16001,7 @@ This directory contains detailed specifications for the project components.
15727
16001
  - Code quality: Clean, maintainable codebase
15728
16002
  - Documentation: Complete and accurate documentation
15729
16003
  `;
15730
- await fs2.writeFile(path3.join(specsDir, "requirements.md"), requirementsContent);
16004
+ await fs3.writeFile(path3.join(specsDir, "requirements.md"), requirementsContent);
15731
16005
  const architectureContent = `# Architecture Specification
15732
16006
 
15733
16007
  ## System Overview
@@ -15809,7 +16083,7 @@ This project uses AI-assisted development with juno-code to achieve: ${variables
15809
16083
  - Performance monitoring
15810
16084
  - Security best practices
15811
16085
  `;
15812
- await fs2.writeFile(path3.join(specsDir, "architecture.md"), architectureContent);
16086
+ await fs3.writeFile(path3.join(specsDir, "architecture.md"), architectureContent);
15813
16087
  const claudeContent = `# Claude Development Session Learnings
15814
16088
 
15815
16089
  ## Project Overview
@@ -15881,7 +16155,7 @@ This file will be updated as development progresses to track:
15881
16155
  - Solutions to complex problems
15882
16156
  - Performance improvements and optimizations
15883
16157
  `;
15884
- await fs2.writeFile(path3.join(targetDirectory, "CLAUDE.md"), claudeContent);
16158
+ await fs3.writeFile(path3.join(targetDirectory, "CLAUDE.md"), claudeContent);
15885
16159
  const agentsContent = `# AI Agent Selection and Performance
15886
16160
 
15887
16161
  ## Available Agents
@@ -15945,7 +16219,7 @@ Track agent performance for:
15945
16219
  4. **Feedback Loop**: Provide feedback to improve agent performance
15946
16220
  5. **Performance Monitoring**: Track and optimize agent usage
15947
16221
  `;
15948
- await fs2.writeFile(path3.join(targetDirectory, "AGENTS.md"), agentsContent);
16222
+ await fs3.writeFile(path3.join(targetDirectory, "AGENTS.md"), agentsContent);
15949
16223
  const readmeContent = `# ${variables.PROJECT_NAME}
15950
16224
 
15951
16225
  ${variables.DESCRIPTION}
@@ -16042,7 +16316,7 @@ ${variables.GIT_URL}` : ""}
16042
16316
  Created with juno-code on ${variables.CURRENT_DATE}
16043
16317
  ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16044
16318
  `;
16045
- await fs2.writeFile(path3.join(targetDirectory, "README.md"), readmeContent);
16319
+ await fs3.writeFile(path3.join(targetDirectory, "README.md"), readmeContent);
16046
16320
  console.log(chalk15.blue("\u{1F4E6} Installing utility scripts..."));
16047
16321
  await this.copyScriptsFromTemplates(junoTaskDir);
16048
16322
  console.log(chalk15.blue("\u{1F40D} Installing Python requirements..."));
@@ -16094,7 +16368,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16094
16368
  hooks: getDefaultHooks()
16095
16369
  };
16096
16370
  const configPath = path3.join(junoTaskDir, "config.json");
16097
- await fs2.writeFile(configPath, JSON.stringify(configContent, null, 2));
16371
+ await fs3.writeFile(configPath, JSON.stringify(configContent, null, 2));
16098
16372
  console.log(chalk15.green(` \u2713 Created .juno_task/config.json with ${this.context.subagent} as default subagent`));
16099
16373
  }
16100
16374
  async createMcpFile(junoTaskDir, targetDirectory) {
@@ -16148,7 +16422,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16148
16422
  }
16149
16423
  };
16150
16424
  const mcpPath = path3.join(junoTaskDir, "mcp.json");
16151
- await fs2.writeFile(mcpPath, JSON.stringify(mcpContent, null, 2));
16425
+ await fs3.writeFile(mcpPath, JSON.stringify(mcpContent, null, 2));
16152
16426
  console.log(chalk15.green(` \u2713 Created .juno_task/mcp.json with roundtable-ai server configuration`));
16153
16427
  }
16154
16428
  getDefaultModelForSubagent(subagent) {
@@ -16167,7 +16441,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16167
16441
  async copyScriptsFromTemplates(junoTaskDir) {
16168
16442
  try {
16169
16443
  const scriptsDir = path3.join(junoTaskDir, "scripts");
16170
- await fs2.ensureDir(scriptsDir);
16444
+ await fs3.ensureDir(scriptsDir);
16171
16445
  const __filename2 = fileURLToPath(import.meta.url);
16172
16446
  const __dirname2 = path3.dirname(__filename2);
16173
16447
  let templatesScriptsDir;
@@ -16178,11 +16452,11 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16178
16452
  } else {
16179
16453
  templatesScriptsDir = path3.join(__dirname2, "../../templates/scripts");
16180
16454
  }
16181
- if (!await fs2.pathExists(templatesScriptsDir)) {
16455
+ if (!await fs3.pathExists(templatesScriptsDir)) {
16182
16456
  console.log(chalk15.yellow(" \u26A0\uFE0F Template scripts directory not found, skipping script installation"));
16183
16457
  return;
16184
16458
  }
16185
- const scriptFiles = await fs2.readdir(templatesScriptsDir);
16459
+ const scriptFiles = await fs3.readdir(templatesScriptsDir);
16186
16460
  if (scriptFiles.length === 0) {
16187
16461
  console.log(chalk15.gray(" \u2139\uFE0F No template scripts found to install"));
16188
16462
  return;
@@ -16191,11 +16465,11 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16191
16465
  for (const scriptFile of scriptFiles) {
16192
16466
  const sourcePath = path3.join(templatesScriptsDir, scriptFile);
16193
16467
  const destPath = path3.join(scriptsDir, scriptFile);
16194
- const stats = await fs2.stat(sourcePath);
16468
+ const stats = await fs3.stat(sourcePath);
16195
16469
  if (stats.isFile()) {
16196
- await fs2.copy(sourcePath, destPath);
16470
+ await fs3.copy(sourcePath, destPath);
16197
16471
  if (scriptFile.endsWith(".sh")) {
16198
- await fs2.chmod(destPath, 493);
16472
+ await fs3.chmod(destPath, 493);
16199
16473
  }
16200
16474
  copiedCount++;
16201
16475
  console.log(chalk15.green(` \u2713 Installed script: ${scriptFile}`));
@@ -16218,7 +16492,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16218
16492
  try {
16219
16493
  const scriptsDir = path3.join(junoTaskDir, "scripts");
16220
16494
  const installScript = path3.join(scriptsDir, "install_requirements.sh");
16221
- if (!await fs2.pathExists(installScript)) {
16495
+ if (!await fs3.pathExists(installScript)) {
16222
16496
  console.log(chalk15.yellow(" \u26A0\uFE0F install_requirements.sh not found, skipping Python dependencies installation"));
16223
16497
  console.log(chalk15.gray(" You can install dependencies manually: juno-kanban, roundtable-ai"));
16224
16498
  return;
@@ -16515,20 +16789,20 @@ init_types();
16515
16789
  async function loadInitPrompt(directory) {
16516
16790
  const junoTaskDir = path3.join(directory, ".juno_task");
16517
16791
  const initFile = path3.join(junoTaskDir, "init.md");
16518
- if (!await fs2.pathExists(junoTaskDir)) {
16792
+ if (!await fs3.pathExists(junoTaskDir)) {
16519
16793
  throw new FileSystemError(
16520
16794
  'No .juno_task directory found. Run "juno-code init" first.',
16521
16795
  junoTaskDir
16522
16796
  );
16523
16797
  }
16524
- if (!await fs2.pathExists(initFile)) {
16798
+ if (!await fs3.pathExists(initFile)) {
16525
16799
  throw new FileSystemError(
16526
16800
  "No init.md file found in .juno_task directory",
16527
16801
  initFile
16528
16802
  );
16529
16803
  }
16530
16804
  try {
16531
- const content = await fs2.readFile(initFile, "utf-8");
16805
+ const content = await fs3.readFile(initFile, "utf-8");
16532
16806
  if (!content.trim()) {
16533
16807
  throw new FileSystemError(
16534
16808
  "init.md file is empty. Please add task instructions.",
@@ -17560,8 +17834,8 @@ Focus on ${request.intelligence === "basic" ? "basic functionality" : request.in
17560
17834
  for (const scenario of scenarios) {
17561
17835
  const testContent = await this.generateTestContent(request, scenario, template);
17562
17836
  const testFilePath = this.resolveTestFilePath(request, scenario);
17563
- await fs2.ensureDir(path3.dirname(testFilePath));
17564
- await fs2.writeFile(testFilePath, testContent);
17837
+ await fs3.ensureDir(path3.dirname(testFilePath));
17838
+ await fs3.writeFile(testFilePath, testContent);
17565
17839
  testFiles.push(testFilePath);
17566
17840
  }
17567
17841
  return testFiles;
@@ -17813,8 +18087,8 @@ var TestExecutionEngine = class {
17813
18087
  async collectCoverage(request) {
17814
18088
  const coverageFile = path3.join(request.workingDirectory, "coverage", "coverage-summary.json");
17815
18089
  try {
17816
- if (await fs2.pathExists(coverageFile)) {
17817
- const coverageData = await fs2.readJson(coverageFile);
18090
+ if (await fs3.pathExists(coverageFile)) {
18091
+ const coverageData = await fs3.readJson(coverageFile);
17818
18092
  return {
17819
18093
  lines: coverageData.total?.lines?.pct || 0,
17820
18094
  functions: coverageData.total?.functions?.pct || 0,
@@ -17997,8 +18271,8 @@ var TestReportEngine = class {
17997
18271
  suggestions: analysis.suggestions,
17998
18272
  recommendations: analysis.recommendations
17999
18273
  };
18000
- await fs2.ensureDir(path3.dirname(outputPath));
18001
- await fs2.writeJson(outputPath, report, { spaces: 2 });
18274
+ await fs3.ensureDir(path3.dirname(outputPath));
18275
+ await fs3.writeJson(outputPath, report, { spaces: 2 });
18002
18276
  return outputPath;
18003
18277
  }
18004
18278
  async generateHTMLReport(analysis, outputPath, includeVisualizations) {
@@ -18061,8 +18335,8 @@ var TestReportEngine = class {
18061
18335
  </body>
18062
18336
  </html>
18063
18337
  `.trim();
18064
- await fs2.ensureDir(path3.dirname(outputPath));
18065
- await fs2.writeFile(outputPath, html);
18338
+ await fs3.ensureDir(path3.dirname(outputPath));
18339
+ await fs3.writeFile(outputPath, html);
18066
18340
  return outputPath;
18067
18341
  }
18068
18342
  generateCharts(analysis) {
@@ -18119,8 +18393,8 @@ ${analysis.suggestions.map((suggestion) => `- ${suggestion}`).join("\n")}
18119
18393
 
18120
18394
  ${analysis.recommendations.map((rec) => `- ${rec}`).join("\n")}
18121
18395
  `.trim();
18122
- await fs2.ensureDir(path3.dirname(outputPath));
18123
- await fs2.writeFile(outputPath, markdown);
18396
+ await fs3.ensureDir(path3.dirname(outputPath));
18397
+ await fs3.writeFile(outputPath, markdown);
18124
18398
  return outputPath;
18125
18399
  }
18126
18400
  async displayConsoleReport(analysis) {
@@ -18460,16 +18734,16 @@ async function compactConfigFile(filePath, options = {}) {
18460
18734
  preserveDays = 30,
18461
18735
  preservePatterns = []
18462
18736
  } = options;
18463
- const originalContent = await fs2.readFile(filePath, "utf-8");
18737
+ const originalContent = await fs3.readFile(filePath, "utf-8");
18464
18738
  const originalSize = originalContent.length;
18465
18739
  let backupPath = "";
18466
18740
  if (createBackup && !dryRun) {
18467
18741
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18468
18742
  const ext = path3.extname(filePath);
18469
18743
  const basename11 = path3.basename(filePath, ext);
18470
- const dirname12 = path3.dirname(filePath);
18471
- backupPath = path3.join(dirname12, `${basename11}.backup.${timestamp}${ext}`);
18472
- await fs2.writeFile(backupPath, originalContent, "utf-8");
18744
+ const dirname13 = path3.dirname(filePath);
18745
+ backupPath = path3.join(dirname13, `${basename11}.backup.${timestamp}${ext}`);
18746
+ await fs3.writeFile(backupPath, originalContent, "utf-8");
18473
18747
  }
18474
18748
  const compactionAnalysis = analyzeMarkdownStructure(originalContent);
18475
18749
  const compactedContent = compactMarkdownContent(
@@ -18484,7 +18758,7 @@ async function compactConfigFile(filePath, options = {}) {
18484
18758
  const compactedSize = finalContent.length;
18485
18759
  const reductionPercentage = Math.round((originalSize - compactedSize) / originalSize * 100);
18486
18760
  if (!dryRun) {
18487
- await fs2.writeFile(filePath, finalContent, "utf-8");
18761
+ await fs3.writeFile(filePath, finalContent, "utf-8");
18488
18762
  }
18489
18763
  return {
18490
18764
  originalSize,
@@ -18614,7 +18888,7 @@ function formatFileSize(bytes) {
18614
18888
  }
18615
18889
  async function shouldCompactFile(filePath, sizeThresholdKB = 50, ageThresholdDays = 30) {
18616
18890
  try {
18617
- const stats = await fs2.stat(filePath);
18891
+ const stats = await fs3.stat(filePath);
18618
18892
  const sizeKB = stats.size / 1024;
18619
18893
  if (sizeKB > sizeThresholdKB) {
18620
18894
  return true;
@@ -18636,19 +18910,19 @@ async function archiveResolvedIssues(options) {
18636
18910
  feedbackFile,
18637
18911
  archiveDir = path3.join(path3.dirname(feedbackFile), "archives"),
18638
18912
  openIssuesThreshold = 10} = options;
18639
- if (!await fs2.pathExists(feedbackFile)) {
18913
+ if (!await fs3.pathExists(feedbackFile)) {
18640
18914
  throw new Error(`Feedback file does not exist: ${feedbackFile}`);
18641
18915
  }
18642
- const content = await fs2.readFile(feedbackFile, "utf-8");
18916
+ const content = await fs3.readFile(feedbackFile, "utf-8");
18643
18917
  const parsed = parseUserFeedback(content);
18644
18918
  const warningsGenerated = [];
18645
18919
  if (parsed.openIssues.length > openIssuesThreshold) {
18646
18920
  const warning = `Found ${parsed.openIssues.length} open issues (threshold: ${openIssuesThreshold}). Consider reviewing and prioritizing.`;
18647
18921
  warningsGenerated.push(warning);
18648
18922
  const logFile = path3.join(path3.dirname(feedbackFile), "logs", "feedback-warnings.log");
18649
- await fs2.ensureDir(path3.dirname(logFile));
18923
+ await fs3.ensureDir(path3.dirname(logFile));
18650
18924
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
18651
- await fs2.appendFile(logFile, `[${timestamp}] ${warning}
18925
+ await fs3.appendFile(logFile, `[${timestamp}] ${warning}
18652
18926
  `);
18653
18927
  }
18654
18928
  if (parsed.resolvedIssues.length === 0) {
@@ -18665,10 +18939,10 @@ async function archiveResolvedIssues(options) {
18665
18939
  const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
18666
18940
  const archiveFile = path3.join(archiveDir, `USER_FEEDBACK_archive_${currentYear}.md`);
18667
18941
  {
18668
- await fs2.ensureDir(archiveDir);
18942
+ await fs3.ensureDir(archiveDir);
18669
18943
  await appendToArchive(archiveFile, parsed.resolvedIssues);
18670
18944
  const compactedContent = generateCompactedFeedback(parsed.openIssues, parsed.metadata);
18671
- await fs2.writeFile(feedbackFile, compactedContent, "utf-8");
18945
+ await fs3.writeFile(feedbackFile, compactedContent, "utf-8");
18672
18946
  }
18673
18947
  {
18674
18948
  console.log(chalk15.green(`\u2705 Archived ${parsed.resolvedIssues.length} resolved issues`));
@@ -18715,8 +18989,8 @@ function parseUserFeedback(content) {
18715
18989
  async function appendToArchive(archiveFile, resolvedIssues) {
18716
18990
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
18717
18991
  let archiveContent = "";
18718
- if (await fs2.pathExists(archiveFile)) {
18719
- archiveContent = await fs2.readFile(archiveFile, "utf-8");
18992
+ if (await fs3.pathExists(archiveFile)) {
18993
+ archiveContent = await fs3.readFile(archiveFile, "utf-8");
18720
18994
  } else {
18721
18995
  const year = path3.basename(archiveFile).match(/(\d{4})/)?.[1] || (/* @__PURE__ */ new Date()).getFullYear();
18722
18996
  archiveContent = `# User Feedback Archive ${year}
@@ -18750,7 +19024,7 @@ ${resolvedIssue}
18750
19024
  `- Last updated: ${timestamp}`
18751
19025
  );
18752
19026
  }
18753
- await fs2.writeFile(archiveFile, archiveContent, "utf-8");
19027
+ await fs3.writeFile(archiveFile, archiveContent, "utf-8");
18754
19028
  }
18755
19029
  function generateCompactedFeedback(openIssues, metadata) {
18756
19030
  let content = metadata.trim() + "\n";
@@ -18775,15 +19049,15 @@ async function shouldArchive(feedbackFile, options = {}) {
18775
19049
  // 50KB
18776
19050
  lineCountThreshold = 500
18777
19051
  } = options;
18778
- if (!await fs2.pathExists(feedbackFile)) {
19052
+ if (!await fs3.pathExists(feedbackFile)) {
18779
19053
  return {
18780
19054
  shouldArchive: false,
18781
19055
  reasons: [],
18782
19056
  stats: { openIssuesCount: 0, resolvedIssuesCount: 0, fileSizeBytes: 0, lineCount: 0 }
18783
19057
  };
18784
19058
  }
18785
- const content = await fs2.readFile(feedbackFile, "utf-8");
18786
- const stats = await fs2.stat(feedbackFile);
19059
+ const content = await fs3.readFile(feedbackFile, "utf-8");
19060
+ const stats = await fs3.stat(feedbackFile);
18787
19061
  const parsed = parseUserFeedback(content);
18788
19062
  const lineCount = content.split("\n").length;
18789
19063
  const reasons = [];
@@ -18857,16 +19131,16 @@ var EnhancedFeedbackFileManager = class {
18857
19131
  this.feedbackFile = feedbackFile;
18858
19132
  }
18859
19133
  async ensureExists() {
18860
- if (!await fs2.pathExists(this.feedbackFile)) {
19134
+ if (!await fs3.pathExists(this.feedbackFile)) {
18861
19135
  await this.createInitialFile();
18862
19136
  }
18863
19137
  }
18864
19138
  async addFeedback(issue, testCriteria) {
18865
19139
  await this.ensureExists();
18866
19140
  try {
18867
- const content = await fs2.readFile(this.feedbackFile, "utf-8");
19141
+ const content = await fs3.readFile(this.feedbackFile, "utf-8");
18868
19142
  const updatedContent = this.addIssueToContent(content, issue, testCriteria);
18869
- await fs2.writeFile(this.feedbackFile, updatedContent, "utf-8");
19143
+ await fs3.writeFile(this.feedbackFile, updatedContent, "utf-8");
18870
19144
  } catch (error) {
18871
19145
  throw new ValidationError(
18872
19146
  `Failed to save feedback: ${error}`,
@@ -18879,12 +19153,12 @@ var EnhancedFeedbackFileManager = class {
18879
19153
  */
18880
19154
  async repairMalformedFile() {
18881
19155
  try {
18882
- const content = await fs2.readFile(this.feedbackFile, "utf-8");
19156
+ const content = await fs3.readFile(this.feedbackFile, "utf-8");
18883
19157
  const hasOpenIssues = content.includes("<OPEN_ISSUES>");
18884
19158
  const hasClosingTag = content.includes("</OPEN_ISSUES>");
18885
19159
  if (!hasOpenIssues || !hasClosingTag) {
18886
19160
  const backupPath = this.feedbackFile + ".backup." + Date.now();
18887
- await fs2.writeFile(backupPath, content, "utf-8");
19161
+ await fs3.writeFile(backupPath, content, "utf-8");
18888
19162
  const existingIssues = this.extractIssuesFromMalformedContent(content);
18889
19163
  await this.createInitialFile(existingIssues);
18890
19164
  }
@@ -18916,8 +19190,8 @@ List any features you'd like to see added or bugs you've encountered.
18916
19190
 
18917
19191
  <!-- Resolved issues will be moved here -->
18918
19192
  `;
18919
- await fs2.ensureDir(path3.dirname(this.feedbackFile));
18920
- await fs2.writeFile(this.feedbackFile, initialContent, "utf-8");
19193
+ await fs3.ensureDir(path3.dirname(this.feedbackFile));
19194
+ await fs3.writeFile(this.feedbackFile, initialContent, "utf-8");
18921
19195
  }
18922
19196
  addIssueToContent(content, issue, testCriteria) {
18923
19197
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -19033,17 +19307,17 @@ async function handleCompactCommand(subArgs, options) {
19033
19307
  if (subArgs.length > 0) {
19034
19308
  for (const filePath of subArgs) {
19035
19309
  const resolvedPath = path3.resolve(filePath);
19036
- if (await fs2.pathExists(resolvedPath)) {
19310
+ if (await fs3.pathExists(resolvedPath)) {
19037
19311
  filesToCompact.push(resolvedPath);
19038
19312
  } else {
19039
19313
  console.warn(chalk15.yellow(`\u26A0\uFE0F File not found: ${filePath}`));
19040
19314
  }
19041
19315
  }
19042
19316
  } else {
19043
- if (await fs2.pathExists(defaultClaudeFile)) {
19317
+ if (await fs3.pathExists(defaultClaudeFile)) {
19044
19318
  filesToCompact.push(defaultClaudeFile);
19045
19319
  }
19046
- if (await fs2.pathExists(defaultAgentsFile)) {
19320
+ if (await fs3.pathExists(defaultAgentsFile)) {
19047
19321
  filesToCompact.push(defaultAgentsFile);
19048
19322
  }
19049
19323
  }
@@ -19977,11 +20251,11 @@ var GitManager = class {
19977
20251
  */
19978
20252
  async updateJunoTaskConfig(gitUrl) {
19979
20253
  const configPath = path3.join(this.workingDirectory, ".juno_task", "init.md");
19980
- if (!await fs2.pathExists(configPath)) {
20254
+ if (!await fs3.pathExists(configPath)) {
19981
20255
  return;
19982
20256
  }
19983
20257
  try {
19984
- let content = await fs2.readFile(configPath, "utf-8");
20258
+ let content = await fs3.readFile(configPath, "utf-8");
19985
20259
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
19986
20260
  if (frontmatterMatch) {
19987
20261
  let frontmatter = frontmatterMatch[1];
@@ -20002,7 +20276,7 @@ GIT_URL: ${gitUrl}
20002
20276
  `;
20003
20277
  content = frontmatter + content;
20004
20278
  }
20005
- await fs2.writeFile(configPath, content, "utf-8");
20279
+ await fs3.writeFile(configPath, content, "utf-8");
20006
20280
  } catch (error) {
20007
20281
  console.warn(`Warning: Failed to update juno-code configuration: ${error}`);
20008
20282
  }
@@ -20934,7 +21208,7 @@ var LogViewer = ({
20934
21208
  init_types();
20935
21209
  async function exportLogs(logger2, filepath, options) {
20936
21210
  try {
20937
- const fs21 = await import('fs-extra');
21211
+ const fs22 = await import('fs-extra');
20938
21212
  let entries = logger2.getRecentEntries(options.tail || 1e3);
20939
21213
  if (options.level) {
20940
21214
  const level = LogLevel[options.level.toUpperCase()];
@@ -20964,7 +21238,7 @@ async function exportLogs(logger2, filepath, options) {
20964
21238
  },
20965
21239
  entries
20966
21240
  };
20967
- await fs21.writeFile(filepath, JSON.stringify(exportData, null, 2));
21241
+ await fs22.writeFile(filepath, JSON.stringify(exportData, null, 2));
20968
21242
  console.log(chalk15.green(`\u2705 Exported ${entries.length} log entries to: ${filepath}`));
20969
21243
  } catch (error) {
20970
21244
  console.error(chalk15.red(`\u274C Failed to export logs: ${error}`));
@@ -22508,7 +22782,7 @@ function createServicesCommand() {
22508
22782
  const servicesCmd = new Command("services").description("Manage juno-code service scripts").addHelpText("after", `
22509
22783
  Examples:
22510
22784
  $ 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)
22785
+ $ juno-code services install --force Reinstall/refresh service scripts (codex.py/claude.py/gemini.py)
22512
22786
  $ juno-code services list List installed service scripts
22513
22787
  $ juno-code services status Check installation status
22514
22788
  $ juno-code services uninstall Remove all service scripts
@@ -22765,10 +23039,10 @@ autoload -U compinit && compinit`;
22765
23039
  */
22766
23040
  async isSourceCommandPresent(configPath, sourceCommand) {
22767
23041
  try {
22768
- if (!await fs2.pathExists(configPath)) {
23042
+ if (!await fs3.pathExists(configPath)) {
22769
23043
  return false;
22770
23044
  }
22771
- const content = await fs2.readFile(configPath, "utf-8");
23045
+ const content = await fs3.readFile(configPath, "utf-8");
22772
23046
  return content.includes("juno-code completion");
22773
23047
  } catch {
22774
23048
  return false;
@@ -22790,15 +23064,15 @@ autoload -U compinit && compinit`;
22790
23064
  continue;
22791
23065
  }
22792
23066
  try {
22793
- const completionExists = await fs2.pathExists(shell.completionPath);
22794
- const configExists = await fs2.pathExists(shell.configPath);
23067
+ const completionExists = await fs3.pathExists(shell.completionPath);
23068
+ const configExists = await fs3.pathExists(shell.configPath);
22795
23069
  const isSourced = configExists && await this.isSourceCommandPresent(
22796
23070
  shell.configPath,
22797
23071
  this.getSourceCommand(shell.name, shell.completionPath)
22798
23072
  );
22799
23073
  let lastInstalled;
22800
23074
  if (completionExists) {
22801
- const stats = await fs2.stat(shell.completionPath);
23075
+ const stats = await fs3.stat(shell.completionPath);
22802
23076
  lastInstalled = stats.mtime;
22803
23077
  }
22804
23078
  statuses.push({
@@ -22824,7 +23098,7 @@ autoload -U compinit && compinit`;
22824
23098
  async ensureCompletionDirectory(shell) {
22825
23099
  const completionPath = this.getCompletionPath(shell);
22826
23100
  const completionDir = path3.dirname(completionPath);
22827
- await fs2.ensureDir(completionDir);
23101
+ await fs3.ensureDir(completionDir);
22828
23102
  }
22829
23103
  /**
22830
23104
  * Ensure directory exists for shell configuration
@@ -22832,7 +23106,7 @@ autoload -U compinit && compinit`;
22832
23106
  async ensureConfigDirectory(shell) {
22833
23107
  const configPath = this.getConfigPath(shell);
22834
23108
  const configDir = path3.dirname(configPath);
22835
- await fs2.ensureDir(configDir);
23109
+ await fs3.ensureDir(configDir);
22836
23110
  }
22837
23111
  /**
22838
23112
  * Get shell version information
@@ -22856,15 +23130,15 @@ autoload -U compinit && compinit`;
22856
23130
  try {
22857
23131
  const configPath = this.getConfigPath(shell);
22858
23132
  const configDir = path3.dirname(configPath);
22859
- await fs2.access(configDir, fs2.constants.W_OK);
23133
+ await fs3.access(configDir, fs3.constants.W_OK);
22860
23134
  } catch {
22861
23135
  issues.push(`Cannot write to ${shell} configuration directory`);
22862
23136
  }
22863
23137
  try {
22864
23138
  const completionPath = this.getCompletionPath(shell);
22865
23139
  const completionDir = path3.dirname(completionPath);
22866
- await fs2.ensureDir(completionDir);
22867
- await fs2.access(completionDir, fs2.constants.W_OK);
23140
+ await fs3.ensureDir(completionDir);
23141
+ await fs3.access(completionDir, fs3.constants.W_OK);
22868
23142
  } catch {
22869
23143
  issues.push(`Cannot write to ${shell} completion directory`);
22870
23144
  }
@@ -22935,10 +23209,10 @@ var ContextAwareCompletion = class {
22935
23209
  async getSessionIds() {
22936
23210
  try {
22937
23211
  const sessionDir = path3.join(process.cwd(), ".juno_task", "sessions");
22938
- if (!await fs2.pathExists(sessionDir)) {
23212
+ if (!await fs3.pathExists(sessionDir)) {
22939
23213
  return [];
22940
23214
  }
22941
- const sessions = await fs2.readdir(sessionDir);
23215
+ const sessions = await fs3.readdir(sessionDir);
22942
23216
  return sessions.filter((name) => name.match(/^session_\d+$/)).map((name) => name.replace("session_", "")).sort((a, b) => parseInt(b) - parseInt(a));
22943
23217
  } catch {
22944
23218
  return [];
@@ -22952,8 +23226,8 @@ var ContextAwareCompletion = class {
22952
23226
  const builtinTemplates = ["basic", "advanced", "research", "development"];
22953
23227
  const customTemplatesDir = path3.join(process.cwd(), ".juno_task", "templates");
22954
23228
  let customTemplates = [];
22955
- if (await fs2.pathExists(customTemplatesDir)) {
22956
- const files = await fs2.readdir(customTemplatesDir);
23229
+ if (await fs3.pathExists(customTemplatesDir)) {
23230
+ const files = await fs3.readdir(customTemplatesDir);
22957
23231
  customTemplates = files.filter((name) => name.endsWith(".md") || name.endsWith(".hbs")).map((name) => path3.basename(name, path3.extname(name)));
22958
23232
  }
22959
23233
  return [...builtinTemplates, ...customTemplates].sort();
@@ -23030,7 +23304,7 @@ var CompletionInstaller = class {
23030
23304
  await this.shellDetector.ensureConfigDirectory(shell);
23031
23305
  const script = this.generateEnhancedCompletion(shell, "juno-code");
23032
23306
  const completionPath = this.shellDetector.getCompletionPath(shell);
23033
- await fs2.writeFile(completionPath, script, "utf-8");
23307
+ await fs3.writeFile(completionPath, script, "utf-8");
23034
23308
  const configPath = this.shellDetector.getConfigPath(shell);
23035
23309
  const sourceCommand = this.shellDetector.getSourceCommand(shell, completionPath);
23036
23310
  const warnings = [];
@@ -23038,7 +23312,7 @@ var CompletionInstaller = class {
23038
23312
  const isPresent = await this.shellDetector.isSourceCommandPresent(configPath, sourceCommand);
23039
23313
  if (!isPresent) {
23040
23314
  try {
23041
- await fs2.appendFile(configPath, `
23315
+ await fs3.appendFile(configPath, `
23042
23316
 
23043
23317
  ${sourceCommand}
23044
23318
  `);
@@ -23069,8 +23343,8 @@ ${sourceCommand}
23069
23343
  async uninstall(shell) {
23070
23344
  try {
23071
23345
  const completionPath = this.shellDetector.getCompletionPath(shell);
23072
- if (await fs2.pathExists(completionPath)) {
23073
- await fs2.remove(completionPath);
23346
+ if (await fs3.pathExists(completionPath)) {
23347
+ await fs3.remove(completionPath);
23074
23348
  return true;
23075
23349
  }
23076
23350
  return false;
@@ -23084,7 +23358,7 @@ ${sourceCommand}
23084
23358
  async isInstalled(shell) {
23085
23359
  try {
23086
23360
  const completionPath = this.shellDetector.getCompletionPath(shell);
23087
- return await fs2.pathExists(completionPath);
23361
+ return await fs3.pathExists(completionPath);
23088
23362
  } catch {
23089
23363
  return false;
23090
23364
  }
@@ -23885,11 +24159,11 @@ function setupMainCommand(program) {
23885
24159
  );
23886
24160
  const allOptions2 = { ...definedGlobalOptions, ...options };
23887
24161
  if (!globalOptions.subagent && !options.prompt && !options.interactive && !options.interactivePrompt) {
23888
- const fs21 = await import('fs-extra');
23889
- const path23 = await import('path');
24162
+ const fs22 = await import('fs-extra');
24163
+ const path24 = await import('path');
23890
24164
  const cwd2 = process.cwd();
23891
- const junoTaskDir = path23.join(cwd2, ".juno_task");
23892
- if (await fs21.pathExists(junoTaskDir)) {
24165
+ const junoTaskDir = path24.join(cwd2, ".juno_task");
24166
+ if (await fs22.pathExists(junoTaskDir)) {
23893
24167
  console.log(chalk15.blue.bold("\u{1F3AF} Juno Code - Auto-detected Initialized Project\n"));
23894
24168
  try {
23895
24169
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -23906,12 +24180,12 @@ function setupMainCommand(program) {
23906
24180
  allOptions2.subagent = config.defaultSubagent;
23907
24181
  console.log(chalk15.gray(`\u{1F916} Using configured subagent: ${chalk15.cyan(config.defaultSubagent)}`));
23908
24182
  }
23909
- const promptFile = path23.join(junoTaskDir, "prompt.md");
23910
- if (!allOptions2.prompt && await fs21.pathExists(promptFile)) {
24183
+ const promptFile = path24.join(junoTaskDir, "prompt.md");
24184
+ if (!allOptions2.prompt && await fs22.pathExists(promptFile)) {
23911
24185
  allOptions2.prompt = promptFile;
23912
24186
  console.log(chalk15.gray(`\u{1F4C4} Using default prompt: ${chalk15.cyan(".juno_task/prompt.md")}`));
23913
24187
  }
23914
- if (allOptions2.subagent && (allOptions2.prompt || await fs21.pathExists(promptFile))) {
24188
+ if (allOptions2.subagent && (allOptions2.prompt || await fs22.pathExists(promptFile))) {
23915
24189
  console.log(chalk15.green("\u2713 Auto-detected project configuration\n"));
23916
24190
  const { mainCommandHandler: mainCommandHandler3 } = await Promise.resolve().then(() => (init_main(), main_exports));
23917
24191
  await mainCommandHandler3([], allOptions2, command);
@@ -24068,6 +24342,17 @@ async function main() {
24068
24342
  console.error("[DEBUG] Service auto-update failed:", error instanceof Error ? error.message : String(error));
24069
24343
  }
24070
24344
  }
24345
+ try {
24346
+ const { ScriptInstaller: ScriptInstaller2 } = await Promise.resolve().then(() => (init_script_installer(), script_installer_exports));
24347
+ const installed = await ScriptInstaller2.autoInstallMissing(process.cwd(), true);
24348
+ if (installed && (process.argv.includes("--verbose") || process.argv.includes("-v") || process.env.JUNO_CODE_DEBUG === "1")) {
24349
+ console.error("[DEBUG] Project scripts auto-installed to .juno_task/scripts/");
24350
+ }
24351
+ } catch (error) {
24352
+ if (process.env.JUNO_CODE_DEBUG === "1") {
24353
+ console.error("[DEBUG] Script auto-install failed:", error instanceof Error ? error.message : String(error));
24354
+ }
24355
+ }
24071
24356
  program.name("juno-code").description("TypeScript implementation of juno-code CLI tool for AI subagent orchestration").version(VERSION, "-V, --version", "Display version information").helpOption("-h, --help", "Display help information");
24072
24357
  setupGlobalOptions(program);
24073
24358
  const isVerbose = process.argv.includes("--verbose") || process.argv.includes("-v");
@@ -24075,10 +24360,10 @@ async function main() {
24075
24360
  const isHelpOrVersion = process.argv.includes("--help") || process.argv.includes("-h") || process.argv.includes("--version") || process.argv.includes("-V");
24076
24361
  const hasNoArguments = process.argv.length <= 2;
24077
24362
  const isInitCommand = process.argv.includes("init");
24078
- const fs21 = await import('fs-extra');
24079
- const path23 = await import('path');
24080
- const junoTaskDir = path23.join(process.cwd(), ".juno_task");
24081
- const isInitialized = await fs21.pathExists(junoTaskDir);
24363
+ const fs22 = await import('fs-extra');
24364
+ const path24 = await import('path');
24365
+ const junoTaskDir = path24.join(process.cwd(), ".juno_task");
24366
+ const isInitialized = await fs22.pathExists(junoTaskDir);
24082
24367
  if (!isHelpOrVersion && !hasNoArguments && !isInitCommand && isInitialized) {
24083
24368
  try {
24084
24369
  const { validateStartupConfigs: validateStartupConfigs2 } = await Promise.resolve().then(() => (init_startup_validation(), startup_validation_exports));