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/README.md +364 -64
- package/dist/bin/cli.js +445 -181
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +444 -180
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/dist/templates/scripts/clean_logs_folder.sh +0 -0
- package/dist/templates/scripts/run_until_completion.sh +202 -0
- package/dist/templates/services/README.md +43 -0
- package/dist/templates/services/claude.py +1 -1
- package/dist/templates/services/codex.py +4 -4
- package/dist/templates/services/gemini.py +473 -0
- package/package.json +3 -3
package/dist/bin/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
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,
|
|
146
|
-
super(
|
|
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 (
|
|
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 (
|
|
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
|
|
350
|
+
const exists = await fs3.pathExists(this.VERSION_FILE);
|
|
338
351
|
if (!exists) {
|
|
339
352
|
return null;
|
|
340
353
|
}
|
|
341
|
-
const version3 = await
|
|
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
|
|
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
|
|
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
|
|
373
|
-
|
|
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
|
|
382
|
-
|
|
383
|
-
|
|
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
|
-
|
|
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
|
-
|
|
397
|
-
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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(
|
|
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
|
|
452
|
+
await fs3.ensureDir(this.SERVICES_DIR);
|
|
438
453
|
const packageServicesDir = this.getPackageServicesDir();
|
|
439
|
-
await
|
|
454
|
+
await fs3.copy(packageServicesDir, this.SERVICES_DIR, {
|
|
440
455
|
overwrite: true,
|
|
441
456
|
preserveTimestamps: true
|
|
442
457
|
});
|
|
443
|
-
const
|
|
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
|
|
465
|
+
const stat = await fs3.stat(filePath);
|
|
447
466
|
if (stat.isFile() && (file.endsWith(".py") || file.endsWith(".sh"))) {
|
|
448
|
-
await
|
|
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
|
|
514
|
+
const exists = await fs3.pathExists(this.SERVICES_DIR);
|
|
496
515
|
if (!exists) {
|
|
497
516
|
return false;
|
|
498
517
|
}
|
|
499
|
-
const files = await
|
|
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
|
|
541
|
+
const exists = await fs3.pathExists(this.SERVICES_DIR);
|
|
523
542
|
if (!exists) {
|
|
524
543
|
return [];
|
|
525
544
|
}
|
|
526
|
-
const files = await
|
|
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
|
|
556
|
+
const exists = await fs3.pathExists(this.SERVICES_DIR);
|
|
538
557
|
if (exists) {
|
|
539
|
-
await
|
|
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
|
|
1116
|
-
const configExists = await
|
|
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
|
|
1142
|
+
await fs3.writeJson(configPath, defaultConfig, { spaces: 2 });
|
|
1124
1143
|
} else {
|
|
1125
|
-
const existingConfig = await
|
|
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
|
|
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
|
|
1762
|
-
if (!await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
15571
|
+
await fs3.copy(targetPath, backupPath);
|
|
15319
15572
|
}
|
|
15320
|
-
await
|
|
15321
|
-
await
|
|
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
|
|
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
|
|
15883
|
+
await fs3.ensureDir(targetDirectory);
|
|
15631
15884
|
const junoTaskDir = path3.join(targetDirectory, ".juno_task");
|
|
15632
|
-
const junoTaskExists = await
|
|
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
|
|
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
|
|
15674
|
-
await
|
|
15675
|
-
await
|
|
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
|
|
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
|
|
15937
|
+
await fs3.writeFile(path3.join(junoTaskDir, "plan.md"), planContent);
|
|
15685
15938
|
}
|
|
15686
15939
|
const specsDir = path3.join(junoTaskDir, "specs");
|
|
15687
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
16468
|
+
const stats = await fs3.stat(sourcePath);
|
|
16216
16469
|
if (stats.isFile()) {
|
|
16217
|
-
await
|
|
16470
|
+
await fs3.copy(sourcePath, destPath);
|
|
16218
16471
|
if (scriptFile.endsWith(".sh")) {
|
|
16219
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
17585
|
-
await
|
|
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
|
|
17838
|
-
const coverageData = await
|
|
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
|
|
18022
|
-
await
|
|
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
|
|
18086
|
-
await
|
|
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
|
|
18144
|
-
await
|
|
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
|
|
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
|
|
18492
|
-
backupPath = path3.join(
|
|
18493
|
-
await
|
|
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
|
|
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
|
|
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
|
|
18913
|
+
if (!await fs3.pathExists(feedbackFile)) {
|
|
18661
18914
|
throw new Error(`Feedback file does not exist: ${feedbackFile}`);
|
|
18662
18915
|
}
|
|
18663
|
-
const content = await
|
|
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
|
|
18923
|
+
await fs3.ensureDir(path3.dirname(logFile));
|
|
18671
18924
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
18672
|
-
await
|
|
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
|
|
18942
|
+
await fs3.ensureDir(archiveDir);
|
|
18690
18943
|
await appendToArchive(archiveFile, parsed.resolvedIssues);
|
|
18691
18944
|
const compactedContent = generateCompactedFeedback(parsed.openIssues, parsed.metadata);
|
|
18692
|
-
await
|
|
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
|
|
18740
|
-
archiveContent = await
|
|
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
|
|
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
|
|
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
|
|
18807
|
-
const stats = await
|
|
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
|
|
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
|
|
19141
|
+
const content = await fs3.readFile(this.feedbackFile, "utf-8");
|
|
18889
19142
|
const updatedContent = this.addIssueToContent(content, issue, testCriteria);
|
|
18890
|
-
await
|
|
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
|
|
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
|
|
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
|
|
18941
|
-
await
|
|
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
|
|
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
|
|
19317
|
+
if (await fs3.pathExists(defaultClaudeFile)) {
|
|
19065
19318
|
filesToCompact.push(defaultClaudeFile);
|
|
19066
19319
|
}
|
|
19067
|
-
if (await
|
|
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
|
|
20254
|
+
if (!await fs3.pathExists(configPath)) {
|
|
20002
20255
|
return;
|
|
20003
20256
|
}
|
|
20004
20257
|
try {
|
|
20005
|
-
let content = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
23042
|
+
if (!await fs3.pathExists(configPath)) {
|
|
22790
23043
|
return false;
|
|
22791
23044
|
}
|
|
22792
|
-
const content = await
|
|
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
|
|
22815
|
-
const configExists = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
22888
|
-
await
|
|
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
|
|
23212
|
+
if (!await fs3.pathExists(sessionDir)) {
|
|
22960
23213
|
return [];
|
|
22961
23214
|
}
|
|
22962
|
-
const sessions = await
|
|
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
|
|
22977
|
-
const files = await
|
|
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
|
|
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
|
|
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
|
|
23094
|
-
await
|
|
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
|
|
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
|
|
23910
|
-
const
|
|
24162
|
+
const fs22 = await import('fs-extra');
|
|
24163
|
+
const path24 = await import('path');
|
|
23911
24164
|
const cwd2 = process.cwd();
|
|
23912
|
-
const junoTaskDir =
|
|
23913
|
-
if (await
|
|
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 =
|
|
23931
|
-
if (!allOptions2.prompt && await
|
|
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
|
|
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
|
|
24100
|
-
const
|
|
24101
|
-
const junoTaskDir =
|
|
24102
|
-
const isInitialized = await
|
|
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));
|