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