juno-code 1.0.35 → 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
  }
@@ -12774,12 +12806,18 @@ async function mainCommandHandler(args, options, command) {
12774
12806
  console.error(chalk15.red("\n\u274C Error: --allowed-tools and --append-allowed-tools are mutually exclusive. Use one or the other."));
12775
12807
  process.exit(1);
12776
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
+ }
12777
12815
  const executionRequest = createExecutionRequest({
12778
12816
  instruction,
12779
12817
  subagent: options.subagent,
12780
12818
  backend: selectedBackend,
12781
12819
  workingDirectory: config.workingDirectory,
12782
- maxIterations: options.maxIterations || config.defaultMaxIterations,
12820
+ maxIterations: options.maxIterations ?? config.defaultMaxIterations,
12783
12821
  model: options.model || config.defaultModel,
12784
12822
  agents: options.agents,
12785
12823
  tools: options.tools,
@@ -12944,7 +12982,7 @@ var init_main = __esm({
12944
12982
  return await this.collectInteractivePrompt();
12945
12983
  } else {
12946
12984
  const defaultPromptPath = path3.join(process.cwd(), ".juno_task", "prompt.md");
12947
- if (await fs2.pathExists(defaultPromptPath)) {
12985
+ if (await fs3.pathExists(defaultPromptPath)) {
12948
12986
  console.error(chalk15.blue(`\u{1F4C4} Using default prompt: ${chalk15.cyan(".juno_task/prompt.md")}`));
12949
12987
  return await this.loadPromptFromFile(defaultPromptPath);
12950
12988
  } else {
@@ -12972,7 +13010,7 @@ var init_main = __esm({
12972
13010
  }
12973
13011
  try {
12974
13012
  const resolvedPath = path3.resolve(prompt);
12975
- return await fs2.pathExists(resolvedPath);
13013
+ return await fs3.pathExists(resolvedPath);
12976
13014
  } catch {
12977
13015
  return false;
12978
13016
  }
@@ -12980,7 +13018,7 @@ var init_main = __esm({
12980
13018
  async loadPromptFromFile(filePath) {
12981
13019
  try {
12982
13020
  const resolvedPath = path3.resolve(filePath);
12983
- const content = await fs2.readFile(resolvedPath, "utf-8");
13021
+ const content = await fs3.readFile(resolvedPath, "utf-8");
12984
13022
  if (!content.trim()) {
12985
13023
  throw new FileSystemError(
12986
13024
  "Prompt file is empty",
@@ -13302,6 +13340,221 @@ var init_main = __esm({
13302
13340
  }
13303
13341
  });
13304
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
+
13305
13558
  // src/utils/startup-validation.ts
13306
13559
  var startup_validation_exports = {};
13307
13560
  __export(startup_validation_exports, {
@@ -13392,7 +13645,7 @@ async function validateJSONFile(configSchema, baseDir) {
13392
13645
  const warnings = [];
13393
13646
  const filePath = path3__default.join(baseDir, configSchema.file);
13394
13647
  try {
13395
- const exists = await fs2.pathExists(filePath);
13648
+ const exists = await fs3.pathExists(filePath);
13396
13649
  if (!exists) {
13397
13650
  if (configSchema.required) {
13398
13651
  errors.push({
@@ -13415,7 +13668,7 @@ async function validateJSONFile(configSchema, baseDir) {
13415
13668
  return { isValid: !configSchema.required, errors, warnings };
13416
13669
  }
13417
13670
  try {
13418
- await fs2.access(filePath, fs2.constants.R_OK);
13671
+ await fs3.access(filePath, fs3.constants.R_OK);
13419
13672
  } catch (accessError) {
13420
13673
  errors.push({
13421
13674
  file: configSchema.file,
@@ -13431,7 +13684,7 @@ async function validateJSONFile(configSchema, baseDir) {
13431
13684
  }
13432
13685
  let jsonData;
13433
13686
  try {
13434
- const fileContent = await fs2.readFile(filePath, "utf8");
13687
+ const fileContent = await fs3.readFile(filePath, "utf8");
13435
13688
  jsonData = JSON.parse(fileContent);
13436
13689
  } catch (parseError) {
13437
13690
  const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
@@ -13566,7 +13819,7 @@ function displayValidationResults(result) {
13566
13819
  }
13567
13820
  async function logValidationResults(result, baseDir = process.cwd()) {
13568
13821
  const logDir = path3__default.join(baseDir, ".juno_task", "logs");
13569
- await fs2.ensureDir(logDir);
13822
+ await fs3.ensureDir(logDir);
13570
13823
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
13571
13824
  const logFile = path3__default.join(logDir, `startup-validation-${timestamp}.log`);
13572
13825
  const logContent = [
@@ -13609,7 +13862,7 @@ async function logValidationResults(result, baseDir = process.cwd()) {
13609
13862
  logContent.push(``);
13610
13863
  }
13611
13864
  }
13612
- await fs2.writeFile(logFile, logContent.join("\n"));
13865
+ await fs3.writeFile(logFile, logContent.join("\n"));
13613
13866
  return logFile;
13614
13867
  }
13615
13868
  async function validateStartupConfigs(baseDir = process.cwd(), verbose = false) {
@@ -13943,7 +14196,7 @@ var TemplateEngine = class {
13943
14196
  let overallError;
13944
14197
  try {
13945
14198
  if (!options.dryRun) {
13946
- await fs2.ensureDir(targetDirectory);
14199
+ await fs3.ensureDir(targetDirectory);
13947
14200
  }
13948
14201
  for (const template of templates) {
13949
14202
  try {
@@ -15293,7 +15546,7 @@ This directory contains specification documents for your project.
15293
15546
  const fileName = this.getTemplateFileName(template);
15294
15547
  const targetPath = path3.join(targetDirectory, fileName);
15295
15548
  try {
15296
- const fileExists = await fs2.pathExists(targetPath);
15549
+ const fileExists = await fs3.pathExists(targetPath);
15297
15550
  if (fileExists && !options.force && options.onConflict !== "overwrite") {
15298
15551
  return {
15299
15552
  path: targetPath,
@@ -15315,10 +15568,10 @@ This directory contains specification documents for your project.
15315
15568
  }
15316
15569
  if (options.createBackup && fileExists) {
15317
15570
  const backupPath = `${targetPath}.backup.${Date.now()}`;
15318
- await fs2.copy(targetPath, backupPath);
15571
+ await fs3.copy(targetPath, backupPath);
15319
15572
  }
15320
- await fs2.ensureDir(path3.dirname(targetPath));
15321
- await fs2.writeFile(targetPath, renderedContent, "utf8");
15573
+ await fs3.ensureDir(path3.dirname(targetPath));
15574
+ await fs3.writeFile(targetPath, renderedContent, "utf8");
15322
15575
  return {
15323
15576
  path: targetPath,
15324
15577
  content: renderedContent,
@@ -15582,7 +15835,7 @@ var SimpleInitTUI = class {
15582
15835
  }
15583
15836
  async confirmSave(targetDirectory) {
15584
15837
  const junoTaskPath = path3.join(targetDirectory, ".juno_task");
15585
- if (await fs2.pathExists(junoTaskPath)) {
15838
+ if (await fs3.pathExists(junoTaskPath)) {
15586
15839
  console.log(chalk15.yellow(" \u26A0\uFE0F .juno_task directory already exists"));
15587
15840
  console.log(chalk15.gray(" Would you like to:"));
15588
15841
  console.log(chalk15.gray(" 1) Override existing files"));
@@ -15627,16 +15880,16 @@ var SimpleProjectGenerator = class {
15627
15880
  async generate() {
15628
15881
  const { targetDirectory, variables, force } = this.context;
15629
15882
  console.log(chalk15.blue("\u{1F4C1} Creating project directory..."));
15630
- await fs2.ensureDir(targetDirectory);
15883
+ await fs3.ensureDir(targetDirectory);
15631
15884
  const junoTaskDir = path3.join(targetDirectory, ".juno_task");
15632
- const junoTaskExists = await fs2.pathExists(junoTaskDir);
15885
+ const junoTaskExists = await fs3.pathExists(junoTaskDir);
15633
15886
  if (junoTaskExists && !force) {
15634
15887
  throw new ValidationError(
15635
15888
  "Project already initialized. Directory .juno_task already exists.",
15636
15889
  ["Use --force flag to overwrite existing files", "Choose a different directory"]
15637
15890
  );
15638
15891
  }
15639
- await fs2.ensureDir(junoTaskDir);
15892
+ await fs3.ensureDir(junoTaskDir);
15640
15893
  console.log(chalk15.blue("\u2699\uFE0F Creating project configuration..."));
15641
15894
  await this.createConfigFile(junoTaskDir, targetDirectory);
15642
15895
  console.log(chalk15.blue("\u{1F527} Setting up MCP configuration..."));
@@ -15670,21 +15923,21 @@ var SimpleProjectGenerator = class {
15670
15923
  const promptContent = await templateEngine.render(promptTemplate, templateContext);
15671
15924
  const initContent = await templateEngine.render(initTemplate, templateContext);
15672
15925
  const implementContent = await templateEngine.render(implementTemplate, templateContext);
15673
- await fs2.writeFile(path3.join(junoTaskDir, "prompt.md"), promptContent);
15674
- await fs2.writeFile(path3.join(junoTaskDir, "init.md"), initContent);
15675
- 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);
15676
15929
  const userFeedbackTemplate = templateEngine.getBuiltInTemplate("USER_FEEDBACK.md");
15677
15930
  if (userFeedbackTemplate) {
15678
15931
  const userFeedbackContent = await templateEngine.render(userFeedbackTemplate, templateContext);
15679
- await fs2.writeFile(path3.join(junoTaskDir, "USER_FEEDBACK.md"), userFeedbackContent);
15932
+ await fs3.writeFile(path3.join(junoTaskDir, "USER_FEEDBACK.md"), userFeedbackContent);
15680
15933
  }
15681
15934
  const planTemplate = templateEngine.getBuiltInTemplate("plan.md");
15682
15935
  if (planTemplate) {
15683
15936
  const planContent = await templateEngine.render(planTemplate, templateContext);
15684
- await fs2.writeFile(path3.join(junoTaskDir, "plan.md"), planContent);
15937
+ await fs3.writeFile(path3.join(junoTaskDir, "plan.md"), planContent);
15685
15938
  }
15686
15939
  const specsDir = path3.join(junoTaskDir, "specs");
15687
- await fs2.ensureDir(specsDir);
15940
+ await fs3.ensureDir(specsDir);
15688
15941
  const specsReadmeContent = `# Project Specifications
15689
15942
 
15690
15943
  This directory contains detailed specifications for the project components.
@@ -15701,7 +15954,7 @@ This directory contains detailed specifications for the project components.
15701
15954
  - Avoid conflicts with existing file names
15702
15955
  - Use \`.md\` extension for all specification files
15703
15956
  `;
15704
- await fs2.writeFile(path3.join(specsDir, "README.md"), specsReadmeContent);
15957
+ await fs3.writeFile(path3.join(specsDir, "README.md"), specsReadmeContent);
15705
15958
  const requirementsContent = `# Requirements Specification
15706
15959
 
15707
15960
  ## Functional Requirements
@@ -15748,7 +16001,7 @@ This directory contains detailed specifications for the project components.
15748
16001
  - Code quality: Clean, maintainable codebase
15749
16002
  - Documentation: Complete and accurate documentation
15750
16003
  `;
15751
- await fs2.writeFile(path3.join(specsDir, "requirements.md"), requirementsContent);
16004
+ await fs3.writeFile(path3.join(specsDir, "requirements.md"), requirementsContent);
15752
16005
  const architectureContent = `# Architecture Specification
15753
16006
 
15754
16007
  ## System Overview
@@ -15830,7 +16083,7 @@ This project uses AI-assisted development with juno-code to achieve: ${variables
15830
16083
  - Performance monitoring
15831
16084
  - Security best practices
15832
16085
  `;
15833
- await fs2.writeFile(path3.join(specsDir, "architecture.md"), architectureContent);
16086
+ await fs3.writeFile(path3.join(specsDir, "architecture.md"), architectureContent);
15834
16087
  const claudeContent = `# Claude Development Session Learnings
15835
16088
 
15836
16089
  ## Project Overview
@@ -15902,7 +16155,7 @@ This file will be updated as development progresses to track:
15902
16155
  - Solutions to complex problems
15903
16156
  - Performance improvements and optimizations
15904
16157
  `;
15905
- await fs2.writeFile(path3.join(targetDirectory, "CLAUDE.md"), claudeContent);
16158
+ await fs3.writeFile(path3.join(targetDirectory, "CLAUDE.md"), claudeContent);
15906
16159
  const agentsContent = `# AI Agent Selection and Performance
15907
16160
 
15908
16161
  ## Available Agents
@@ -15966,7 +16219,7 @@ Track agent performance for:
15966
16219
  4. **Feedback Loop**: Provide feedback to improve agent performance
15967
16220
  5. **Performance Monitoring**: Track and optimize agent usage
15968
16221
  `;
15969
- await fs2.writeFile(path3.join(targetDirectory, "AGENTS.md"), agentsContent);
16222
+ await fs3.writeFile(path3.join(targetDirectory, "AGENTS.md"), agentsContent);
15970
16223
  const readmeContent = `# ${variables.PROJECT_NAME}
15971
16224
 
15972
16225
  ${variables.DESCRIPTION}
@@ -16063,7 +16316,7 @@ ${variables.GIT_URL}` : ""}
16063
16316
  Created with juno-code on ${variables.CURRENT_DATE}
16064
16317
  ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16065
16318
  `;
16066
- await fs2.writeFile(path3.join(targetDirectory, "README.md"), readmeContent);
16319
+ await fs3.writeFile(path3.join(targetDirectory, "README.md"), readmeContent);
16067
16320
  console.log(chalk15.blue("\u{1F4E6} Installing utility scripts..."));
16068
16321
  await this.copyScriptsFromTemplates(junoTaskDir);
16069
16322
  console.log(chalk15.blue("\u{1F40D} Installing Python requirements..."));
@@ -16115,7 +16368,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16115
16368
  hooks: getDefaultHooks()
16116
16369
  };
16117
16370
  const configPath = path3.join(junoTaskDir, "config.json");
16118
- await fs2.writeFile(configPath, JSON.stringify(configContent, null, 2));
16371
+ await fs3.writeFile(configPath, JSON.stringify(configContent, null, 2));
16119
16372
  console.log(chalk15.green(` \u2713 Created .juno_task/config.json with ${this.context.subagent} as default subagent`));
16120
16373
  }
16121
16374
  async createMcpFile(junoTaskDir, targetDirectory) {
@@ -16169,7 +16422,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16169
16422
  }
16170
16423
  };
16171
16424
  const mcpPath = path3.join(junoTaskDir, "mcp.json");
16172
- await fs2.writeFile(mcpPath, JSON.stringify(mcpContent, null, 2));
16425
+ await fs3.writeFile(mcpPath, JSON.stringify(mcpContent, null, 2));
16173
16426
  console.log(chalk15.green(` \u2713 Created .juno_task/mcp.json with roundtable-ai server configuration`));
16174
16427
  }
16175
16428
  getDefaultModelForSubagent(subagent) {
@@ -16188,7 +16441,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16188
16441
  async copyScriptsFromTemplates(junoTaskDir) {
16189
16442
  try {
16190
16443
  const scriptsDir = path3.join(junoTaskDir, "scripts");
16191
- await fs2.ensureDir(scriptsDir);
16444
+ await fs3.ensureDir(scriptsDir);
16192
16445
  const __filename2 = fileURLToPath(import.meta.url);
16193
16446
  const __dirname2 = path3.dirname(__filename2);
16194
16447
  let templatesScriptsDir;
@@ -16199,11 +16452,11 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16199
16452
  } else {
16200
16453
  templatesScriptsDir = path3.join(__dirname2, "../../templates/scripts");
16201
16454
  }
16202
- if (!await fs2.pathExists(templatesScriptsDir)) {
16455
+ if (!await fs3.pathExists(templatesScriptsDir)) {
16203
16456
  console.log(chalk15.yellow(" \u26A0\uFE0F Template scripts directory not found, skipping script installation"));
16204
16457
  return;
16205
16458
  }
16206
- const scriptFiles = await fs2.readdir(templatesScriptsDir);
16459
+ const scriptFiles = await fs3.readdir(templatesScriptsDir);
16207
16460
  if (scriptFiles.length === 0) {
16208
16461
  console.log(chalk15.gray(" \u2139\uFE0F No template scripts found to install"));
16209
16462
  return;
@@ -16212,11 +16465,11 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16212
16465
  for (const scriptFile of scriptFiles) {
16213
16466
  const sourcePath = path3.join(templatesScriptsDir, scriptFile);
16214
16467
  const destPath = path3.join(scriptsDir, scriptFile);
16215
- const stats = await fs2.stat(sourcePath);
16468
+ const stats = await fs3.stat(sourcePath);
16216
16469
  if (stats.isFile()) {
16217
- await fs2.copy(sourcePath, destPath);
16470
+ await fs3.copy(sourcePath, destPath);
16218
16471
  if (scriptFile.endsWith(".sh")) {
16219
- await fs2.chmod(destPath, 493);
16472
+ await fs3.chmod(destPath, 493);
16220
16473
  }
16221
16474
  copiedCount++;
16222
16475
  console.log(chalk15.green(` \u2713 Installed script: ${scriptFile}`));
@@ -16239,7 +16492,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
16239
16492
  try {
16240
16493
  const scriptsDir = path3.join(junoTaskDir, "scripts");
16241
16494
  const installScript = path3.join(scriptsDir, "install_requirements.sh");
16242
- if (!await fs2.pathExists(installScript)) {
16495
+ if (!await fs3.pathExists(installScript)) {
16243
16496
  console.log(chalk15.yellow(" \u26A0\uFE0F install_requirements.sh not found, skipping Python dependencies installation"));
16244
16497
  console.log(chalk15.gray(" You can install dependencies manually: juno-kanban, roundtable-ai"));
16245
16498
  return;
@@ -16536,20 +16789,20 @@ init_types();
16536
16789
  async function loadInitPrompt(directory) {
16537
16790
  const junoTaskDir = path3.join(directory, ".juno_task");
16538
16791
  const initFile = path3.join(junoTaskDir, "init.md");
16539
- if (!await fs2.pathExists(junoTaskDir)) {
16792
+ if (!await fs3.pathExists(junoTaskDir)) {
16540
16793
  throw new FileSystemError(
16541
16794
  'No .juno_task directory found. Run "juno-code init" first.',
16542
16795
  junoTaskDir
16543
16796
  );
16544
16797
  }
16545
- if (!await fs2.pathExists(initFile)) {
16798
+ if (!await fs3.pathExists(initFile)) {
16546
16799
  throw new FileSystemError(
16547
16800
  "No init.md file found in .juno_task directory",
16548
16801
  initFile
16549
16802
  );
16550
16803
  }
16551
16804
  try {
16552
- const content = await fs2.readFile(initFile, "utf-8");
16805
+ const content = await fs3.readFile(initFile, "utf-8");
16553
16806
  if (!content.trim()) {
16554
16807
  throw new FileSystemError(
16555
16808
  "init.md file is empty. Please add task instructions.",
@@ -17581,8 +17834,8 @@ Focus on ${request.intelligence === "basic" ? "basic functionality" : request.in
17581
17834
  for (const scenario of scenarios) {
17582
17835
  const testContent = await this.generateTestContent(request, scenario, template);
17583
17836
  const testFilePath = this.resolveTestFilePath(request, scenario);
17584
- await fs2.ensureDir(path3.dirname(testFilePath));
17585
- await fs2.writeFile(testFilePath, testContent);
17837
+ await fs3.ensureDir(path3.dirname(testFilePath));
17838
+ await fs3.writeFile(testFilePath, testContent);
17586
17839
  testFiles.push(testFilePath);
17587
17840
  }
17588
17841
  return testFiles;
@@ -17834,8 +18087,8 @@ var TestExecutionEngine = class {
17834
18087
  async collectCoverage(request) {
17835
18088
  const coverageFile = path3.join(request.workingDirectory, "coverage", "coverage-summary.json");
17836
18089
  try {
17837
- if (await fs2.pathExists(coverageFile)) {
17838
- const coverageData = await fs2.readJson(coverageFile);
18090
+ if (await fs3.pathExists(coverageFile)) {
18091
+ const coverageData = await fs3.readJson(coverageFile);
17839
18092
  return {
17840
18093
  lines: coverageData.total?.lines?.pct || 0,
17841
18094
  functions: coverageData.total?.functions?.pct || 0,
@@ -18018,8 +18271,8 @@ var TestReportEngine = class {
18018
18271
  suggestions: analysis.suggestions,
18019
18272
  recommendations: analysis.recommendations
18020
18273
  };
18021
- await fs2.ensureDir(path3.dirname(outputPath));
18022
- await fs2.writeJson(outputPath, report, { spaces: 2 });
18274
+ await fs3.ensureDir(path3.dirname(outputPath));
18275
+ await fs3.writeJson(outputPath, report, { spaces: 2 });
18023
18276
  return outputPath;
18024
18277
  }
18025
18278
  async generateHTMLReport(analysis, outputPath, includeVisualizations) {
@@ -18082,8 +18335,8 @@ var TestReportEngine = class {
18082
18335
  </body>
18083
18336
  </html>
18084
18337
  `.trim();
18085
- await fs2.ensureDir(path3.dirname(outputPath));
18086
- await fs2.writeFile(outputPath, html);
18338
+ await fs3.ensureDir(path3.dirname(outputPath));
18339
+ await fs3.writeFile(outputPath, html);
18087
18340
  return outputPath;
18088
18341
  }
18089
18342
  generateCharts(analysis) {
@@ -18140,8 +18393,8 @@ ${analysis.suggestions.map((suggestion) => `- ${suggestion}`).join("\n")}
18140
18393
 
18141
18394
  ${analysis.recommendations.map((rec) => `- ${rec}`).join("\n")}
18142
18395
  `.trim();
18143
- await fs2.ensureDir(path3.dirname(outputPath));
18144
- await fs2.writeFile(outputPath, markdown);
18396
+ await fs3.ensureDir(path3.dirname(outputPath));
18397
+ await fs3.writeFile(outputPath, markdown);
18145
18398
  return outputPath;
18146
18399
  }
18147
18400
  async displayConsoleReport(analysis) {
@@ -18481,16 +18734,16 @@ async function compactConfigFile(filePath, options = {}) {
18481
18734
  preserveDays = 30,
18482
18735
  preservePatterns = []
18483
18736
  } = options;
18484
- const originalContent = await fs2.readFile(filePath, "utf-8");
18737
+ const originalContent = await fs3.readFile(filePath, "utf-8");
18485
18738
  const originalSize = originalContent.length;
18486
18739
  let backupPath = "";
18487
18740
  if (createBackup && !dryRun) {
18488
18741
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18489
18742
  const ext = path3.extname(filePath);
18490
18743
  const basename11 = path3.basename(filePath, ext);
18491
- const dirname12 = path3.dirname(filePath);
18492
- backupPath = path3.join(dirname12, `${basename11}.backup.${timestamp}${ext}`);
18493
- 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");
18494
18747
  }
18495
18748
  const compactionAnalysis = analyzeMarkdownStructure(originalContent);
18496
18749
  const compactedContent = compactMarkdownContent(
@@ -18505,7 +18758,7 @@ async function compactConfigFile(filePath, options = {}) {
18505
18758
  const compactedSize = finalContent.length;
18506
18759
  const reductionPercentage = Math.round((originalSize - compactedSize) / originalSize * 100);
18507
18760
  if (!dryRun) {
18508
- await fs2.writeFile(filePath, finalContent, "utf-8");
18761
+ await fs3.writeFile(filePath, finalContent, "utf-8");
18509
18762
  }
18510
18763
  return {
18511
18764
  originalSize,
@@ -18635,7 +18888,7 @@ function formatFileSize(bytes) {
18635
18888
  }
18636
18889
  async function shouldCompactFile(filePath, sizeThresholdKB = 50, ageThresholdDays = 30) {
18637
18890
  try {
18638
- const stats = await fs2.stat(filePath);
18891
+ const stats = await fs3.stat(filePath);
18639
18892
  const sizeKB = stats.size / 1024;
18640
18893
  if (sizeKB > sizeThresholdKB) {
18641
18894
  return true;
@@ -18657,19 +18910,19 @@ async function archiveResolvedIssues(options) {
18657
18910
  feedbackFile,
18658
18911
  archiveDir = path3.join(path3.dirname(feedbackFile), "archives"),
18659
18912
  openIssuesThreshold = 10} = options;
18660
- if (!await fs2.pathExists(feedbackFile)) {
18913
+ if (!await fs3.pathExists(feedbackFile)) {
18661
18914
  throw new Error(`Feedback file does not exist: ${feedbackFile}`);
18662
18915
  }
18663
- const content = await fs2.readFile(feedbackFile, "utf-8");
18916
+ const content = await fs3.readFile(feedbackFile, "utf-8");
18664
18917
  const parsed = parseUserFeedback(content);
18665
18918
  const warningsGenerated = [];
18666
18919
  if (parsed.openIssues.length > openIssuesThreshold) {
18667
18920
  const warning = `Found ${parsed.openIssues.length} open issues (threshold: ${openIssuesThreshold}). Consider reviewing and prioritizing.`;
18668
18921
  warningsGenerated.push(warning);
18669
18922
  const logFile = path3.join(path3.dirname(feedbackFile), "logs", "feedback-warnings.log");
18670
- await fs2.ensureDir(path3.dirname(logFile));
18923
+ await fs3.ensureDir(path3.dirname(logFile));
18671
18924
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
18672
- await fs2.appendFile(logFile, `[${timestamp}] ${warning}
18925
+ await fs3.appendFile(logFile, `[${timestamp}] ${warning}
18673
18926
  `);
18674
18927
  }
18675
18928
  if (parsed.resolvedIssues.length === 0) {
@@ -18686,10 +18939,10 @@ async function archiveResolvedIssues(options) {
18686
18939
  const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
18687
18940
  const archiveFile = path3.join(archiveDir, `USER_FEEDBACK_archive_${currentYear}.md`);
18688
18941
  {
18689
- await fs2.ensureDir(archiveDir);
18942
+ await fs3.ensureDir(archiveDir);
18690
18943
  await appendToArchive(archiveFile, parsed.resolvedIssues);
18691
18944
  const compactedContent = generateCompactedFeedback(parsed.openIssues, parsed.metadata);
18692
- await fs2.writeFile(feedbackFile, compactedContent, "utf-8");
18945
+ await fs3.writeFile(feedbackFile, compactedContent, "utf-8");
18693
18946
  }
18694
18947
  {
18695
18948
  console.log(chalk15.green(`\u2705 Archived ${parsed.resolvedIssues.length} resolved issues`));
@@ -18736,8 +18989,8 @@ function parseUserFeedback(content) {
18736
18989
  async function appendToArchive(archiveFile, resolvedIssues) {
18737
18990
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
18738
18991
  let archiveContent = "";
18739
- if (await fs2.pathExists(archiveFile)) {
18740
- archiveContent = await fs2.readFile(archiveFile, "utf-8");
18992
+ if (await fs3.pathExists(archiveFile)) {
18993
+ archiveContent = await fs3.readFile(archiveFile, "utf-8");
18741
18994
  } else {
18742
18995
  const year = path3.basename(archiveFile).match(/(\d{4})/)?.[1] || (/* @__PURE__ */ new Date()).getFullYear();
18743
18996
  archiveContent = `# User Feedback Archive ${year}
@@ -18771,7 +19024,7 @@ ${resolvedIssue}
18771
19024
  `- Last updated: ${timestamp}`
18772
19025
  );
18773
19026
  }
18774
- await fs2.writeFile(archiveFile, archiveContent, "utf-8");
19027
+ await fs3.writeFile(archiveFile, archiveContent, "utf-8");
18775
19028
  }
18776
19029
  function generateCompactedFeedback(openIssues, metadata) {
18777
19030
  let content = metadata.trim() + "\n";
@@ -18796,15 +19049,15 @@ async function shouldArchive(feedbackFile, options = {}) {
18796
19049
  // 50KB
18797
19050
  lineCountThreshold = 500
18798
19051
  } = options;
18799
- if (!await fs2.pathExists(feedbackFile)) {
19052
+ if (!await fs3.pathExists(feedbackFile)) {
18800
19053
  return {
18801
19054
  shouldArchive: false,
18802
19055
  reasons: [],
18803
19056
  stats: { openIssuesCount: 0, resolvedIssuesCount: 0, fileSizeBytes: 0, lineCount: 0 }
18804
19057
  };
18805
19058
  }
18806
- const content = await fs2.readFile(feedbackFile, "utf-8");
18807
- const stats = await fs2.stat(feedbackFile);
19059
+ const content = await fs3.readFile(feedbackFile, "utf-8");
19060
+ const stats = await fs3.stat(feedbackFile);
18808
19061
  const parsed = parseUserFeedback(content);
18809
19062
  const lineCount = content.split("\n").length;
18810
19063
  const reasons = [];
@@ -18878,16 +19131,16 @@ var EnhancedFeedbackFileManager = class {
18878
19131
  this.feedbackFile = feedbackFile;
18879
19132
  }
18880
19133
  async ensureExists() {
18881
- if (!await fs2.pathExists(this.feedbackFile)) {
19134
+ if (!await fs3.pathExists(this.feedbackFile)) {
18882
19135
  await this.createInitialFile();
18883
19136
  }
18884
19137
  }
18885
19138
  async addFeedback(issue, testCriteria) {
18886
19139
  await this.ensureExists();
18887
19140
  try {
18888
- const content = await fs2.readFile(this.feedbackFile, "utf-8");
19141
+ const content = await fs3.readFile(this.feedbackFile, "utf-8");
18889
19142
  const updatedContent = this.addIssueToContent(content, issue, testCriteria);
18890
- await fs2.writeFile(this.feedbackFile, updatedContent, "utf-8");
19143
+ await fs3.writeFile(this.feedbackFile, updatedContent, "utf-8");
18891
19144
  } catch (error) {
18892
19145
  throw new ValidationError(
18893
19146
  `Failed to save feedback: ${error}`,
@@ -18900,12 +19153,12 @@ var EnhancedFeedbackFileManager = class {
18900
19153
  */
18901
19154
  async repairMalformedFile() {
18902
19155
  try {
18903
- const content = await fs2.readFile(this.feedbackFile, "utf-8");
19156
+ const content = await fs3.readFile(this.feedbackFile, "utf-8");
18904
19157
  const hasOpenIssues = content.includes("<OPEN_ISSUES>");
18905
19158
  const hasClosingTag = content.includes("</OPEN_ISSUES>");
18906
19159
  if (!hasOpenIssues || !hasClosingTag) {
18907
19160
  const backupPath = this.feedbackFile + ".backup." + Date.now();
18908
- await fs2.writeFile(backupPath, content, "utf-8");
19161
+ await fs3.writeFile(backupPath, content, "utf-8");
18909
19162
  const existingIssues = this.extractIssuesFromMalformedContent(content);
18910
19163
  await this.createInitialFile(existingIssues);
18911
19164
  }
@@ -18937,8 +19190,8 @@ List any features you'd like to see added or bugs you've encountered.
18937
19190
 
18938
19191
  <!-- Resolved issues will be moved here -->
18939
19192
  `;
18940
- await fs2.ensureDir(path3.dirname(this.feedbackFile));
18941
- 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");
18942
19195
  }
18943
19196
  addIssueToContent(content, issue, testCriteria) {
18944
19197
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -19054,17 +19307,17 @@ async function handleCompactCommand(subArgs, options) {
19054
19307
  if (subArgs.length > 0) {
19055
19308
  for (const filePath of subArgs) {
19056
19309
  const resolvedPath = path3.resolve(filePath);
19057
- if (await fs2.pathExists(resolvedPath)) {
19310
+ if (await fs3.pathExists(resolvedPath)) {
19058
19311
  filesToCompact.push(resolvedPath);
19059
19312
  } else {
19060
19313
  console.warn(chalk15.yellow(`\u26A0\uFE0F File not found: ${filePath}`));
19061
19314
  }
19062
19315
  }
19063
19316
  } else {
19064
- if (await fs2.pathExists(defaultClaudeFile)) {
19317
+ if (await fs3.pathExists(defaultClaudeFile)) {
19065
19318
  filesToCompact.push(defaultClaudeFile);
19066
19319
  }
19067
- if (await fs2.pathExists(defaultAgentsFile)) {
19320
+ if (await fs3.pathExists(defaultAgentsFile)) {
19068
19321
  filesToCompact.push(defaultAgentsFile);
19069
19322
  }
19070
19323
  }
@@ -19998,11 +20251,11 @@ var GitManager = class {
19998
20251
  */
19999
20252
  async updateJunoTaskConfig(gitUrl) {
20000
20253
  const configPath = path3.join(this.workingDirectory, ".juno_task", "init.md");
20001
- if (!await fs2.pathExists(configPath)) {
20254
+ if (!await fs3.pathExists(configPath)) {
20002
20255
  return;
20003
20256
  }
20004
20257
  try {
20005
- let content = await fs2.readFile(configPath, "utf-8");
20258
+ let content = await fs3.readFile(configPath, "utf-8");
20006
20259
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
20007
20260
  if (frontmatterMatch) {
20008
20261
  let frontmatter = frontmatterMatch[1];
@@ -20023,7 +20276,7 @@ GIT_URL: ${gitUrl}
20023
20276
  `;
20024
20277
  content = frontmatter + content;
20025
20278
  }
20026
- await fs2.writeFile(configPath, content, "utf-8");
20279
+ await fs3.writeFile(configPath, content, "utf-8");
20027
20280
  } catch (error) {
20028
20281
  console.warn(`Warning: Failed to update juno-code configuration: ${error}`);
20029
20282
  }
@@ -20955,7 +21208,7 @@ var LogViewer = ({
20955
21208
  init_types();
20956
21209
  async function exportLogs(logger2, filepath, options) {
20957
21210
  try {
20958
- const fs21 = await import('fs-extra');
21211
+ const fs22 = await import('fs-extra');
20959
21212
  let entries = logger2.getRecentEntries(options.tail || 1e3);
20960
21213
  if (options.level) {
20961
21214
  const level = LogLevel[options.level.toUpperCase()];
@@ -20985,7 +21238,7 @@ async function exportLogs(logger2, filepath, options) {
20985
21238
  },
20986
21239
  entries
20987
21240
  };
20988
- await fs21.writeFile(filepath, JSON.stringify(exportData, null, 2));
21241
+ await fs22.writeFile(filepath, JSON.stringify(exportData, null, 2));
20989
21242
  console.log(chalk15.green(`\u2705 Exported ${entries.length} log entries to: ${filepath}`));
20990
21243
  } catch (error) {
20991
21244
  console.error(chalk15.red(`\u274C Failed to export logs: ${error}`));
@@ -22529,7 +22782,7 @@ function createServicesCommand() {
22529
22782
  const servicesCmd = new Command("services").description("Manage juno-code service scripts").addHelpText("after", `
22530
22783
  Examples:
22531
22784
  $ juno-code services install Install service scripts to ~/.juno_code/services/
22532
- $ juno-code services install --force Reinstall/refresh service scripts (codex.py/claude.py)
22785
+ $ juno-code services install --force Reinstall/refresh service scripts (codex.py/claude.py/gemini.py)
22533
22786
  $ juno-code services list List installed service scripts
22534
22787
  $ juno-code services status Check installation status
22535
22788
  $ juno-code services uninstall Remove all service scripts
@@ -22786,10 +23039,10 @@ autoload -U compinit && compinit`;
22786
23039
  */
22787
23040
  async isSourceCommandPresent(configPath, sourceCommand) {
22788
23041
  try {
22789
- if (!await fs2.pathExists(configPath)) {
23042
+ if (!await fs3.pathExists(configPath)) {
22790
23043
  return false;
22791
23044
  }
22792
- const content = await fs2.readFile(configPath, "utf-8");
23045
+ const content = await fs3.readFile(configPath, "utf-8");
22793
23046
  return content.includes("juno-code completion");
22794
23047
  } catch {
22795
23048
  return false;
@@ -22811,15 +23064,15 @@ autoload -U compinit && compinit`;
22811
23064
  continue;
22812
23065
  }
22813
23066
  try {
22814
- const completionExists = await fs2.pathExists(shell.completionPath);
22815
- const configExists = await fs2.pathExists(shell.configPath);
23067
+ const completionExists = await fs3.pathExists(shell.completionPath);
23068
+ const configExists = await fs3.pathExists(shell.configPath);
22816
23069
  const isSourced = configExists && await this.isSourceCommandPresent(
22817
23070
  shell.configPath,
22818
23071
  this.getSourceCommand(shell.name, shell.completionPath)
22819
23072
  );
22820
23073
  let lastInstalled;
22821
23074
  if (completionExists) {
22822
- const stats = await fs2.stat(shell.completionPath);
23075
+ const stats = await fs3.stat(shell.completionPath);
22823
23076
  lastInstalled = stats.mtime;
22824
23077
  }
22825
23078
  statuses.push({
@@ -22845,7 +23098,7 @@ autoload -U compinit && compinit`;
22845
23098
  async ensureCompletionDirectory(shell) {
22846
23099
  const completionPath = this.getCompletionPath(shell);
22847
23100
  const completionDir = path3.dirname(completionPath);
22848
- await fs2.ensureDir(completionDir);
23101
+ await fs3.ensureDir(completionDir);
22849
23102
  }
22850
23103
  /**
22851
23104
  * Ensure directory exists for shell configuration
@@ -22853,7 +23106,7 @@ autoload -U compinit && compinit`;
22853
23106
  async ensureConfigDirectory(shell) {
22854
23107
  const configPath = this.getConfigPath(shell);
22855
23108
  const configDir = path3.dirname(configPath);
22856
- await fs2.ensureDir(configDir);
23109
+ await fs3.ensureDir(configDir);
22857
23110
  }
22858
23111
  /**
22859
23112
  * Get shell version information
@@ -22877,15 +23130,15 @@ autoload -U compinit && compinit`;
22877
23130
  try {
22878
23131
  const configPath = this.getConfigPath(shell);
22879
23132
  const configDir = path3.dirname(configPath);
22880
- await fs2.access(configDir, fs2.constants.W_OK);
23133
+ await fs3.access(configDir, fs3.constants.W_OK);
22881
23134
  } catch {
22882
23135
  issues.push(`Cannot write to ${shell} configuration directory`);
22883
23136
  }
22884
23137
  try {
22885
23138
  const completionPath = this.getCompletionPath(shell);
22886
23139
  const completionDir = path3.dirname(completionPath);
22887
- await fs2.ensureDir(completionDir);
22888
- await fs2.access(completionDir, fs2.constants.W_OK);
23140
+ await fs3.ensureDir(completionDir);
23141
+ await fs3.access(completionDir, fs3.constants.W_OK);
22889
23142
  } catch {
22890
23143
  issues.push(`Cannot write to ${shell} completion directory`);
22891
23144
  }
@@ -22956,10 +23209,10 @@ var ContextAwareCompletion = class {
22956
23209
  async getSessionIds() {
22957
23210
  try {
22958
23211
  const sessionDir = path3.join(process.cwd(), ".juno_task", "sessions");
22959
- if (!await fs2.pathExists(sessionDir)) {
23212
+ if (!await fs3.pathExists(sessionDir)) {
22960
23213
  return [];
22961
23214
  }
22962
- const sessions = await fs2.readdir(sessionDir);
23215
+ const sessions = await fs3.readdir(sessionDir);
22963
23216
  return sessions.filter((name) => name.match(/^session_\d+$/)).map((name) => name.replace("session_", "")).sort((a, b) => parseInt(b) - parseInt(a));
22964
23217
  } catch {
22965
23218
  return [];
@@ -22973,8 +23226,8 @@ var ContextAwareCompletion = class {
22973
23226
  const builtinTemplates = ["basic", "advanced", "research", "development"];
22974
23227
  const customTemplatesDir = path3.join(process.cwd(), ".juno_task", "templates");
22975
23228
  let customTemplates = [];
22976
- if (await fs2.pathExists(customTemplatesDir)) {
22977
- const files = await fs2.readdir(customTemplatesDir);
23229
+ if (await fs3.pathExists(customTemplatesDir)) {
23230
+ const files = await fs3.readdir(customTemplatesDir);
22978
23231
  customTemplates = files.filter((name) => name.endsWith(".md") || name.endsWith(".hbs")).map((name) => path3.basename(name, path3.extname(name)));
22979
23232
  }
22980
23233
  return [...builtinTemplates, ...customTemplates].sort();
@@ -23051,7 +23304,7 @@ var CompletionInstaller = class {
23051
23304
  await this.shellDetector.ensureConfigDirectory(shell);
23052
23305
  const script = this.generateEnhancedCompletion(shell, "juno-code");
23053
23306
  const completionPath = this.shellDetector.getCompletionPath(shell);
23054
- await fs2.writeFile(completionPath, script, "utf-8");
23307
+ await fs3.writeFile(completionPath, script, "utf-8");
23055
23308
  const configPath = this.shellDetector.getConfigPath(shell);
23056
23309
  const sourceCommand = this.shellDetector.getSourceCommand(shell, completionPath);
23057
23310
  const warnings = [];
@@ -23059,7 +23312,7 @@ var CompletionInstaller = class {
23059
23312
  const isPresent = await this.shellDetector.isSourceCommandPresent(configPath, sourceCommand);
23060
23313
  if (!isPresent) {
23061
23314
  try {
23062
- await fs2.appendFile(configPath, `
23315
+ await fs3.appendFile(configPath, `
23063
23316
 
23064
23317
  ${sourceCommand}
23065
23318
  `);
@@ -23090,8 +23343,8 @@ ${sourceCommand}
23090
23343
  async uninstall(shell) {
23091
23344
  try {
23092
23345
  const completionPath = this.shellDetector.getCompletionPath(shell);
23093
- if (await fs2.pathExists(completionPath)) {
23094
- await fs2.remove(completionPath);
23346
+ if (await fs3.pathExists(completionPath)) {
23347
+ await fs3.remove(completionPath);
23095
23348
  return true;
23096
23349
  }
23097
23350
  return false;
@@ -23105,7 +23358,7 @@ ${sourceCommand}
23105
23358
  async isInstalled(shell) {
23106
23359
  try {
23107
23360
  const completionPath = this.shellDetector.getCompletionPath(shell);
23108
- return await fs2.pathExists(completionPath);
23361
+ return await fs3.pathExists(completionPath);
23109
23362
  } catch {
23110
23363
  return false;
23111
23364
  }
@@ -23906,11 +24159,11 @@ function setupMainCommand(program) {
23906
24159
  );
23907
24160
  const allOptions2 = { ...definedGlobalOptions, ...options };
23908
24161
  if (!globalOptions.subagent && !options.prompt && !options.interactive && !options.interactivePrompt) {
23909
- const fs21 = await import('fs-extra');
23910
- const path23 = await import('path');
24162
+ const fs22 = await import('fs-extra');
24163
+ const path24 = await import('path');
23911
24164
  const cwd2 = process.cwd();
23912
- const junoTaskDir = path23.join(cwd2, ".juno_task");
23913
- if (await fs21.pathExists(junoTaskDir)) {
24165
+ const junoTaskDir = path24.join(cwd2, ".juno_task");
24166
+ if (await fs22.pathExists(junoTaskDir)) {
23914
24167
  console.log(chalk15.blue.bold("\u{1F3AF} Juno Code - Auto-detected Initialized Project\n"));
23915
24168
  try {
23916
24169
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -23927,12 +24180,12 @@ function setupMainCommand(program) {
23927
24180
  allOptions2.subagent = config.defaultSubagent;
23928
24181
  console.log(chalk15.gray(`\u{1F916} Using configured subagent: ${chalk15.cyan(config.defaultSubagent)}`));
23929
24182
  }
23930
- const promptFile = path23.join(junoTaskDir, "prompt.md");
23931
- if (!allOptions2.prompt && await fs21.pathExists(promptFile)) {
24183
+ const promptFile = path24.join(junoTaskDir, "prompt.md");
24184
+ if (!allOptions2.prompt && await fs22.pathExists(promptFile)) {
23932
24185
  allOptions2.prompt = promptFile;
23933
24186
  console.log(chalk15.gray(`\u{1F4C4} Using default prompt: ${chalk15.cyan(".juno_task/prompt.md")}`));
23934
24187
  }
23935
- if (allOptions2.subagent && (allOptions2.prompt || await fs21.pathExists(promptFile))) {
24188
+ if (allOptions2.subagent && (allOptions2.prompt || await fs22.pathExists(promptFile))) {
23936
24189
  console.log(chalk15.green("\u2713 Auto-detected project configuration\n"));
23937
24190
  const { mainCommandHandler: mainCommandHandler3 } = await Promise.resolve().then(() => (init_main(), main_exports));
23938
24191
  await mainCommandHandler3([], allOptions2, command);
@@ -24089,6 +24342,17 @@ async function main() {
24089
24342
  console.error("[DEBUG] Service auto-update failed:", error instanceof Error ? error.message : String(error));
24090
24343
  }
24091
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
+ }
24092
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");
24093
24357
  setupGlobalOptions(program);
24094
24358
  const isVerbose = process.argv.includes("--verbose") || process.argv.includes("-v");
@@ -24096,10 +24360,10 @@ async function main() {
24096
24360
  const isHelpOrVersion = process.argv.includes("--help") || process.argv.includes("-h") || process.argv.includes("--version") || process.argv.includes("-V");
24097
24361
  const hasNoArguments = process.argv.length <= 2;
24098
24362
  const isInitCommand = process.argv.includes("init");
24099
- const fs21 = await import('fs-extra');
24100
- const path23 = await import('path');
24101
- const junoTaskDir = path23.join(process.cwd(), ".juno_task");
24102
- 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);
24103
24367
  if (!isHelpOrVersion && !hasNoArguments && !isInitCommand && isInitialized) {
24104
24368
  try {
24105
24369
  const { validateStartupConfigs: validateStartupConfigs2 } = await Promise.resolve().then(() => (init_startup_validation(), startup_validation_exports));