juno-code 1.0.34 → 1.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +364 -64
- package/dist/bin/cli.js +471 -186
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +470 -185
- 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/__pycache__/codex.cpython-38.pyc +0 -0
- package/dist/templates/services/claude.py +1 -1
- package/dist/templates/services/codex.py +68 -14
- 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
|
}
|
|
@@ -7434,7 +7466,7 @@ var init_shell_backend = __esm({
|
|
|
7434
7466
|
* Strategy:
|
|
7435
7467
|
* 1. Try to parse each line as JSON first (for Claude)
|
|
7436
7468
|
* 2. If JSON parsing fails, treat as TEXT streaming (for Codex and other text-based subagents)
|
|
7437
|
-
* 3. Emit all
|
|
7469
|
+
* 3. Emit all text lines (including whitespace-only) as progress events for real-time display
|
|
7438
7470
|
*/
|
|
7439
7471
|
parseAndEmitStreamingEvents(data, sessionId) {
|
|
7440
7472
|
if (!this.jsonBuffer) {
|
|
@@ -7444,14 +7476,35 @@ var init_shell_backend = __esm({
|
|
|
7444
7476
|
const lines = this.jsonBuffer.split("\n");
|
|
7445
7477
|
this.jsonBuffer = lines.pop() || "";
|
|
7446
7478
|
for (const line of lines) {
|
|
7447
|
-
const
|
|
7448
|
-
if (!
|
|
7479
|
+
const rawLine = line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
7480
|
+
if (!rawLine) continue;
|
|
7481
|
+
const hasNonWhitespace = rawLine.trim().length > 0;
|
|
7482
|
+
if (!hasNonWhitespace) {
|
|
7483
|
+
this.emitProgressEvent({
|
|
7484
|
+
sessionId,
|
|
7485
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
7486
|
+
backend: "shell",
|
|
7487
|
+
count: ++this.eventCounter,
|
|
7488
|
+
type: "thinking",
|
|
7489
|
+
content: rawLine,
|
|
7490
|
+
metadata: {
|
|
7491
|
+
format: "text",
|
|
7492
|
+
raw: true
|
|
7493
|
+
}
|
|
7494
|
+
}).catch((error) => {
|
|
7495
|
+
if (this.config?.debug) {
|
|
7496
|
+
engineLogger.warn(`Failed to emit whitespace-only streaming event: ${error instanceof Error ? error.message : String(error)}`);
|
|
7497
|
+
}
|
|
7498
|
+
});
|
|
7499
|
+
continue;
|
|
7500
|
+
}
|
|
7501
|
+
const trimmedLine = rawLine.trim();
|
|
7449
7502
|
let isJsonParsed = false;
|
|
7450
7503
|
try {
|
|
7451
7504
|
const jsonEvent = JSON.parse(trimmedLine);
|
|
7452
7505
|
let progressEvent;
|
|
7453
7506
|
if (this.isClaudeCliEvent(jsonEvent)) {
|
|
7454
|
-
progressEvent = this.convertClaudeEventToProgress(jsonEvent, sessionId,
|
|
7507
|
+
progressEvent = this.convertClaudeEventToProgress(jsonEvent, sessionId, rawLine);
|
|
7455
7508
|
isJsonParsed = true;
|
|
7456
7509
|
} else if (this.isGenericStreamingEvent(jsonEvent)) {
|
|
7457
7510
|
progressEvent = {
|
|
@@ -7486,7 +7539,7 @@ var init_shell_backend = __esm({
|
|
|
7486
7539
|
backend: "shell",
|
|
7487
7540
|
count: ++this.eventCounter,
|
|
7488
7541
|
type: "thinking",
|
|
7489
|
-
content:
|
|
7542
|
+
content: rawLine,
|
|
7490
7543
|
metadata: {
|
|
7491
7544
|
format: "text",
|
|
7492
7545
|
raw: true
|
|
@@ -12753,12 +12806,18 @@ async function mainCommandHandler(args, options, command) {
|
|
|
12753
12806
|
console.error(chalk15.red("\n\u274C Error: --allowed-tools and --append-allowed-tools are mutually exclusive. Use one or the other."));
|
|
12754
12807
|
process.exit(1);
|
|
12755
12808
|
}
|
|
12809
|
+
if (options.maxIterations !== void 0 && Number.isNaN(options.maxIterations)) {
|
|
12810
|
+
throw new ValidationError(
|
|
12811
|
+
"Max iterations must be a valid number",
|
|
12812
|
+
["Use -1 for unlimited iterations", "Use positive integers like 1, 5, or 10", "Example: -i 5"]
|
|
12813
|
+
);
|
|
12814
|
+
}
|
|
12756
12815
|
const executionRequest = createExecutionRequest({
|
|
12757
12816
|
instruction,
|
|
12758
12817
|
subagent: options.subagent,
|
|
12759
12818
|
backend: selectedBackend,
|
|
12760
12819
|
workingDirectory: config.workingDirectory,
|
|
12761
|
-
maxIterations: options.maxIterations
|
|
12820
|
+
maxIterations: options.maxIterations ?? config.defaultMaxIterations,
|
|
12762
12821
|
model: options.model || config.defaultModel,
|
|
12763
12822
|
agents: options.agents,
|
|
12764
12823
|
tools: options.tools,
|
|
@@ -12923,7 +12982,7 @@ var init_main = __esm({
|
|
|
12923
12982
|
return await this.collectInteractivePrompt();
|
|
12924
12983
|
} else {
|
|
12925
12984
|
const defaultPromptPath = path3.join(process.cwd(), ".juno_task", "prompt.md");
|
|
12926
|
-
if (await
|
|
12985
|
+
if (await fs3.pathExists(defaultPromptPath)) {
|
|
12927
12986
|
console.error(chalk15.blue(`\u{1F4C4} Using default prompt: ${chalk15.cyan(".juno_task/prompt.md")}`));
|
|
12928
12987
|
return await this.loadPromptFromFile(defaultPromptPath);
|
|
12929
12988
|
} else {
|
|
@@ -12951,7 +13010,7 @@ var init_main = __esm({
|
|
|
12951
13010
|
}
|
|
12952
13011
|
try {
|
|
12953
13012
|
const resolvedPath = path3.resolve(prompt);
|
|
12954
|
-
return await
|
|
13013
|
+
return await fs3.pathExists(resolvedPath);
|
|
12955
13014
|
} catch {
|
|
12956
13015
|
return false;
|
|
12957
13016
|
}
|
|
@@ -12959,7 +13018,7 @@ var init_main = __esm({
|
|
|
12959
13018
|
async loadPromptFromFile(filePath) {
|
|
12960
13019
|
try {
|
|
12961
13020
|
const resolvedPath = path3.resolve(filePath);
|
|
12962
|
-
const content = await
|
|
13021
|
+
const content = await fs3.readFile(resolvedPath, "utf-8");
|
|
12963
13022
|
if (!content.trim()) {
|
|
12964
13023
|
throw new FileSystemError(
|
|
12965
13024
|
"Prompt file is empty",
|
|
@@ -13281,6 +13340,221 @@ var init_main = __esm({
|
|
|
13281
13340
|
}
|
|
13282
13341
|
});
|
|
13283
13342
|
|
|
13343
|
+
// src/utils/script-installer.ts
|
|
13344
|
+
var script_installer_exports = {};
|
|
13345
|
+
__export(script_installer_exports, {
|
|
13346
|
+
ScriptInstaller: () => ScriptInstaller
|
|
13347
|
+
});
|
|
13348
|
+
var ScriptInstaller;
|
|
13349
|
+
var init_script_installer = __esm({
|
|
13350
|
+
"src/utils/script-installer.ts"() {
|
|
13351
|
+
init_version();
|
|
13352
|
+
ScriptInstaller = class {
|
|
13353
|
+
/**
|
|
13354
|
+
* Scripts that should be auto-installed if missing
|
|
13355
|
+
* These are critical scripts that users expect to be available
|
|
13356
|
+
*/
|
|
13357
|
+
/**
|
|
13358
|
+
* Required scripts include both standalone scripts and their dependencies.
|
|
13359
|
+
* kanban.sh depends on install_requirements.sh for Python venv setup.
|
|
13360
|
+
*/
|
|
13361
|
+
static REQUIRED_SCRIPTS = [
|
|
13362
|
+
"run_until_completion.sh",
|
|
13363
|
+
"kanban.sh",
|
|
13364
|
+
"install_requirements.sh"
|
|
13365
|
+
// Required by kanban.sh for Python venv creation
|
|
13366
|
+
];
|
|
13367
|
+
/**
|
|
13368
|
+
* Get the templates scripts directory from the package
|
|
13369
|
+
*/
|
|
13370
|
+
static getPackageScriptsDir() {
|
|
13371
|
+
const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
|
|
13372
|
+
const candidates = [
|
|
13373
|
+
path3.join(__dirname2, "..", "..", "templates", "scripts"),
|
|
13374
|
+
// dist (production)
|
|
13375
|
+
path3.join(__dirname2, "..", "templates", "scripts")
|
|
13376
|
+
// src (development)
|
|
13377
|
+
];
|
|
13378
|
+
for (const scriptsPath of candidates) {
|
|
13379
|
+
if (fs3.existsSync(scriptsPath)) {
|
|
13380
|
+
return scriptsPath;
|
|
13381
|
+
}
|
|
13382
|
+
}
|
|
13383
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13384
|
+
console.error("[DEBUG] ScriptInstaller: Could not find templates/scripts directory");
|
|
13385
|
+
console.error("[DEBUG] Tried:", candidates);
|
|
13386
|
+
}
|
|
13387
|
+
return null;
|
|
13388
|
+
}
|
|
13389
|
+
/**
|
|
13390
|
+
* Check if a specific script exists in the project's .juno_task/scripts/ directory
|
|
13391
|
+
*/
|
|
13392
|
+
static async scriptExists(projectDir, scriptName) {
|
|
13393
|
+
const scriptPath = path3.join(projectDir, ".juno_task", "scripts", scriptName);
|
|
13394
|
+
return fs3.pathExists(scriptPath);
|
|
13395
|
+
}
|
|
13396
|
+
/**
|
|
13397
|
+
* Install a specific script to the project's .juno_task/scripts/ directory
|
|
13398
|
+
* @param projectDir - The project root directory
|
|
13399
|
+
* @param scriptName - Name of the script to install (e.g., 'run_until_completion.sh')
|
|
13400
|
+
* @param silent - If true, suppresses console output
|
|
13401
|
+
* @returns true if script was installed, false if installation was skipped or failed
|
|
13402
|
+
*/
|
|
13403
|
+
static async installScript(projectDir, scriptName, silent = false) {
|
|
13404
|
+
try {
|
|
13405
|
+
const packageScriptsDir = this.getPackageScriptsDir();
|
|
13406
|
+
if (!packageScriptsDir) {
|
|
13407
|
+
if (!silent && process.env.JUNO_CODE_DEBUG === "1") {
|
|
13408
|
+
console.error("[DEBUG] ScriptInstaller: Package scripts directory not found");
|
|
13409
|
+
}
|
|
13410
|
+
return false;
|
|
13411
|
+
}
|
|
13412
|
+
const sourcePath = path3.join(packageScriptsDir, scriptName);
|
|
13413
|
+
if (!await fs3.pathExists(sourcePath)) {
|
|
13414
|
+
if (!silent && process.env.JUNO_CODE_DEBUG === "1") {
|
|
13415
|
+
console.error(`[DEBUG] ScriptInstaller: Source script not found: ${sourcePath}`);
|
|
13416
|
+
}
|
|
13417
|
+
return false;
|
|
13418
|
+
}
|
|
13419
|
+
const destDir = path3.join(projectDir, ".juno_task", "scripts");
|
|
13420
|
+
await fs3.ensureDir(destDir);
|
|
13421
|
+
const destPath = path3.join(destDir, scriptName);
|
|
13422
|
+
await fs3.copy(sourcePath, destPath, { overwrite: true });
|
|
13423
|
+
if (scriptName.endsWith(".sh") || scriptName.endsWith(".py")) {
|
|
13424
|
+
await fs3.chmod(destPath, 493);
|
|
13425
|
+
}
|
|
13426
|
+
if (!silent) {
|
|
13427
|
+
console.log(`\u2713 Installed script: ${scriptName} to .juno_task/scripts/`);
|
|
13428
|
+
}
|
|
13429
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13430
|
+
console.error(`[DEBUG] ScriptInstaller: Installed ${scriptName} to ${destPath}`);
|
|
13431
|
+
}
|
|
13432
|
+
return true;
|
|
13433
|
+
} catch (error) {
|
|
13434
|
+
if (!silent && process.env.JUNO_CODE_DEBUG === "1") {
|
|
13435
|
+
console.error(`[DEBUG] ScriptInstaller: Failed to install ${scriptName}:`, error);
|
|
13436
|
+
}
|
|
13437
|
+
return false;
|
|
13438
|
+
}
|
|
13439
|
+
}
|
|
13440
|
+
/**
|
|
13441
|
+
* Check which required scripts are missing from the project
|
|
13442
|
+
* @param projectDir - The project root directory
|
|
13443
|
+
* @returns Array of missing script names
|
|
13444
|
+
*/
|
|
13445
|
+
static async getMissingScripts(projectDir) {
|
|
13446
|
+
const missing = [];
|
|
13447
|
+
for (const script of this.REQUIRED_SCRIPTS) {
|
|
13448
|
+
if (!await this.scriptExists(projectDir, script)) {
|
|
13449
|
+
missing.push(script);
|
|
13450
|
+
}
|
|
13451
|
+
}
|
|
13452
|
+
return missing;
|
|
13453
|
+
}
|
|
13454
|
+
/**
|
|
13455
|
+
* Auto-install any missing required scripts
|
|
13456
|
+
* This should be called on CLI startup for initialized projects
|
|
13457
|
+
* @param projectDir - The project root directory
|
|
13458
|
+
* @param silent - If true, suppresses console output
|
|
13459
|
+
* @returns true if any scripts were installed
|
|
13460
|
+
*/
|
|
13461
|
+
static async autoInstallMissing(projectDir, silent = true) {
|
|
13462
|
+
try {
|
|
13463
|
+
const junoTaskDir = path3.join(projectDir, ".juno_task");
|
|
13464
|
+
if (!await fs3.pathExists(junoTaskDir)) {
|
|
13465
|
+
return false;
|
|
13466
|
+
}
|
|
13467
|
+
const missing = await this.getMissingScripts(projectDir);
|
|
13468
|
+
if (missing.length === 0) {
|
|
13469
|
+
return false;
|
|
13470
|
+
}
|
|
13471
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13472
|
+
console.error(`[DEBUG] ScriptInstaller: Missing scripts: ${missing.join(", ")}`);
|
|
13473
|
+
}
|
|
13474
|
+
let installedAny = false;
|
|
13475
|
+
for (const script of missing) {
|
|
13476
|
+
const installed = await this.installScript(projectDir, script, silent);
|
|
13477
|
+
if (installed) {
|
|
13478
|
+
installedAny = true;
|
|
13479
|
+
}
|
|
13480
|
+
}
|
|
13481
|
+
if (installedAny && !silent) {
|
|
13482
|
+
console.log(`\u2713 Auto-installed ${missing.length} missing script(s)`);
|
|
13483
|
+
}
|
|
13484
|
+
return installedAny;
|
|
13485
|
+
} catch (error) {
|
|
13486
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13487
|
+
console.error("[DEBUG] ScriptInstaller: autoInstallMissing error:", error);
|
|
13488
|
+
}
|
|
13489
|
+
return false;
|
|
13490
|
+
}
|
|
13491
|
+
}
|
|
13492
|
+
/**
|
|
13493
|
+
* Update a script if the package version is newer (by content comparison)
|
|
13494
|
+
* @param projectDir - The project root directory
|
|
13495
|
+
* @param scriptName - Name of the script to update
|
|
13496
|
+
* @param silent - If true, suppresses console output
|
|
13497
|
+
* @returns true if script was updated
|
|
13498
|
+
*/
|
|
13499
|
+
static async updateScriptIfNewer(projectDir, scriptName, silent = true) {
|
|
13500
|
+
try {
|
|
13501
|
+
const packageScriptsDir = this.getPackageScriptsDir();
|
|
13502
|
+
if (!packageScriptsDir) {
|
|
13503
|
+
return false;
|
|
13504
|
+
}
|
|
13505
|
+
const sourcePath = path3.join(packageScriptsDir, scriptName);
|
|
13506
|
+
const destPath = path3.join(projectDir, ".juno_task", "scripts", scriptName);
|
|
13507
|
+
if (!await fs3.pathExists(destPath)) {
|
|
13508
|
+
return this.installScript(projectDir, scriptName, silent);
|
|
13509
|
+
}
|
|
13510
|
+
const [sourceContent, destContent] = await Promise.all([
|
|
13511
|
+
fs3.readFile(sourcePath, "utf-8"),
|
|
13512
|
+
fs3.readFile(destPath, "utf-8")
|
|
13513
|
+
]);
|
|
13514
|
+
if (sourceContent !== destContent) {
|
|
13515
|
+
await fs3.copy(sourcePath, destPath, { overwrite: true });
|
|
13516
|
+
if (scriptName.endsWith(".sh") || scriptName.endsWith(".py")) {
|
|
13517
|
+
await fs3.chmod(destPath, 493);
|
|
13518
|
+
}
|
|
13519
|
+
if (!silent) {
|
|
13520
|
+
console.log(`\u2713 Updated script: ${scriptName}`);
|
|
13521
|
+
}
|
|
13522
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13523
|
+
console.error(`[DEBUG] ScriptInstaller: Updated ${scriptName} (content changed)`);
|
|
13524
|
+
}
|
|
13525
|
+
return true;
|
|
13526
|
+
}
|
|
13527
|
+
return false;
|
|
13528
|
+
} catch (error) {
|
|
13529
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13530
|
+
console.error(`[DEBUG] ScriptInstaller: updateScriptIfNewer error for ${scriptName}:`, error);
|
|
13531
|
+
}
|
|
13532
|
+
return false;
|
|
13533
|
+
}
|
|
13534
|
+
}
|
|
13535
|
+
/**
|
|
13536
|
+
* Get the path to a script in the project's .juno_task/scripts/ directory
|
|
13537
|
+
*/
|
|
13538
|
+
static getScriptPath(projectDir, scriptName) {
|
|
13539
|
+
return path3.join(projectDir, ".juno_task", "scripts", scriptName);
|
|
13540
|
+
}
|
|
13541
|
+
/**
|
|
13542
|
+
* List all required scripts and their installation status
|
|
13543
|
+
*/
|
|
13544
|
+
static async listRequiredScripts(projectDir) {
|
|
13545
|
+
const results = [];
|
|
13546
|
+
for (const script of this.REQUIRED_SCRIPTS) {
|
|
13547
|
+
results.push({
|
|
13548
|
+
name: script,
|
|
13549
|
+
installed: await this.scriptExists(projectDir, script)
|
|
13550
|
+
});
|
|
13551
|
+
}
|
|
13552
|
+
return results;
|
|
13553
|
+
}
|
|
13554
|
+
};
|
|
13555
|
+
}
|
|
13556
|
+
});
|
|
13557
|
+
|
|
13284
13558
|
// src/utils/startup-validation.ts
|
|
13285
13559
|
var startup_validation_exports = {};
|
|
13286
13560
|
__export(startup_validation_exports, {
|
|
@@ -13371,7 +13645,7 @@ async function validateJSONFile(configSchema, baseDir) {
|
|
|
13371
13645
|
const warnings = [];
|
|
13372
13646
|
const filePath = path3__default.join(baseDir, configSchema.file);
|
|
13373
13647
|
try {
|
|
13374
|
-
const exists = await
|
|
13648
|
+
const exists = await fs3.pathExists(filePath);
|
|
13375
13649
|
if (!exists) {
|
|
13376
13650
|
if (configSchema.required) {
|
|
13377
13651
|
errors.push({
|
|
@@ -13394,7 +13668,7 @@ async function validateJSONFile(configSchema, baseDir) {
|
|
|
13394
13668
|
return { isValid: !configSchema.required, errors, warnings };
|
|
13395
13669
|
}
|
|
13396
13670
|
try {
|
|
13397
|
-
await
|
|
13671
|
+
await fs3.access(filePath, fs3.constants.R_OK);
|
|
13398
13672
|
} catch (accessError) {
|
|
13399
13673
|
errors.push({
|
|
13400
13674
|
file: configSchema.file,
|
|
@@ -13410,7 +13684,7 @@ async function validateJSONFile(configSchema, baseDir) {
|
|
|
13410
13684
|
}
|
|
13411
13685
|
let jsonData;
|
|
13412
13686
|
try {
|
|
13413
|
-
const fileContent = await
|
|
13687
|
+
const fileContent = await fs3.readFile(filePath, "utf8");
|
|
13414
13688
|
jsonData = JSON.parse(fileContent);
|
|
13415
13689
|
} catch (parseError) {
|
|
13416
13690
|
const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
|
|
@@ -13545,7 +13819,7 @@ function displayValidationResults(result) {
|
|
|
13545
13819
|
}
|
|
13546
13820
|
async function logValidationResults(result, baseDir = process.cwd()) {
|
|
13547
13821
|
const logDir = path3__default.join(baseDir, ".juno_task", "logs");
|
|
13548
|
-
await
|
|
13822
|
+
await fs3.ensureDir(logDir);
|
|
13549
13823
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13550
13824
|
const logFile = path3__default.join(logDir, `startup-validation-${timestamp}.log`);
|
|
13551
13825
|
const logContent = [
|
|
@@ -13588,7 +13862,7 @@ async function logValidationResults(result, baseDir = process.cwd()) {
|
|
|
13588
13862
|
logContent.push(``);
|
|
13589
13863
|
}
|
|
13590
13864
|
}
|
|
13591
|
-
await
|
|
13865
|
+
await fs3.writeFile(logFile, logContent.join("\n"));
|
|
13592
13866
|
return logFile;
|
|
13593
13867
|
}
|
|
13594
13868
|
async function validateStartupConfigs(baseDir = process.cwd(), verbose = false) {
|
|
@@ -13922,7 +14196,7 @@ var TemplateEngine = class {
|
|
|
13922
14196
|
let overallError;
|
|
13923
14197
|
try {
|
|
13924
14198
|
if (!options.dryRun) {
|
|
13925
|
-
await
|
|
14199
|
+
await fs3.ensureDir(targetDirectory);
|
|
13926
14200
|
}
|
|
13927
14201
|
for (const template of templates) {
|
|
13928
14202
|
try {
|
|
@@ -15272,7 +15546,7 @@ This directory contains specification documents for your project.
|
|
|
15272
15546
|
const fileName = this.getTemplateFileName(template);
|
|
15273
15547
|
const targetPath = path3.join(targetDirectory, fileName);
|
|
15274
15548
|
try {
|
|
15275
|
-
const fileExists = await
|
|
15549
|
+
const fileExists = await fs3.pathExists(targetPath);
|
|
15276
15550
|
if (fileExists && !options.force && options.onConflict !== "overwrite") {
|
|
15277
15551
|
return {
|
|
15278
15552
|
path: targetPath,
|
|
@@ -15294,10 +15568,10 @@ This directory contains specification documents for your project.
|
|
|
15294
15568
|
}
|
|
15295
15569
|
if (options.createBackup && fileExists) {
|
|
15296
15570
|
const backupPath = `${targetPath}.backup.${Date.now()}`;
|
|
15297
|
-
await
|
|
15571
|
+
await fs3.copy(targetPath, backupPath);
|
|
15298
15572
|
}
|
|
15299
|
-
await
|
|
15300
|
-
await
|
|
15573
|
+
await fs3.ensureDir(path3.dirname(targetPath));
|
|
15574
|
+
await fs3.writeFile(targetPath, renderedContent, "utf8");
|
|
15301
15575
|
return {
|
|
15302
15576
|
path: targetPath,
|
|
15303
15577
|
content: renderedContent,
|
|
@@ -15561,7 +15835,7 @@ var SimpleInitTUI = class {
|
|
|
15561
15835
|
}
|
|
15562
15836
|
async confirmSave(targetDirectory) {
|
|
15563
15837
|
const junoTaskPath = path3.join(targetDirectory, ".juno_task");
|
|
15564
|
-
if (await
|
|
15838
|
+
if (await fs3.pathExists(junoTaskPath)) {
|
|
15565
15839
|
console.log(chalk15.yellow(" \u26A0\uFE0F .juno_task directory already exists"));
|
|
15566
15840
|
console.log(chalk15.gray(" Would you like to:"));
|
|
15567
15841
|
console.log(chalk15.gray(" 1) Override existing files"));
|
|
@@ -15606,16 +15880,16 @@ var SimpleProjectGenerator = class {
|
|
|
15606
15880
|
async generate() {
|
|
15607
15881
|
const { targetDirectory, variables, force } = this.context;
|
|
15608
15882
|
console.log(chalk15.blue("\u{1F4C1} Creating project directory..."));
|
|
15609
|
-
await
|
|
15883
|
+
await fs3.ensureDir(targetDirectory);
|
|
15610
15884
|
const junoTaskDir = path3.join(targetDirectory, ".juno_task");
|
|
15611
|
-
const junoTaskExists = await
|
|
15885
|
+
const junoTaskExists = await fs3.pathExists(junoTaskDir);
|
|
15612
15886
|
if (junoTaskExists && !force) {
|
|
15613
15887
|
throw new ValidationError(
|
|
15614
15888
|
"Project already initialized. Directory .juno_task already exists.",
|
|
15615
15889
|
["Use --force flag to overwrite existing files", "Choose a different directory"]
|
|
15616
15890
|
);
|
|
15617
15891
|
}
|
|
15618
|
-
await
|
|
15892
|
+
await fs3.ensureDir(junoTaskDir);
|
|
15619
15893
|
console.log(chalk15.blue("\u2699\uFE0F Creating project configuration..."));
|
|
15620
15894
|
await this.createConfigFile(junoTaskDir, targetDirectory);
|
|
15621
15895
|
console.log(chalk15.blue("\u{1F527} Setting up MCP configuration..."));
|
|
@@ -15649,21 +15923,21 @@ var SimpleProjectGenerator = class {
|
|
|
15649
15923
|
const promptContent = await templateEngine.render(promptTemplate, templateContext);
|
|
15650
15924
|
const initContent = await templateEngine.render(initTemplate, templateContext);
|
|
15651
15925
|
const implementContent = await templateEngine.render(implementTemplate, templateContext);
|
|
15652
|
-
await
|
|
15653
|
-
await
|
|
15654
|
-
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);
|
|
15655
15929
|
const userFeedbackTemplate = templateEngine.getBuiltInTemplate("USER_FEEDBACK.md");
|
|
15656
15930
|
if (userFeedbackTemplate) {
|
|
15657
15931
|
const userFeedbackContent = await templateEngine.render(userFeedbackTemplate, templateContext);
|
|
15658
|
-
await
|
|
15932
|
+
await fs3.writeFile(path3.join(junoTaskDir, "USER_FEEDBACK.md"), userFeedbackContent);
|
|
15659
15933
|
}
|
|
15660
15934
|
const planTemplate = templateEngine.getBuiltInTemplate("plan.md");
|
|
15661
15935
|
if (planTemplate) {
|
|
15662
15936
|
const planContent = await templateEngine.render(planTemplate, templateContext);
|
|
15663
|
-
await
|
|
15937
|
+
await fs3.writeFile(path3.join(junoTaskDir, "plan.md"), planContent);
|
|
15664
15938
|
}
|
|
15665
15939
|
const specsDir = path3.join(junoTaskDir, "specs");
|
|
15666
|
-
await
|
|
15940
|
+
await fs3.ensureDir(specsDir);
|
|
15667
15941
|
const specsReadmeContent = `# Project Specifications
|
|
15668
15942
|
|
|
15669
15943
|
This directory contains detailed specifications for the project components.
|
|
@@ -15680,7 +15954,7 @@ This directory contains detailed specifications for the project components.
|
|
|
15680
15954
|
- Avoid conflicts with existing file names
|
|
15681
15955
|
- Use \`.md\` extension for all specification files
|
|
15682
15956
|
`;
|
|
15683
|
-
await
|
|
15957
|
+
await fs3.writeFile(path3.join(specsDir, "README.md"), specsReadmeContent);
|
|
15684
15958
|
const requirementsContent = `# Requirements Specification
|
|
15685
15959
|
|
|
15686
15960
|
## Functional Requirements
|
|
@@ -15727,7 +16001,7 @@ This directory contains detailed specifications for the project components.
|
|
|
15727
16001
|
- Code quality: Clean, maintainable codebase
|
|
15728
16002
|
- Documentation: Complete and accurate documentation
|
|
15729
16003
|
`;
|
|
15730
|
-
await
|
|
16004
|
+
await fs3.writeFile(path3.join(specsDir, "requirements.md"), requirementsContent);
|
|
15731
16005
|
const architectureContent = `# Architecture Specification
|
|
15732
16006
|
|
|
15733
16007
|
## System Overview
|
|
@@ -15809,7 +16083,7 @@ This project uses AI-assisted development with juno-code to achieve: ${variables
|
|
|
15809
16083
|
- Performance monitoring
|
|
15810
16084
|
- Security best practices
|
|
15811
16085
|
`;
|
|
15812
|
-
await
|
|
16086
|
+
await fs3.writeFile(path3.join(specsDir, "architecture.md"), architectureContent);
|
|
15813
16087
|
const claudeContent = `# Claude Development Session Learnings
|
|
15814
16088
|
|
|
15815
16089
|
## Project Overview
|
|
@@ -15881,7 +16155,7 @@ This file will be updated as development progresses to track:
|
|
|
15881
16155
|
- Solutions to complex problems
|
|
15882
16156
|
- Performance improvements and optimizations
|
|
15883
16157
|
`;
|
|
15884
|
-
await
|
|
16158
|
+
await fs3.writeFile(path3.join(targetDirectory, "CLAUDE.md"), claudeContent);
|
|
15885
16159
|
const agentsContent = `# AI Agent Selection and Performance
|
|
15886
16160
|
|
|
15887
16161
|
## Available Agents
|
|
@@ -15945,7 +16219,7 @@ Track agent performance for:
|
|
|
15945
16219
|
4. **Feedback Loop**: Provide feedback to improve agent performance
|
|
15946
16220
|
5. **Performance Monitoring**: Track and optimize agent usage
|
|
15947
16221
|
`;
|
|
15948
|
-
await
|
|
16222
|
+
await fs3.writeFile(path3.join(targetDirectory, "AGENTS.md"), agentsContent);
|
|
15949
16223
|
const readmeContent = `# ${variables.PROJECT_NAME}
|
|
15950
16224
|
|
|
15951
16225
|
${variables.DESCRIPTION}
|
|
@@ -16042,7 +16316,7 @@ ${variables.GIT_URL}` : ""}
|
|
|
16042
16316
|
Created with juno-code on ${variables.CURRENT_DATE}
|
|
16043
16317
|
${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
16044
16318
|
`;
|
|
16045
|
-
await
|
|
16319
|
+
await fs3.writeFile(path3.join(targetDirectory, "README.md"), readmeContent);
|
|
16046
16320
|
console.log(chalk15.blue("\u{1F4E6} Installing utility scripts..."));
|
|
16047
16321
|
await this.copyScriptsFromTemplates(junoTaskDir);
|
|
16048
16322
|
console.log(chalk15.blue("\u{1F40D} Installing Python requirements..."));
|
|
@@ -16094,7 +16368,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16094
16368
|
hooks: getDefaultHooks()
|
|
16095
16369
|
};
|
|
16096
16370
|
const configPath = path3.join(junoTaskDir, "config.json");
|
|
16097
|
-
await
|
|
16371
|
+
await fs3.writeFile(configPath, JSON.stringify(configContent, null, 2));
|
|
16098
16372
|
console.log(chalk15.green(` \u2713 Created .juno_task/config.json with ${this.context.subagent} as default subagent`));
|
|
16099
16373
|
}
|
|
16100
16374
|
async createMcpFile(junoTaskDir, targetDirectory) {
|
|
@@ -16148,7 +16422,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16148
16422
|
}
|
|
16149
16423
|
};
|
|
16150
16424
|
const mcpPath = path3.join(junoTaskDir, "mcp.json");
|
|
16151
|
-
await
|
|
16425
|
+
await fs3.writeFile(mcpPath, JSON.stringify(mcpContent, null, 2));
|
|
16152
16426
|
console.log(chalk15.green(` \u2713 Created .juno_task/mcp.json with roundtable-ai server configuration`));
|
|
16153
16427
|
}
|
|
16154
16428
|
getDefaultModelForSubagent(subagent) {
|
|
@@ -16167,7 +16441,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16167
16441
|
async copyScriptsFromTemplates(junoTaskDir) {
|
|
16168
16442
|
try {
|
|
16169
16443
|
const scriptsDir = path3.join(junoTaskDir, "scripts");
|
|
16170
|
-
await
|
|
16444
|
+
await fs3.ensureDir(scriptsDir);
|
|
16171
16445
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
16172
16446
|
const __dirname2 = path3.dirname(__filename2);
|
|
16173
16447
|
let templatesScriptsDir;
|
|
@@ -16178,11 +16452,11 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16178
16452
|
} else {
|
|
16179
16453
|
templatesScriptsDir = path3.join(__dirname2, "../../templates/scripts");
|
|
16180
16454
|
}
|
|
16181
|
-
if (!await
|
|
16455
|
+
if (!await fs3.pathExists(templatesScriptsDir)) {
|
|
16182
16456
|
console.log(chalk15.yellow(" \u26A0\uFE0F Template scripts directory not found, skipping script installation"));
|
|
16183
16457
|
return;
|
|
16184
16458
|
}
|
|
16185
|
-
const scriptFiles = await
|
|
16459
|
+
const scriptFiles = await fs3.readdir(templatesScriptsDir);
|
|
16186
16460
|
if (scriptFiles.length === 0) {
|
|
16187
16461
|
console.log(chalk15.gray(" \u2139\uFE0F No template scripts found to install"));
|
|
16188
16462
|
return;
|
|
@@ -16191,11 +16465,11 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16191
16465
|
for (const scriptFile of scriptFiles) {
|
|
16192
16466
|
const sourcePath = path3.join(templatesScriptsDir, scriptFile);
|
|
16193
16467
|
const destPath = path3.join(scriptsDir, scriptFile);
|
|
16194
|
-
const stats = await
|
|
16468
|
+
const stats = await fs3.stat(sourcePath);
|
|
16195
16469
|
if (stats.isFile()) {
|
|
16196
|
-
await
|
|
16470
|
+
await fs3.copy(sourcePath, destPath);
|
|
16197
16471
|
if (scriptFile.endsWith(".sh")) {
|
|
16198
|
-
await
|
|
16472
|
+
await fs3.chmod(destPath, 493);
|
|
16199
16473
|
}
|
|
16200
16474
|
copiedCount++;
|
|
16201
16475
|
console.log(chalk15.green(` \u2713 Installed script: ${scriptFile}`));
|
|
@@ -16218,7 +16492,7 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16218
16492
|
try {
|
|
16219
16493
|
const scriptsDir = path3.join(junoTaskDir, "scripts");
|
|
16220
16494
|
const installScript = path3.join(scriptsDir, "install_requirements.sh");
|
|
16221
|
-
if (!await
|
|
16495
|
+
if (!await fs3.pathExists(installScript)) {
|
|
16222
16496
|
console.log(chalk15.yellow(" \u26A0\uFE0F install_requirements.sh not found, skipping Python dependencies installation"));
|
|
16223
16497
|
console.log(chalk15.gray(" You can install dependencies manually: juno-kanban, roundtable-ai"));
|
|
16224
16498
|
return;
|
|
@@ -16515,20 +16789,20 @@ init_types();
|
|
|
16515
16789
|
async function loadInitPrompt(directory) {
|
|
16516
16790
|
const junoTaskDir = path3.join(directory, ".juno_task");
|
|
16517
16791
|
const initFile = path3.join(junoTaskDir, "init.md");
|
|
16518
|
-
if (!await
|
|
16792
|
+
if (!await fs3.pathExists(junoTaskDir)) {
|
|
16519
16793
|
throw new FileSystemError(
|
|
16520
16794
|
'No .juno_task directory found. Run "juno-code init" first.',
|
|
16521
16795
|
junoTaskDir
|
|
16522
16796
|
);
|
|
16523
16797
|
}
|
|
16524
|
-
if (!await
|
|
16798
|
+
if (!await fs3.pathExists(initFile)) {
|
|
16525
16799
|
throw new FileSystemError(
|
|
16526
16800
|
"No init.md file found in .juno_task directory",
|
|
16527
16801
|
initFile
|
|
16528
16802
|
);
|
|
16529
16803
|
}
|
|
16530
16804
|
try {
|
|
16531
|
-
const content = await
|
|
16805
|
+
const content = await fs3.readFile(initFile, "utf-8");
|
|
16532
16806
|
if (!content.trim()) {
|
|
16533
16807
|
throw new FileSystemError(
|
|
16534
16808
|
"init.md file is empty. Please add task instructions.",
|
|
@@ -17560,8 +17834,8 @@ Focus on ${request.intelligence === "basic" ? "basic functionality" : request.in
|
|
|
17560
17834
|
for (const scenario of scenarios) {
|
|
17561
17835
|
const testContent = await this.generateTestContent(request, scenario, template);
|
|
17562
17836
|
const testFilePath = this.resolveTestFilePath(request, scenario);
|
|
17563
|
-
await
|
|
17564
|
-
await
|
|
17837
|
+
await fs3.ensureDir(path3.dirname(testFilePath));
|
|
17838
|
+
await fs3.writeFile(testFilePath, testContent);
|
|
17565
17839
|
testFiles.push(testFilePath);
|
|
17566
17840
|
}
|
|
17567
17841
|
return testFiles;
|
|
@@ -17813,8 +18087,8 @@ var TestExecutionEngine = class {
|
|
|
17813
18087
|
async collectCoverage(request) {
|
|
17814
18088
|
const coverageFile = path3.join(request.workingDirectory, "coverage", "coverage-summary.json");
|
|
17815
18089
|
try {
|
|
17816
|
-
if (await
|
|
17817
|
-
const coverageData = await
|
|
18090
|
+
if (await fs3.pathExists(coverageFile)) {
|
|
18091
|
+
const coverageData = await fs3.readJson(coverageFile);
|
|
17818
18092
|
return {
|
|
17819
18093
|
lines: coverageData.total?.lines?.pct || 0,
|
|
17820
18094
|
functions: coverageData.total?.functions?.pct || 0,
|
|
@@ -17997,8 +18271,8 @@ var TestReportEngine = class {
|
|
|
17997
18271
|
suggestions: analysis.suggestions,
|
|
17998
18272
|
recommendations: analysis.recommendations
|
|
17999
18273
|
};
|
|
18000
|
-
await
|
|
18001
|
-
await
|
|
18274
|
+
await fs3.ensureDir(path3.dirname(outputPath));
|
|
18275
|
+
await fs3.writeJson(outputPath, report, { spaces: 2 });
|
|
18002
18276
|
return outputPath;
|
|
18003
18277
|
}
|
|
18004
18278
|
async generateHTMLReport(analysis, outputPath, includeVisualizations) {
|
|
@@ -18061,8 +18335,8 @@ var TestReportEngine = class {
|
|
|
18061
18335
|
</body>
|
|
18062
18336
|
</html>
|
|
18063
18337
|
`.trim();
|
|
18064
|
-
await
|
|
18065
|
-
await
|
|
18338
|
+
await fs3.ensureDir(path3.dirname(outputPath));
|
|
18339
|
+
await fs3.writeFile(outputPath, html);
|
|
18066
18340
|
return outputPath;
|
|
18067
18341
|
}
|
|
18068
18342
|
generateCharts(analysis) {
|
|
@@ -18119,8 +18393,8 @@ ${analysis.suggestions.map((suggestion) => `- ${suggestion}`).join("\n")}
|
|
|
18119
18393
|
|
|
18120
18394
|
${analysis.recommendations.map((rec) => `- ${rec}`).join("\n")}
|
|
18121
18395
|
`.trim();
|
|
18122
|
-
await
|
|
18123
|
-
await
|
|
18396
|
+
await fs3.ensureDir(path3.dirname(outputPath));
|
|
18397
|
+
await fs3.writeFile(outputPath, markdown);
|
|
18124
18398
|
return outputPath;
|
|
18125
18399
|
}
|
|
18126
18400
|
async displayConsoleReport(analysis) {
|
|
@@ -18460,16 +18734,16 @@ async function compactConfigFile(filePath, options = {}) {
|
|
|
18460
18734
|
preserveDays = 30,
|
|
18461
18735
|
preservePatterns = []
|
|
18462
18736
|
} = options;
|
|
18463
|
-
const originalContent = await
|
|
18737
|
+
const originalContent = await fs3.readFile(filePath, "utf-8");
|
|
18464
18738
|
const originalSize = originalContent.length;
|
|
18465
18739
|
let backupPath = "";
|
|
18466
18740
|
if (createBackup && !dryRun) {
|
|
18467
18741
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
18468
18742
|
const ext = path3.extname(filePath);
|
|
18469
18743
|
const basename11 = path3.basename(filePath, ext);
|
|
18470
|
-
const
|
|
18471
|
-
backupPath = path3.join(
|
|
18472
|
-
await
|
|
18744
|
+
const dirname13 = path3.dirname(filePath);
|
|
18745
|
+
backupPath = path3.join(dirname13, `${basename11}.backup.${timestamp}${ext}`);
|
|
18746
|
+
await fs3.writeFile(backupPath, originalContent, "utf-8");
|
|
18473
18747
|
}
|
|
18474
18748
|
const compactionAnalysis = analyzeMarkdownStructure(originalContent);
|
|
18475
18749
|
const compactedContent = compactMarkdownContent(
|
|
@@ -18484,7 +18758,7 @@ async function compactConfigFile(filePath, options = {}) {
|
|
|
18484
18758
|
const compactedSize = finalContent.length;
|
|
18485
18759
|
const reductionPercentage = Math.round((originalSize - compactedSize) / originalSize * 100);
|
|
18486
18760
|
if (!dryRun) {
|
|
18487
|
-
await
|
|
18761
|
+
await fs3.writeFile(filePath, finalContent, "utf-8");
|
|
18488
18762
|
}
|
|
18489
18763
|
return {
|
|
18490
18764
|
originalSize,
|
|
@@ -18614,7 +18888,7 @@ function formatFileSize(bytes) {
|
|
|
18614
18888
|
}
|
|
18615
18889
|
async function shouldCompactFile(filePath, sizeThresholdKB = 50, ageThresholdDays = 30) {
|
|
18616
18890
|
try {
|
|
18617
|
-
const stats = await
|
|
18891
|
+
const stats = await fs3.stat(filePath);
|
|
18618
18892
|
const sizeKB = stats.size / 1024;
|
|
18619
18893
|
if (sizeKB > sizeThresholdKB) {
|
|
18620
18894
|
return true;
|
|
@@ -18636,19 +18910,19 @@ async function archiveResolvedIssues(options) {
|
|
|
18636
18910
|
feedbackFile,
|
|
18637
18911
|
archiveDir = path3.join(path3.dirname(feedbackFile), "archives"),
|
|
18638
18912
|
openIssuesThreshold = 10} = options;
|
|
18639
|
-
if (!await
|
|
18913
|
+
if (!await fs3.pathExists(feedbackFile)) {
|
|
18640
18914
|
throw new Error(`Feedback file does not exist: ${feedbackFile}`);
|
|
18641
18915
|
}
|
|
18642
|
-
const content = await
|
|
18916
|
+
const content = await fs3.readFile(feedbackFile, "utf-8");
|
|
18643
18917
|
const parsed = parseUserFeedback(content);
|
|
18644
18918
|
const warningsGenerated = [];
|
|
18645
18919
|
if (parsed.openIssues.length > openIssuesThreshold) {
|
|
18646
18920
|
const warning = `Found ${parsed.openIssues.length} open issues (threshold: ${openIssuesThreshold}). Consider reviewing and prioritizing.`;
|
|
18647
18921
|
warningsGenerated.push(warning);
|
|
18648
18922
|
const logFile = path3.join(path3.dirname(feedbackFile), "logs", "feedback-warnings.log");
|
|
18649
|
-
await
|
|
18923
|
+
await fs3.ensureDir(path3.dirname(logFile));
|
|
18650
18924
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
18651
|
-
await
|
|
18925
|
+
await fs3.appendFile(logFile, `[${timestamp}] ${warning}
|
|
18652
18926
|
`);
|
|
18653
18927
|
}
|
|
18654
18928
|
if (parsed.resolvedIssues.length === 0) {
|
|
@@ -18665,10 +18939,10 @@ async function archiveResolvedIssues(options) {
|
|
|
18665
18939
|
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
18666
18940
|
const archiveFile = path3.join(archiveDir, `USER_FEEDBACK_archive_${currentYear}.md`);
|
|
18667
18941
|
{
|
|
18668
|
-
await
|
|
18942
|
+
await fs3.ensureDir(archiveDir);
|
|
18669
18943
|
await appendToArchive(archiveFile, parsed.resolvedIssues);
|
|
18670
18944
|
const compactedContent = generateCompactedFeedback(parsed.openIssues, parsed.metadata);
|
|
18671
|
-
await
|
|
18945
|
+
await fs3.writeFile(feedbackFile, compactedContent, "utf-8");
|
|
18672
18946
|
}
|
|
18673
18947
|
{
|
|
18674
18948
|
console.log(chalk15.green(`\u2705 Archived ${parsed.resolvedIssues.length} resolved issues`));
|
|
@@ -18715,8 +18989,8 @@ function parseUserFeedback(content) {
|
|
|
18715
18989
|
async function appendToArchive(archiveFile, resolvedIssues) {
|
|
18716
18990
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
18717
18991
|
let archiveContent = "";
|
|
18718
|
-
if (await
|
|
18719
|
-
archiveContent = await
|
|
18992
|
+
if (await fs3.pathExists(archiveFile)) {
|
|
18993
|
+
archiveContent = await fs3.readFile(archiveFile, "utf-8");
|
|
18720
18994
|
} else {
|
|
18721
18995
|
const year = path3.basename(archiveFile).match(/(\d{4})/)?.[1] || (/* @__PURE__ */ new Date()).getFullYear();
|
|
18722
18996
|
archiveContent = `# User Feedback Archive ${year}
|
|
@@ -18750,7 +19024,7 @@ ${resolvedIssue}
|
|
|
18750
19024
|
`- Last updated: ${timestamp}`
|
|
18751
19025
|
);
|
|
18752
19026
|
}
|
|
18753
|
-
await
|
|
19027
|
+
await fs3.writeFile(archiveFile, archiveContent, "utf-8");
|
|
18754
19028
|
}
|
|
18755
19029
|
function generateCompactedFeedback(openIssues, metadata) {
|
|
18756
19030
|
let content = metadata.trim() + "\n";
|
|
@@ -18775,15 +19049,15 @@ async function shouldArchive(feedbackFile, options = {}) {
|
|
|
18775
19049
|
// 50KB
|
|
18776
19050
|
lineCountThreshold = 500
|
|
18777
19051
|
} = options;
|
|
18778
|
-
if (!await
|
|
19052
|
+
if (!await fs3.pathExists(feedbackFile)) {
|
|
18779
19053
|
return {
|
|
18780
19054
|
shouldArchive: false,
|
|
18781
19055
|
reasons: [],
|
|
18782
19056
|
stats: { openIssuesCount: 0, resolvedIssuesCount: 0, fileSizeBytes: 0, lineCount: 0 }
|
|
18783
19057
|
};
|
|
18784
19058
|
}
|
|
18785
|
-
const content = await
|
|
18786
|
-
const stats = await
|
|
19059
|
+
const content = await fs3.readFile(feedbackFile, "utf-8");
|
|
19060
|
+
const stats = await fs3.stat(feedbackFile);
|
|
18787
19061
|
const parsed = parseUserFeedback(content);
|
|
18788
19062
|
const lineCount = content.split("\n").length;
|
|
18789
19063
|
const reasons = [];
|
|
@@ -18857,16 +19131,16 @@ var EnhancedFeedbackFileManager = class {
|
|
|
18857
19131
|
this.feedbackFile = feedbackFile;
|
|
18858
19132
|
}
|
|
18859
19133
|
async ensureExists() {
|
|
18860
|
-
if (!await
|
|
19134
|
+
if (!await fs3.pathExists(this.feedbackFile)) {
|
|
18861
19135
|
await this.createInitialFile();
|
|
18862
19136
|
}
|
|
18863
19137
|
}
|
|
18864
19138
|
async addFeedback(issue, testCriteria) {
|
|
18865
19139
|
await this.ensureExists();
|
|
18866
19140
|
try {
|
|
18867
|
-
const content = await
|
|
19141
|
+
const content = await fs3.readFile(this.feedbackFile, "utf-8");
|
|
18868
19142
|
const updatedContent = this.addIssueToContent(content, issue, testCriteria);
|
|
18869
|
-
await
|
|
19143
|
+
await fs3.writeFile(this.feedbackFile, updatedContent, "utf-8");
|
|
18870
19144
|
} catch (error) {
|
|
18871
19145
|
throw new ValidationError(
|
|
18872
19146
|
`Failed to save feedback: ${error}`,
|
|
@@ -18879,12 +19153,12 @@ var EnhancedFeedbackFileManager = class {
|
|
|
18879
19153
|
*/
|
|
18880
19154
|
async repairMalformedFile() {
|
|
18881
19155
|
try {
|
|
18882
|
-
const content = await
|
|
19156
|
+
const content = await fs3.readFile(this.feedbackFile, "utf-8");
|
|
18883
19157
|
const hasOpenIssues = content.includes("<OPEN_ISSUES>");
|
|
18884
19158
|
const hasClosingTag = content.includes("</OPEN_ISSUES>");
|
|
18885
19159
|
if (!hasOpenIssues || !hasClosingTag) {
|
|
18886
19160
|
const backupPath = this.feedbackFile + ".backup." + Date.now();
|
|
18887
|
-
await
|
|
19161
|
+
await fs3.writeFile(backupPath, content, "utf-8");
|
|
18888
19162
|
const existingIssues = this.extractIssuesFromMalformedContent(content);
|
|
18889
19163
|
await this.createInitialFile(existingIssues);
|
|
18890
19164
|
}
|
|
@@ -18916,8 +19190,8 @@ List any features you'd like to see added or bugs you've encountered.
|
|
|
18916
19190
|
|
|
18917
19191
|
<!-- Resolved issues will be moved here -->
|
|
18918
19192
|
`;
|
|
18919
|
-
await
|
|
18920
|
-
await
|
|
19193
|
+
await fs3.ensureDir(path3.dirname(this.feedbackFile));
|
|
19194
|
+
await fs3.writeFile(this.feedbackFile, initialContent, "utf-8");
|
|
18921
19195
|
}
|
|
18922
19196
|
addIssueToContent(content, issue, testCriteria) {
|
|
18923
19197
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -19033,17 +19307,17 @@ async function handleCompactCommand(subArgs, options) {
|
|
|
19033
19307
|
if (subArgs.length > 0) {
|
|
19034
19308
|
for (const filePath of subArgs) {
|
|
19035
19309
|
const resolvedPath = path3.resolve(filePath);
|
|
19036
|
-
if (await
|
|
19310
|
+
if (await fs3.pathExists(resolvedPath)) {
|
|
19037
19311
|
filesToCompact.push(resolvedPath);
|
|
19038
19312
|
} else {
|
|
19039
19313
|
console.warn(chalk15.yellow(`\u26A0\uFE0F File not found: ${filePath}`));
|
|
19040
19314
|
}
|
|
19041
19315
|
}
|
|
19042
19316
|
} else {
|
|
19043
|
-
if (await
|
|
19317
|
+
if (await fs3.pathExists(defaultClaudeFile)) {
|
|
19044
19318
|
filesToCompact.push(defaultClaudeFile);
|
|
19045
19319
|
}
|
|
19046
|
-
if (await
|
|
19320
|
+
if (await fs3.pathExists(defaultAgentsFile)) {
|
|
19047
19321
|
filesToCompact.push(defaultAgentsFile);
|
|
19048
19322
|
}
|
|
19049
19323
|
}
|
|
@@ -19977,11 +20251,11 @@ var GitManager = class {
|
|
|
19977
20251
|
*/
|
|
19978
20252
|
async updateJunoTaskConfig(gitUrl) {
|
|
19979
20253
|
const configPath = path3.join(this.workingDirectory, ".juno_task", "init.md");
|
|
19980
|
-
if (!await
|
|
20254
|
+
if (!await fs3.pathExists(configPath)) {
|
|
19981
20255
|
return;
|
|
19982
20256
|
}
|
|
19983
20257
|
try {
|
|
19984
|
-
let content = await
|
|
20258
|
+
let content = await fs3.readFile(configPath, "utf-8");
|
|
19985
20259
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
19986
20260
|
if (frontmatterMatch) {
|
|
19987
20261
|
let frontmatter = frontmatterMatch[1];
|
|
@@ -20002,7 +20276,7 @@ GIT_URL: ${gitUrl}
|
|
|
20002
20276
|
`;
|
|
20003
20277
|
content = frontmatter + content;
|
|
20004
20278
|
}
|
|
20005
|
-
await
|
|
20279
|
+
await fs3.writeFile(configPath, content, "utf-8");
|
|
20006
20280
|
} catch (error) {
|
|
20007
20281
|
console.warn(`Warning: Failed to update juno-code configuration: ${error}`);
|
|
20008
20282
|
}
|
|
@@ -20934,7 +21208,7 @@ var LogViewer = ({
|
|
|
20934
21208
|
init_types();
|
|
20935
21209
|
async function exportLogs(logger2, filepath, options) {
|
|
20936
21210
|
try {
|
|
20937
|
-
const
|
|
21211
|
+
const fs22 = await import('fs-extra');
|
|
20938
21212
|
let entries = logger2.getRecentEntries(options.tail || 1e3);
|
|
20939
21213
|
if (options.level) {
|
|
20940
21214
|
const level = LogLevel[options.level.toUpperCase()];
|
|
@@ -20964,7 +21238,7 @@ async function exportLogs(logger2, filepath, options) {
|
|
|
20964
21238
|
},
|
|
20965
21239
|
entries
|
|
20966
21240
|
};
|
|
20967
|
-
await
|
|
21241
|
+
await fs22.writeFile(filepath, JSON.stringify(exportData, null, 2));
|
|
20968
21242
|
console.log(chalk15.green(`\u2705 Exported ${entries.length} log entries to: ${filepath}`));
|
|
20969
21243
|
} catch (error) {
|
|
20970
21244
|
console.error(chalk15.red(`\u274C Failed to export logs: ${error}`));
|
|
@@ -22508,7 +22782,7 @@ function createServicesCommand() {
|
|
|
22508
22782
|
const servicesCmd = new Command("services").description("Manage juno-code service scripts").addHelpText("after", `
|
|
22509
22783
|
Examples:
|
|
22510
22784
|
$ juno-code services install Install service scripts to ~/.juno_code/services/
|
|
22511
|
-
$ juno-code services install --force Reinstall/refresh service scripts (codex.py/claude.py)
|
|
22785
|
+
$ juno-code services install --force Reinstall/refresh service scripts (codex.py/claude.py/gemini.py)
|
|
22512
22786
|
$ juno-code services list List installed service scripts
|
|
22513
22787
|
$ juno-code services status Check installation status
|
|
22514
22788
|
$ juno-code services uninstall Remove all service scripts
|
|
@@ -22765,10 +23039,10 @@ autoload -U compinit && compinit`;
|
|
|
22765
23039
|
*/
|
|
22766
23040
|
async isSourceCommandPresent(configPath, sourceCommand) {
|
|
22767
23041
|
try {
|
|
22768
|
-
if (!await
|
|
23042
|
+
if (!await fs3.pathExists(configPath)) {
|
|
22769
23043
|
return false;
|
|
22770
23044
|
}
|
|
22771
|
-
const content = await
|
|
23045
|
+
const content = await fs3.readFile(configPath, "utf-8");
|
|
22772
23046
|
return content.includes("juno-code completion");
|
|
22773
23047
|
} catch {
|
|
22774
23048
|
return false;
|
|
@@ -22790,15 +23064,15 @@ autoload -U compinit && compinit`;
|
|
|
22790
23064
|
continue;
|
|
22791
23065
|
}
|
|
22792
23066
|
try {
|
|
22793
|
-
const completionExists = await
|
|
22794
|
-
const configExists = await
|
|
23067
|
+
const completionExists = await fs3.pathExists(shell.completionPath);
|
|
23068
|
+
const configExists = await fs3.pathExists(shell.configPath);
|
|
22795
23069
|
const isSourced = configExists && await this.isSourceCommandPresent(
|
|
22796
23070
|
shell.configPath,
|
|
22797
23071
|
this.getSourceCommand(shell.name, shell.completionPath)
|
|
22798
23072
|
);
|
|
22799
23073
|
let lastInstalled;
|
|
22800
23074
|
if (completionExists) {
|
|
22801
|
-
const stats = await
|
|
23075
|
+
const stats = await fs3.stat(shell.completionPath);
|
|
22802
23076
|
lastInstalled = stats.mtime;
|
|
22803
23077
|
}
|
|
22804
23078
|
statuses.push({
|
|
@@ -22824,7 +23098,7 @@ autoload -U compinit && compinit`;
|
|
|
22824
23098
|
async ensureCompletionDirectory(shell) {
|
|
22825
23099
|
const completionPath = this.getCompletionPath(shell);
|
|
22826
23100
|
const completionDir = path3.dirname(completionPath);
|
|
22827
|
-
await
|
|
23101
|
+
await fs3.ensureDir(completionDir);
|
|
22828
23102
|
}
|
|
22829
23103
|
/**
|
|
22830
23104
|
* Ensure directory exists for shell configuration
|
|
@@ -22832,7 +23106,7 @@ autoload -U compinit && compinit`;
|
|
|
22832
23106
|
async ensureConfigDirectory(shell) {
|
|
22833
23107
|
const configPath = this.getConfigPath(shell);
|
|
22834
23108
|
const configDir = path3.dirname(configPath);
|
|
22835
|
-
await
|
|
23109
|
+
await fs3.ensureDir(configDir);
|
|
22836
23110
|
}
|
|
22837
23111
|
/**
|
|
22838
23112
|
* Get shell version information
|
|
@@ -22856,15 +23130,15 @@ autoload -U compinit && compinit`;
|
|
|
22856
23130
|
try {
|
|
22857
23131
|
const configPath = this.getConfigPath(shell);
|
|
22858
23132
|
const configDir = path3.dirname(configPath);
|
|
22859
|
-
await
|
|
23133
|
+
await fs3.access(configDir, fs3.constants.W_OK);
|
|
22860
23134
|
} catch {
|
|
22861
23135
|
issues.push(`Cannot write to ${shell} configuration directory`);
|
|
22862
23136
|
}
|
|
22863
23137
|
try {
|
|
22864
23138
|
const completionPath = this.getCompletionPath(shell);
|
|
22865
23139
|
const completionDir = path3.dirname(completionPath);
|
|
22866
|
-
await
|
|
22867
|
-
await
|
|
23140
|
+
await fs3.ensureDir(completionDir);
|
|
23141
|
+
await fs3.access(completionDir, fs3.constants.W_OK);
|
|
22868
23142
|
} catch {
|
|
22869
23143
|
issues.push(`Cannot write to ${shell} completion directory`);
|
|
22870
23144
|
}
|
|
@@ -22935,10 +23209,10 @@ var ContextAwareCompletion = class {
|
|
|
22935
23209
|
async getSessionIds() {
|
|
22936
23210
|
try {
|
|
22937
23211
|
const sessionDir = path3.join(process.cwd(), ".juno_task", "sessions");
|
|
22938
|
-
if (!await
|
|
23212
|
+
if (!await fs3.pathExists(sessionDir)) {
|
|
22939
23213
|
return [];
|
|
22940
23214
|
}
|
|
22941
|
-
const sessions = await
|
|
23215
|
+
const sessions = await fs3.readdir(sessionDir);
|
|
22942
23216
|
return sessions.filter((name) => name.match(/^session_\d+$/)).map((name) => name.replace("session_", "")).sort((a, b) => parseInt(b) - parseInt(a));
|
|
22943
23217
|
} catch {
|
|
22944
23218
|
return [];
|
|
@@ -22952,8 +23226,8 @@ var ContextAwareCompletion = class {
|
|
|
22952
23226
|
const builtinTemplates = ["basic", "advanced", "research", "development"];
|
|
22953
23227
|
const customTemplatesDir = path3.join(process.cwd(), ".juno_task", "templates");
|
|
22954
23228
|
let customTemplates = [];
|
|
22955
|
-
if (await
|
|
22956
|
-
const files = await
|
|
23229
|
+
if (await fs3.pathExists(customTemplatesDir)) {
|
|
23230
|
+
const files = await fs3.readdir(customTemplatesDir);
|
|
22957
23231
|
customTemplates = files.filter((name) => name.endsWith(".md") || name.endsWith(".hbs")).map((name) => path3.basename(name, path3.extname(name)));
|
|
22958
23232
|
}
|
|
22959
23233
|
return [...builtinTemplates, ...customTemplates].sort();
|
|
@@ -23030,7 +23304,7 @@ var CompletionInstaller = class {
|
|
|
23030
23304
|
await this.shellDetector.ensureConfigDirectory(shell);
|
|
23031
23305
|
const script = this.generateEnhancedCompletion(shell, "juno-code");
|
|
23032
23306
|
const completionPath = this.shellDetector.getCompletionPath(shell);
|
|
23033
|
-
await
|
|
23307
|
+
await fs3.writeFile(completionPath, script, "utf-8");
|
|
23034
23308
|
const configPath = this.shellDetector.getConfigPath(shell);
|
|
23035
23309
|
const sourceCommand = this.shellDetector.getSourceCommand(shell, completionPath);
|
|
23036
23310
|
const warnings = [];
|
|
@@ -23038,7 +23312,7 @@ var CompletionInstaller = class {
|
|
|
23038
23312
|
const isPresent = await this.shellDetector.isSourceCommandPresent(configPath, sourceCommand);
|
|
23039
23313
|
if (!isPresent) {
|
|
23040
23314
|
try {
|
|
23041
|
-
await
|
|
23315
|
+
await fs3.appendFile(configPath, `
|
|
23042
23316
|
|
|
23043
23317
|
${sourceCommand}
|
|
23044
23318
|
`);
|
|
@@ -23069,8 +23343,8 @@ ${sourceCommand}
|
|
|
23069
23343
|
async uninstall(shell) {
|
|
23070
23344
|
try {
|
|
23071
23345
|
const completionPath = this.shellDetector.getCompletionPath(shell);
|
|
23072
|
-
if (await
|
|
23073
|
-
await
|
|
23346
|
+
if (await fs3.pathExists(completionPath)) {
|
|
23347
|
+
await fs3.remove(completionPath);
|
|
23074
23348
|
return true;
|
|
23075
23349
|
}
|
|
23076
23350
|
return false;
|
|
@@ -23084,7 +23358,7 @@ ${sourceCommand}
|
|
|
23084
23358
|
async isInstalled(shell) {
|
|
23085
23359
|
try {
|
|
23086
23360
|
const completionPath = this.shellDetector.getCompletionPath(shell);
|
|
23087
|
-
return await
|
|
23361
|
+
return await fs3.pathExists(completionPath);
|
|
23088
23362
|
} catch {
|
|
23089
23363
|
return false;
|
|
23090
23364
|
}
|
|
@@ -23885,11 +24159,11 @@ function setupMainCommand(program) {
|
|
|
23885
24159
|
);
|
|
23886
24160
|
const allOptions2 = { ...definedGlobalOptions, ...options };
|
|
23887
24161
|
if (!globalOptions.subagent && !options.prompt && !options.interactive && !options.interactivePrompt) {
|
|
23888
|
-
const
|
|
23889
|
-
const
|
|
24162
|
+
const fs22 = await import('fs-extra');
|
|
24163
|
+
const path24 = await import('path');
|
|
23890
24164
|
const cwd2 = process.cwd();
|
|
23891
|
-
const junoTaskDir =
|
|
23892
|
-
if (await
|
|
24165
|
+
const junoTaskDir = path24.join(cwd2, ".juno_task");
|
|
24166
|
+
if (await fs22.pathExists(junoTaskDir)) {
|
|
23893
24167
|
console.log(chalk15.blue.bold("\u{1F3AF} Juno Code - Auto-detected Initialized Project\n"));
|
|
23894
24168
|
try {
|
|
23895
24169
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -23906,12 +24180,12 @@ function setupMainCommand(program) {
|
|
|
23906
24180
|
allOptions2.subagent = config.defaultSubagent;
|
|
23907
24181
|
console.log(chalk15.gray(`\u{1F916} Using configured subagent: ${chalk15.cyan(config.defaultSubagent)}`));
|
|
23908
24182
|
}
|
|
23909
|
-
const promptFile =
|
|
23910
|
-
if (!allOptions2.prompt && await
|
|
24183
|
+
const promptFile = path24.join(junoTaskDir, "prompt.md");
|
|
24184
|
+
if (!allOptions2.prompt && await fs22.pathExists(promptFile)) {
|
|
23911
24185
|
allOptions2.prompt = promptFile;
|
|
23912
24186
|
console.log(chalk15.gray(`\u{1F4C4} Using default prompt: ${chalk15.cyan(".juno_task/prompt.md")}`));
|
|
23913
24187
|
}
|
|
23914
|
-
if (allOptions2.subagent && (allOptions2.prompt || await
|
|
24188
|
+
if (allOptions2.subagent && (allOptions2.prompt || await fs22.pathExists(promptFile))) {
|
|
23915
24189
|
console.log(chalk15.green("\u2713 Auto-detected project configuration\n"));
|
|
23916
24190
|
const { mainCommandHandler: mainCommandHandler3 } = await Promise.resolve().then(() => (init_main(), main_exports));
|
|
23917
24191
|
await mainCommandHandler3([], allOptions2, command);
|
|
@@ -24068,6 +24342,17 @@ async function main() {
|
|
|
24068
24342
|
console.error("[DEBUG] Service auto-update failed:", error instanceof Error ? error.message : String(error));
|
|
24069
24343
|
}
|
|
24070
24344
|
}
|
|
24345
|
+
try {
|
|
24346
|
+
const { ScriptInstaller: ScriptInstaller2 } = await Promise.resolve().then(() => (init_script_installer(), script_installer_exports));
|
|
24347
|
+
const installed = await ScriptInstaller2.autoInstallMissing(process.cwd(), true);
|
|
24348
|
+
if (installed && (process.argv.includes("--verbose") || process.argv.includes("-v") || process.env.JUNO_CODE_DEBUG === "1")) {
|
|
24349
|
+
console.error("[DEBUG] Project scripts auto-installed to .juno_task/scripts/");
|
|
24350
|
+
}
|
|
24351
|
+
} catch (error) {
|
|
24352
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
24353
|
+
console.error("[DEBUG] Script auto-install failed:", error instanceof Error ? error.message : String(error));
|
|
24354
|
+
}
|
|
24355
|
+
}
|
|
24071
24356
|
program.name("juno-code").description("TypeScript implementation of juno-code CLI tool for AI subagent orchestration").version(VERSION, "-V, --version", "Display version information").helpOption("-h, --help", "Display help information");
|
|
24072
24357
|
setupGlobalOptions(program);
|
|
24073
24358
|
const isVerbose = process.argv.includes("--verbose") || process.argv.includes("-v");
|
|
@@ -24075,10 +24360,10 @@ async function main() {
|
|
|
24075
24360
|
const isHelpOrVersion = process.argv.includes("--help") || process.argv.includes("-h") || process.argv.includes("--version") || process.argv.includes("-V");
|
|
24076
24361
|
const hasNoArguments = process.argv.length <= 2;
|
|
24077
24362
|
const isInitCommand = process.argv.includes("init");
|
|
24078
|
-
const
|
|
24079
|
-
const
|
|
24080
|
-
const junoTaskDir =
|
|
24081
|
-
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);
|
|
24082
24367
|
if (!isHelpOrVersion && !hasNoArguments && !isInitCommand && isInitialized) {
|
|
24083
24368
|
try {
|
|
24084
24369
|
const { validateStartupConfigs: validateStartupConfigs2 } = await Promise.resolve().then(() => (init_startup_validation(), startup_validation_exports));
|