juno-code 1.0.45 → 1.0.47
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 +1 -1
- package/dist/bin/cli.js +645 -43
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +645 -43
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -4
- package/dist/index.mjs.map +1 -1
- package/dist/templates/scripts/log_scanner.sh +790 -0
- package/dist/templates/services/claude.py +2 -1
- package/dist/templates/services/codex.py +5 -4
- package/dist/templates/skills/claude/.gitkeep +0 -0
- package/dist/templates/skills/claude/plan-kanban-tasks/SKILL.md +25 -0
- package/dist/templates/skills/claude/ralph-loop/SKILL.md +43 -0
- package/dist/templates/skills/claude/ralph-loop/references/first_check.md +20 -0
- package/dist/templates/skills/claude/ralph-loop/references/implement.md +99 -0
- package/dist/templates/skills/claude/ralph-loop/scripts/kanban.sh +293 -0
- package/dist/templates/skills/claude/understand-project/SKILL.md +39 -0
- package/dist/templates/skills/codex/.gitkeep +0 -0
- package/dist/templates/skills/codex/ralph-loop/SKILL.md +43 -0
- package/dist/templates/skills/codex/ralph-loop/references/first_check.md +20 -0
- package/dist/templates/skills/codex/ralph-loop/references/implement.md +99 -0
- package/dist/templates/skills/codex/ralph-loop/scripts/kanban.sh +293 -0
- package/package.json +3 -2
package/dist/bin/cli.js
CHANGED
|
@@ -178,8 +178,8 @@ var init_types = __esm({
|
|
|
178
178
|
};
|
|
179
179
|
FileSystemError = class extends CLIError {
|
|
180
180
|
code = "FILESYSTEM_ERROR";
|
|
181
|
-
constructor(message,
|
|
182
|
-
super(
|
|
181
|
+
constructor(message, path25) {
|
|
182
|
+
super(path25 ? `${message}: ${path25}` : message);
|
|
183
183
|
this.suggestions = [
|
|
184
184
|
"Check file/directory permissions",
|
|
185
185
|
"Verify path exists and is accessible",
|
|
@@ -1326,6 +1326,8 @@ var init_config = __esm({
|
|
|
1326
1326
|
defaultBackend: BackendTypeSchema.describe("Default backend to use for task execution"),
|
|
1327
1327
|
defaultMaxIterations: zod.z.number().int().min(1).max(1e3).describe("Default maximum number of iterations for task execution"),
|
|
1328
1328
|
defaultModel: zod.z.string().optional().describe("Default model to use for the subagent"),
|
|
1329
|
+
// Project metadata
|
|
1330
|
+
mainTask: zod.z.string().optional().describe("Main task objective for the project"),
|
|
1329
1331
|
// Logging settings
|
|
1330
1332
|
logLevel: LogLevelSchema.describe("Logging level for the application"),
|
|
1331
1333
|
logFile: zod.z.string().optional().describe("Path to log file (optional)"),
|
|
@@ -1854,8 +1856,8 @@ ${helpText}
|
|
|
1854
1856
|
}
|
|
1855
1857
|
}
|
|
1856
1858
|
if (options.cwd) {
|
|
1857
|
-
const
|
|
1858
|
-
if (!await
|
|
1859
|
+
const fs24 = await import('fs-extra');
|
|
1860
|
+
if (!await fs24.pathExists(options.cwd)) {
|
|
1859
1861
|
throw new ValidationError(
|
|
1860
1862
|
`Working directory does not exist: ${options.cwd}`,
|
|
1861
1863
|
["Verify the path exists", "Use absolute paths to avoid ambiguity"]
|
|
@@ -3682,31 +3684,103 @@ function parseResetTime(message) {
|
|
|
3682
3684
|
}
|
|
3683
3685
|
return { resetTime, timezone };
|
|
3684
3686
|
}
|
|
3687
|
+
function parseCodexResetTime(message) {
|
|
3688
|
+
const resetPattern = /try again at\s+(\w+)\s+(\d{1,2})(?:st|nd|rd|th)?,?\s*(\d{4})\s+(\d{1,2}):(\d{2})\s*(AM|PM)/i;
|
|
3689
|
+
const match = message.match(resetPattern);
|
|
3690
|
+
if (!match) {
|
|
3691
|
+
return null;
|
|
3692
|
+
}
|
|
3693
|
+
const monthStr = match[1];
|
|
3694
|
+
const day = parseInt(match[2], 10);
|
|
3695
|
+
const year = parseInt(match[3], 10);
|
|
3696
|
+
let hours = parseInt(match[4], 10);
|
|
3697
|
+
const minutes = parseInt(match[5], 10);
|
|
3698
|
+
const ampm = match[6].toUpperCase();
|
|
3699
|
+
const MONTH_MAP = {
|
|
3700
|
+
"jan": 0,
|
|
3701
|
+
"january": 0,
|
|
3702
|
+
"feb": 1,
|
|
3703
|
+
"february": 1,
|
|
3704
|
+
"mar": 2,
|
|
3705
|
+
"march": 2,
|
|
3706
|
+
"apr": 3,
|
|
3707
|
+
"april": 3,
|
|
3708
|
+
"may": 4,
|
|
3709
|
+
"jun": 5,
|
|
3710
|
+
"june": 5,
|
|
3711
|
+
"jul": 6,
|
|
3712
|
+
"july": 6,
|
|
3713
|
+
"aug": 7,
|
|
3714
|
+
"august": 7,
|
|
3715
|
+
"sep": 8,
|
|
3716
|
+
"september": 8,
|
|
3717
|
+
"oct": 9,
|
|
3718
|
+
"october": 9,
|
|
3719
|
+
"nov": 10,
|
|
3720
|
+
"november": 10,
|
|
3721
|
+
"dec": 11,
|
|
3722
|
+
"december": 11
|
|
3723
|
+
};
|
|
3724
|
+
const month = MONTH_MAP[monthStr.toLowerCase()];
|
|
3725
|
+
if (month === void 0) {
|
|
3726
|
+
return null;
|
|
3727
|
+
}
|
|
3728
|
+
if (ampm === "PM" && hours !== 12) {
|
|
3729
|
+
hours += 12;
|
|
3730
|
+
} else if (ampm === "AM" && hours === 12) {
|
|
3731
|
+
hours = 0;
|
|
3732
|
+
}
|
|
3733
|
+
const resetTime = new Date(year, month, day, hours, minutes, 0, 0);
|
|
3734
|
+
const now = /* @__PURE__ */ new Date();
|
|
3735
|
+
if (resetTime.getTime() <= now.getTime()) {
|
|
3736
|
+
resetTime.setTime(resetTime.getTime() + 24 * 60 * 60 * 1e3);
|
|
3737
|
+
}
|
|
3738
|
+
return { resetTime };
|
|
3739
|
+
}
|
|
3685
3740
|
function detectQuotaLimit(message) {
|
|
3686
3741
|
if (!message || typeof message !== "string") {
|
|
3687
3742
|
return { detected: false };
|
|
3688
3743
|
}
|
|
3689
|
-
const
|
|
3690
|
-
|
|
3744
|
+
const claudePattern = /you'?ve hit your limit/i;
|
|
3745
|
+
const codexPattern = /you'?ve hit your usage limit/i;
|
|
3746
|
+
const isClaudeQuota = claudePattern.test(message) && !codexPattern.test(message);
|
|
3747
|
+
const isCodexQuota = codexPattern.test(message);
|
|
3748
|
+
if (!isClaudeQuota && !isCodexQuota) {
|
|
3691
3749
|
return { detected: false };
|
|
3692
3750
|
}
|
|
3693
|
-
const
|
|
3694
|
-
|
|
3751
|
+
const source = isCodexQuota ? "codex" : "claude";
|
|
3752
|
+
const parsedClaude = parseResetTime(message);
|
|
3753
|
+
if (parsedClaude) {
|
|
3695
3754
|
const now = /* @__PURE__ */ new Date();
|
|
3696
|
-
const sleepDurationMs = Math.max(0,
|
|
3755
|
+
const sleepDurationMs = Math.max(0, parsedClaude.resetTime.getTime() - now.getTime());
|
|
3697
3756
|
return {
|
|
3698
3757
|
detected: true,
|
|
3699
|
-
resetTime:
|
|
3758
|
+
resetTime: parsedClaude.resetTime,
|
|
3700
3759
|
sleepDurationMs,
|
|
3701
|
-
timezone:
|
|
3702
|
-
originalMessage: message
|
|
3760
|
+
timezone: parsedClaude.timezone,
|
|
3761
|
+
originalMessage: message,
|
|
3762
|
+
source
|
|
3763
|
+
};
|
|
3764
|
+
}
|
|
3765
|
+
const parsedCodex = parseCodexResetTime(message);
|
|
3766
|
+
if (parsedCodex) {
|
|
3767
|
+
const now = /* @__PURE__ */ new Date();
|
|
3768
|
+
const sleepDurationMs = Math.max(0, parsedCodex.resetTime.getTime() - now.getTime());
|
|
3769
|
+
return {
|
|
3770
|
+
detected: true,
|
|
3771
|
+
resetTime: parsedCodex.resetTime,
|
|
3772
|
+
sleepDurationMs,
|
|
3773
|
+
timezone: "local",
|
|
3774
|
+
originalMessage: message,
|
|
3775
|
+
source
|
|
3703
3776
|
};
|
|
3704
3777
|
}
|
|
3705
3778
|
return {
|
|
3706
3779
|
detected: true,
|
|
3707
3780
|
sleepDurationMs: 5 * 60 * 1e3,
|
|
3708
3781
|
// 5 minutes default
|
|
3709
|
-
originalMessage: message
|
|
3782
|
+
originalMessage: message,
|
|
3783
|
+
source
|
|
3710
3784
|
};
|
|
3711
3785
|
}
|
|
3712
3786
|
function formatDuration(ms) {
|
|
@@ -4231,8 +4305,68 @@ var init_shell_backend = __esm({
|
|
|
4231
4305
|
metadata
|
|
4232
4306
|
};
|
|
4233
4307
|
}
|
|
4308
|
+
if (subagentType === "codex") {
|
|
4309
|
+
const codexQuotaMessage = this.extractCodexQuotaMessage(result.output, result.error);
|
|
4310
|
+
if (codexQuotaMessage) {
|
|
4311
|
+
const quotaLimitInfo = detectQuotaLimit(codexQuotaMessage);
|
|
4312
|
+
if (quotaLimitInfo.detected) {
|
|
4313
|
+
const metadata = {
|
|
4314
|
+
structuredOutput: true,
|
|
4315
|
+
contentType: "application/json",
|
|
4316
|
+
rawOutput: result.output,
|
|
4317
|
+
quotaLimitInfo
|
|
4318
|
+
};
|
|
4319
|
+
const structuredPayload = {
|
|
4320
|
+
type: "result",
|
|
4321
|
+
subtype: "error",
|
|
4322
|
+
is_error: true,
|
|
4323
|
+
result: codexQuotaMessage,
|
|
4324
|
+
error: codexQuotaMessage,
|
|
4325
|
+
exit_code: result.exitCode,
|
|
4326
|
+
duration_ms: result.duration,
|
|
4327
|
+
quota_limit: quotaLimitInfo
|
|
4328
|
+
};
|
|
4329
|
+
return {
|
|
4330
|
+
content: JSON.stringify(structuredPayload),
|
|
4331
|
+
metadata
|
|
4332
|
+
};
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4234
4336
|
return { content: result.output, metadata: result.metadata };
|
|
4235
4337
|
}
|
|
4338
|
+
/**
|
|
4339
|
+
* Extract quota limit message from Codex stream output
|
|
4340
|
+
* Codex outputs JSON events like:
|
|
4341
|
+
* {"type": "error", "message": "You've hit your usage limit..."}
|
|
4342
|
+
* {"type": "turn.failed", "error": {"message": "You've hit your usage limit..."}}
|
|
4343
|
+
*/
|
|
4344
|
+
extractCodexQuotaMessage(output, stderr) {
|
|
4345
|
+
const sources = [output, stderr].filter(Boolean);
|
|
4346
|
+
for (const source of sources) {
|
|
4347
|
+
const lines = source.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
4348
|
+
for (const line of lines) {
|
|
4349
|
+
try {
|
|
4350
|
+
const parsed = JSON.parse(line);
|
|
4351
|
+
if (parsed?.type === "error" && parsed?.message) {
|
|
4352
|
+
if (/you'?ve hit your usage limit/i.test(parsed.message)) {
|
|
4353
|
+
return parsed.message;
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
if (parsed?.type === "turn.failed" && parsed?.error?.message) {
|
|
4357
|
+
if (/you'?ve hit your usage limit/i.test(parsed.error.message)) {
|
|
4358
|
+
return parsed.error.message;
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
} catch {
|
|
4362
|
+
if (/you'?ve hit your usage limit/i.test(line)) {
|
|
4363
|
+
return line;
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
}
|
|
4367
|
+
}
|
|
4368
|
+
return null;
|
|
4369
|
+
}
|
|
4236
4370
|
/**
|
|
4237
4371
|
* Extract the last valid JSON object from a script's stdout to use as a structured payload fallback.
|
|
4238
4372
|
*/
|
|
@@ -5212,8 +5346,9 @@ var init_engine = __esm({
|
|
|
5212
5346
|
hour12: true,
|
|
5213
5347
|
timeZoneName: "short"
|
|
5214
5348
|
}) : "unknown";
|
|
5349
|
+
const sourceLabel = quotaInfo.source === "codex" ? "Codex" : "Claude";
|
|
5215
5350
|
engineLogger.info(`\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`);
|
|
5216
|
-
engineLogger.info(`\u2551
|
|
5351
|
+
engineLogger.info(`\u2551 ${sourceLabel} Quota Limit Reached${" ".repeat(44 - sourceLabel.length - " Quota Limit Reached".length)}\u2551`);
|
|
5217
5352
|
engineLogger.info(`\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563`);
|
|
5218
5353
|
engineLogger.info(`\u2551 Quota resets at: ${resetTimeStr2.padEnd(44)}\u2551`);
|
|
5219
5354
|
engineLogger.info(`\u2551 Behavior: raise (exit immediately) \u2551`);
|
|
@@ -5223,7 +5358,7 @@ var init_engine = __esm({
|
|
|
5223
5358
|
engineLogger.info(`\u2551 Or in config.json: { "onHourlyLimit": "wait" } \u2551`);
|
|
5224
5359
|
engineLogger.info(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`);
|
|
5225
5360
|
this.emit("quota-limit:raise", { context, quotaInfo });
|
|
5226
|
-
throw new Error(
|
|
5361
|
+
throw new Error(`${sourceLabel} quota limit reached. Quota resets at ${resetTimeStr2}. Use --on-hourly-limit wait to auto-retry.`);
|
|
5227
5362
|
}
|
|
5228
5363
|
const waitTimeMs = quotaInfo.sleepDurationMs;
|
|
5229
5364
|
const maxWaitTimeMs = 12 * 60 * 60 * 1e3;
|
|
@@ -5239,8 +5374,9 @@ var init_engine = __esm({
|
|
|
5239
5374
|
timeZoneName: "short"
|
|
5240
5375
|
}) : "unknown";
|
|
5241
5376
|
const durationStr = formatDuration(waitTimeMs);
|
|
5377
|
+
const waitSourceLabel = quotaInfo.source === "codex" ? "Codex" : "Claude";
|
|
5242
5378
|
engineLogger.info(`\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`);
|
|
5243
|
-
engineLogger.info(`\u2551
|
|
5379
|
+
engineLogger.info(`\u2551 ${waitSourceLabel} Quota Limit Reached${" ".repeat(44 - waitSourceLabel.length - " Quota Limit Reached".length)}\u2551`);
|
|
5244
5380
|
engineLogger.info(`\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563`);
|
|
5245
5381
|
engineLogger.info(`\u2551 Quota resets at: ${resetTimeStr.padEnd(44)}\u2551`);
|
|
5246
5382
|
engineLogger.info(`\u2551 Sleeping for: ${durationStr.padEnd(44)}\u2551`);
|
|
@@ -12699,12 +12835,12 @@ function safeConsoleOutput(message, options = {}) {
|
|
|
12699
12835
|
const capabilities = getTUICapabilities();
|
|
12700
12836
|
let output = message;
|
|
12701
12837
|
if (capabilities.hasColors && (color || bold)) {
|
|
12702
|
-
const
|
|
12703
|
-
if (color &&
|
|
12704
|
-
output =
|
|
12838
|
+
const chalk25 = __require("chalk");
|
|
12839
|
+
if (color && chalk25[color]) {
|
|
12840
|
+
output = chalk25[color](output);
|
|
12705
12841
|
}
|
|
12706
12842
|
if (bold) {
|
|
12707
|
-
output =
|
|
12843
|
+
output = chalk25.bold(output);
|
|
12708
12844
|
}
|
|
12709
12845
|
}
|
|
12710
12846
|
console[type](output);
|
|
@@ -13040,8 +13176,45 @@ var init_tui = __esm({
|
|
|
13040
13176
|
var main_exports = {};
|
|
13041
13177
|
__export(main_exports, {
|
|
13042
13178
|
createMainCommand: () => createMainCommand,
|
|
13179
|
+
getDefaultModelForSubagent: () => getDefaultModelForSubagent,
|
|
13180
|
+
isModelCompatibleWithSubagent: () => isModelCompatibleWithSubagent,
|
|
13043
13181
|
mainCommandHandler: () => mainCommandHandler
|
|
13044
13182
|
});
|
|
13183
|
+
function getDefaultModelForSubagent(subagent) {
|
|
13184
|
+
const modelDefaults = {
|
|
13185
|
+
claude: ":sonnet",
|
|
13186
|
+
codex: ":codex",
|
|
13187
|
+
// Expands to gpt-5.3-codex in codex.py
|
|
13188
|
+
gemini: ":pro",
|
|
13189
|
+
// Expands to gemini-2.5-pro in gemini.py
|
|
13190
|
+
cursor: "auto"
|
|
13191
|
+
};
|
|
13192
|
+
return modelDefaults[subagent] || modelDefaults.claude;
|
|
13193
|
+
}
|
|
13194
|
+
function isModelCompatibleWithSubagent(model, subagent) {
|
|
13195
|
+
if (!model.startsWith(":")) {
|
|
13196
|
+
return true;
|
|
13197
|
+
}
|
|
13198
|
+
const claudeShorthands = [":sonnet", ":haiku", ":opus"];
|
|
13199
|
+
const codexShorthands = [":codex", ":codex-mini", ":gpt-5", ":mini"];
|
|
13200
|
+
const geminiShorthands = [":pro", ":flash"];
|
|
13201
|
+
const isClaudeModel = claudeShorthands.includes(model) || model.startsWith(":claude");
|
|
13202
|
+
const isCodexModel = codexShorthands.includes(model) || model.startsWith(":gpt");
|
|
13203
|
+
const isGeminiModel = geminiShorthands.includes(model) || model.startsWith(":gemini");
|
|
13204
|
+
switch (subagent) {
|
|
13205
|
+
case "claude":
|
|
13206
|
+
return isClaudeModel || !isCodexModel && !isGeminiModel;
|
|
13207
|
+
case "codex":
|
|
13208
|
+
return isCodexModel || !isClaudeModel && !isGeminiModel;
|
|
13209
|
+
case "gemini":
|
|
13210
|
+
return isGeminiModel || !isClaudeModel && !isCodexModel;
|
|
13211
|
+
case "cursor":
|
|
13212
|
+
return true;
|
|
13213
|
+
// Cursor accepts any model
|
|
13214
|
+
default:
|
|
13215
|
+
return true;
|
|
13216
|
+
}
|
|
13217
|
+
}
|
|
13045
13218
|
async function mainCommandHandler(args, options, command) {
|
|
13046
13219
|
try {
|
|
13047
13220
|
const validSubagents = ["claude", "cursor", "codex", "gemini"];
|
|
@@ -13093,13 +13266,15 @@ async function mainCommandHandler(args, options, command) {
|
|
|
13093
13266
|
["Use -1 for unlimited iterations", "Use positive integers like 1, 5, or 10", "Example: -i 5"]
|
|
13094
13267
|
);
|
|
13095
13268
|
}
|
|
13269
|
+
const configModelIsValid = config.defaultModel && config.defaultSubagent === options.subagent && isModelCompatibleWithSubagent(config.defaultModel, options.subagent);
|
|
13270
|
+
const resolvedModel = options.model || (configModelIsValid ? config.defaultModel : void 0) || getDefaultModelForSubagent(options.subagent);
|
|
13096
13271
|
const executionRequest = createExecutionRequest({
|
|
13097
13272
|
instruction,
|
|
13098
13273
|
subagent: options.subagent,
|
|
13099
13274
|
backend: selectedBackend,
|
|
13100
13275
|
workingDirectory: config.workingDirectory,
|
|
13101
13276
|
maxIterations: options.maxIterations ?? config.defaultMaxIterations,
|
|
13102
|
-
model:
|
|
13277
|
+
model: resolvedModel,
|
|
13103
13278
|
agents: options.agents,
|
|
13104
13279
|
tools: options.tools,
|
|
13105
13280
|
allowedTools: options.allowedTools,
|
|
@@ -13621,6 +13796,289 @@ var init_main = __esm({
|
|
|
13621
13796
|
}
|
|
13622
13797
|
});
|
|
13623
13798
|
|
|
13799
|
+
// src/utils/skill-installer.ts
|
|
13800
|
+
var skill_installer_exports = {};
|
|
13801
|
+
__export(skill_installer_exports, {
|
|
13802
|
+
SkillInstaller: () => SkillInstaller
|
|
13803
|
+
});
|
|
13804
|
+
var SkillInstaller;
|
|
13805
|
+
var init_skill_installer = __esm({
|
|
13806
|
+
"src/utils/skill-installer.ts"() {
|
|
13807
|
+
init_version();
|
|
13808
|
+
SkillInstaller = class {
|
|
13809
|
+
/**
|
|
13810
|
+
* Skill groups define which template folders map to which project directories.
|
|
13811
|
+
* New agents can be added here without changing any other logic.
|
|
13812
|
+
*/
|
|
13813
|
+
static SKILL_GROUPS = [
|
|
13814
|
+
{ name: "codex", destDir: ".agents/skills" },
|
|
13815
|
+
{ name: "claude", destDir: ".claude/skills" }
|
|
13816
|
+
];
|
|
13817
|
+
/**
|
|
13818
|
+
* Get the templates skills directory from the package
|
|
13819
|
+
*/
|
|
13820
|
+
static getPackageSkillsDir() {
|
|
13821
|
+
const __dirname2 = path3__namespace.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.js', document.baseURI).href))));
|
|
13822
|
+
const candidates = [
|
|
13823
|
+
path3__namespace.join(__dirname2, "..", "..", "templates", "skills"),
|
|
13824
|
+
// dist (production)
|
|
13825
|
+
path3__namespace.join(__dirname2, "..", "templates", "skills")
|
|
13826
|
+
// src (development)
|
|
13827
|
+
];
|
|
13828
|
+
for (const skillsPath of candidates) {
|
|
13829
|
+
if (fs3__default.default.existsSync(skillsPath)) {
|
|
13830
|
+
return skillsPath;
|
|
13831
|
+
}
|
|
13832
|
+
}
|
|
13833
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13834
|
+
console.error("[DEBUG] SkillInstaller: Could not find templates/skills directory");
|
|
13835
|
+
console.error("[DEBUG] Tried:", candidates);
|
|
13836
|
+
}
|
|
13837
|
+
return null;
|
|
13838
|
+
}
|
|
13839
|
+
/**
|
|
13840
|
+
* Get list of skill files in a specific skill group template directory.
|
|
13841
|
+
* Returns paths relative to the group directory.
|
|
13842
|
+
*/
|
|
13843
|
+
static async getSkillFiles(groupDir) {
|
|
13844
|
+
if (!await fs3__default.default.pathExists(groupDir)) {
|
|
13845
|
+
return [];
|
|
13846
|
+
}
|
|
13847
|
+
const files = [];
|
|
13848
|
+
const walk = async (dir, prefix) => {
|
|
13849
|
+
const entries = await fs3__default.default.readdir(dir, { withFileTypes: true });
|
|
13850
|
+
for (const entry of entries) {
|
|
13851
|
+
if (entry.name.startsWith(".") || entry.name === "__pycache__") {
|
|
13852
|
+
continue;
|
|
13853
|
+
}
|
|
13854
|
+
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
13855
|
+
if (entry.isDirectory()) {
|
|
13856
|
+
await walk(path3__namespace.join(dir, entry.name), relPath);
|
|
13857
|
+
} else {
|
|
13858
|
+
files.push(relPath);
|
|
13859
|
+
}
|
|
13860
|
+
}
|
|
13861
|
+
};
|
|
13862
|
+
await walk(groupDir, "");
|
|
13863
|
+
return files;
|
|
13864
|
+
}
|
|
13865
|
+
/**
|
|
13866
|
+
* Install skills for a single skill group.
|
|
13867
|
+
* Only copies skill files, does NOT delete or modify any other files in the destination.
|
|
13868
|
+
*
|
|
13869
|
+
* @param projectDir - The project root directory
|
|
13870
|
+
* @param group - The skill group to install
|
|
13871
|
+
* @param silent - If true, suppresses console output
|
|
13872
|
+
* @param force - If true, overwrite even if content is identical
|
|
13873
|
+
* @returns number of files installed or updated
|
|
13874
|
+
*/
|
|
13875
|
+
static async installGroup(projectDir, group, silent = true, force = false) {
|
|
13876
|
+
const debug = process.env.JUNO_CODE_DEBUG === "1";
|
|
13877
|
+
const packageSkillsDir = this.getPackageSkillsDir();
|
|
13878
|
+
if (!packageSkillsDir) {
|
|
13879
|
+
if (debug) {
|
|
13880
|
+
console.error("[DEBUG] SkillInstaller: Package skills directory not found");
|
|
13881
|
+
}
|
|
13882
|
+
return 0;
|
|
13883
|
+
}
|
|
13884
|
+
const sourceGroupDir = path3__namespace.join(packageSkillsDir, group.name);
|
|
13885
|
+
const destGroupDir = path3__namespace.join(projectDir, group.destDir);
|
|
13886
|
+
const skillFiles = await this.getSkillFiles(sourceGroupDir);
|
|
13887
|
+
if (skillFiles.length === 0) {
|
|
13888
|
+
if (debug) {
|
|
13889
|
+
console.error(`[DEBUG] SkillInstaller: No skill files found for group '${group.name}'`);
|
|
13890
|
+
}
|
|
13891
|
+
return 0;
|
|
13892
|
+
}
|
|
13893
|
+
await fs3__default.default.ensureDir(destGroupDir);
|
|
13894
|
+
let installed = 0;
|
|
13895
|
+
for (const relFile of skillFiles) {
|
|
13896
|
+
const srcPath = path3__namespace.join(sourceGroupDir, relFile);
|
|
13897
|
+
const destPath = path3__namespace.join(destGroupDir, relFile);
|
|
13898
|
+
const destParent = path3__namespace.dirname(destPath);
|
|
13899
|
+
await fs3__default.default.ensureDir(destParent);
|
|
13900
|
+
let shouldCopy = force;
|
|
13901
|
+
if (!shouldCopy) {
|
|
13902
|
+
if (!await fs3__default.default.pathExists(destPath)) {
|
|
13903
|
+
shouldCopy = true;
|
|
13904
|
+
} else {
|
|
13905
|
+
const [srcContent, destContent] = await Promise.all([
|
|
13906
|
+
fs3__default.default.readFile(srcPath, "utf-8"),
|
|
13907
|
+
fs3__default.default.readFile(destPath, "utf-8")
|
|
13908
|
+
]);
|
|
13909
|
+
if (srcContent !== destContent) {
|
|
13910
|
+
shouldCopy = true;
|
|
13911
|
+
}
|
|
13912
|
+
}
|
|
13913
|
+
}
|
|
13914
|
+
if (shouldCopy) {
|
|
13915
|
+
await fs3__default.default.copy(srcPath, destPath, { overwrite: true });
|
|
13916
|
+
if (relFile.endsWith(".sh") || relFile.endsWith(".py")) {
|
|
13917
|
+
await fs3__default.default.chmod(destPath, 493);
|
|
13918
|
+
}
|
|
13919
|
+
installed++;
|
|
13920
|
+
if (debug) {
|
|
13921
|
+
console.error(`[DEBUG] SkillInstaller: Installed ${group.name}/${relFile} -> ${destPath}`);
|
|
13922
|
+
}
|
|
13923
|
+
}
|
|
13924
|
+
}
|
|
13925
|
+
if (installed > 0 && !silent) {
|
|
13926
|
+
console.log(`\u2713 Installed ${installed} skill file(s) for ${group.name} -> ${group.destDir}`);
|
|
13927
|
+
}
|
|
13928
|
+
return installed;
|
|
13929
|
+
}
|
|
13930
|
+
/**
|
|
13931
|
+
* Install skills for all skill groups.
|
|
13932
|
+
* This copies skill files to the appropriate project directories while
|
|
13933
|
+
* preserving any existing files the user may have added.
|
|
13934
|
+
*
|
|
13935
|
+
* @param projectDir - The project root directory
|
|
13936
|
+
* @param silent - If true, suppresses console output
|
|
13937
|
+
* @param force - If true, overwrite even if content matches
|
|
13938
|
+
* @returns true if any skill files were installed or updated
|
|
13939
|
+
*/
|
|
13940
|
+
static async install(projectDir, silent = false, force = false) {
|
|
13941
|
+
const debug = process.env.JUNO_CODE_DEBUG === "1";
|
|
13942
|
+
let totalInstalled = 0;
|
|
13943
|
+
for (const group of this.SKILL_GROUPS) {
|
|
13944
|
+
try {
|
|
13945
|
+
const count = await this.installGroup(projectDir, group, silent, force);
|
|
13946
|
+
totalInstalled += count;
|
|
13947
|
+
} catch (error) {
|
|
13948
|
+
if (debug) {
|
|
13949
|
+
console.error(`[DEBUG] SkillInstaller: Error installing group '${group.name}':`, error);
|
|
13950
|
+
}
|
|
13951
|
+
if (!silent) {
|
|
13952
|
+
console.error(`\u26A0\uFE0F Failed to install skills for ${group.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13953
|
+
}
|
|
13954
|
+
}
|
|
13955
|
+
}
|
|
13956
|
+
if (totalInstalled > 0 && !silent) {
|
|
13957
|
+
console.log(`\u2713 Total: ${totalInstalled} skill file(s) installed/updated`);
|
|
13958
|
+
}
|
|
13959
|
+
return totalInstalled > 0;
|
|
13960
|
+
}
|
|
13961
|
+
/**
|
|
13962
|
+
* Auto-update skills on CLI startup.
|
|
13963
|
+
* Only installs/updates if the project is initialized (.juno_task exists).
|
|
13964
|
+
* Silently does nothing if no skill files are bundled or project is not initialized.
|
|
13965
|
+
*
|
|
13966
|
+
* @param projectDir - The project root directory
|
|
13967
|
+
* @param force - If true, force reinstall all skills
|
|
13968
|
+
* @returns true if any updates occurred
|
|
13969
|
+
*/
|
|
13970
|
+
static async autoUpdate(projectDir, force = false) {
|
|
13971
|
+
try {
|
|
13972
|
+
const debug = process.env.JUNO_CODE_DEBUG === "1";
|
|
13973
|
+
const junoTaskDir = path3__namespace.join(projectDir, ".juno_task");
|
|
13974
|
+
if (!await fs3__default.default.pathExists(junoTaskDir)) {
|
|
13975
|
+
return false;
|
|
13976
|
+
}
|
|
13977
|
+
if (debug) {
|
|
13978
|
+
console.error(`[DEBUG] SkillInstaller: Auto-updating skills (force=${force})`);
|
|
13979
|
+
}
|
|
13980
|
+
const updated = await this.install(projectDir, true, force);
|
|
13981
|
+
if (updated && debug) {
|
|
13982
|
+
console.error("[DEBUG] SkillInstaller: Skills auto-updated successfully");
|
|
13983
|
+
}
|
|
13984
|
+
return updated;
|
|
13985
|
+
} catch (error) {
|
|
13986
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13987
|
+
console.error("[DEBUG] SkillInstaller: autoUpdate error:", error instanceof Error ? error.message : String(error));
|
|
13988
|
+
}
|
|
13989
|
+
return false;
|
|
13990
|
+
}
|
|
13991
|
+
}
|
|
13992
|
+
/**
|
|
13993
|
+
* Check if any skills need to be installed or updated.
|
|
13994
|
+
*
|
|
13995
|
+
* @param projectDir - The project root directory
|
|
13996
|
+
* @returns true if any skills are missing or outdated
|
|
13997
|
+
*/
|
|
13998
|
+
static async needsUpdate(projectDir) {
|
|
13999
|
+
try {
|
|
14000
|
+
const junoTaskDir = path3__namespace.join(projectDir, ".juno_task");
|
|
14001
|
+
if (!await fs3__default.default.pathExists(junoTaskDir)) {
|
|
14002
|
+
return false;
|
|
14003
|
+
}
|
|
14004
|
+
const packageSkillsDir = this.getPackageSkillsDir();
|
|
14005
|
+
if (!packageSkillsDir) {
|
|
14006
|
+
return false;
|
|
14007
|
+
}
|
|
14008
|
+
for (const group of this.SKILL_GROUPS) {
|
|
14009
|
+
const sourceGroupDir = path3__namespace.join(packageSkillsDir, group.name);
|
|
14010
|
+
const destGroupDir = path3__namespace.join(projectDir, group.destDir);
|
|
14011
|
+
const skillFiles = await this.getSkillFiles(sourceGroupDir);
|
|
14012
|
+
for (const relFile of skillFiles) {
|
|
14013
|
+
const srcPath = path3__namespace.join(sourceGroupDir, relFile);
|
|
14014
|
+
const destPath = path3__namespace.join(destGroupDir, relFile);
|
|
14015
|
+
if (!await fs3__default.default.pathExists(destPath)) {
|
|
14016
|
+
return true;
|
|
14017
|
+
}
|
|
14018
|
+
const [srcContent, destContent] = await Promise.all([
|
|
14019
|
+
fs3__default.default.readFile(srcPath, "utf-8"),
|
|
14020
|
+
fs3__default.default.readFile(destPath, "utf-8")
|
|
14021
|
+
]);
|
|
14022
|
+
if (srcContent !== destContent) {
|
|
14023
|
+
return true;
|
|
14024
|
+
}
|
|
14025
|
+
}
|
|
14026
|
+
}
|
|
14027
|
+
return false;
|
|
14028
|
+
} catch {
|
|
14029
|
+
return false;
|
|
14030
|
+
}
|
|
14031
|
+
}
|
|
14032
|
+
/**
|
|
14033
|
+
* List all skill groups and their installation status.
|
|
14034
|
+
*
|
|
14035
|
+
* @param projectDir - The project root directory
|
|
14036
|
+
* @returns Array of skill group status objects
|
|
14037
|
+
*/
|
|
14038
|
+
static async listSkillGroups(projectDir) {
|
|
14039
|
+
const packageSkillsDir = this.getPackageSkillsDir();
|
|
14040
|
+
const results = [];
|
|
14041
|
+
for (const group of this.SKILL_GROUPS) {
|
|
14042
|
+
const sourceGroupDir = packageSkillsDir ? path3__namespace.join(packageSkillsDir, group.name) : "";
|
|
14043
|
+
const destGroupDir = path3__namespace.join(projectDir, group.destDir);
|
|
14044
|
+
const skillFiles = packageSkillsDir ? await this.getSkillFiles(sourceGroupDir) : [];
|
|
14045
|
+
const files = [];
|
|
14046
|
+
for (const relFile of skillFiles) {
|
|
14047
|
+
const srcPath = path3__namespace.join(sourceGroupDir, relFile);
|
|
14048
|
+
const destPath = path3__namespace.join(destGroupDir, relFile);
|
|
14049
|
+
const installed = await fs3__default.default.pathExists(destPath);
|
|
14050
|
+
let upToDate = false;
|
|
14051
|
+
if (installed) {
|
|
14052
|
+
try {
|
|
14053
|
+
const [srcContent, destContent] = await Promise.all([
|
|
14054
|
+
fs3__default.default.readFile(srcPath, "utf-8"),
|
|
14055
|
+
fs3__default.default.readFile(destPath, "utf-8")
|
|
14056
|
+
]);
|
|
14057
|
+
upToDate = srcContent === destContent;
|
|
14058
|
+
} catch {
|
|
14059
|
+
upToDate = false;
|
|
14060
|
+
}
|
|
14061
|
+
}
|
|
14062
|
+
files.push({ name: relFile, installed, upToDate });
|
|
14063
|
+
}
|
|
14064
|
+
results.push({
|
|
14065
|
+
name: group.name,
|
|
14066
|
+
destDir: group.destDir,
|
|
14067
|
+
files
|
|
14068
|
+
});
|
|
14069
|
+
}
|
|
14070
|
+
return results;
|
|
14071
|
+
}
|
|
14072
|
+
/**
|
|
14073
|
+
* Get the list of skill group configurations.
|
|
14074
|
+
*/
|
|
14075
|
+
static getSkillGroups() {
|
|
14076
|
+
return [...this.SKILL_GROUPS];
|
|
14077
|
+
}
|
|
14078
|
+
};
|
|
14079
|
+
}
|
|
14080
|
+
});
|
|
14081
|
+
|
|
13624
14082
|
// src/utils/script-installer.ts
|
|
13625
14083
|
var script_installer_exports = {};
|
|
13626
14084
|
__export(script_installer_exports, {
|
|
@@ -13664,8 +14122,11 @@ var init_script_installer = __esm({
|
|
|
13664
14122
|
"github.py",
|
|
13665
14123
|
// Unified GitHub integration (fetch, respond, sync)
|
|
13666
14124
|
// Claude Code hooks (stored in hooks/ subdirectory)
|
|
13667
|
-
"hooks/session_counter.sh"
|
|
14125
|
+
"hooks/session_counter.sh",
|
|
13668
14126
|
// Session message counter hook for warning about long sessions
|
|
14127
|
+
// Log scanning utility
|
|
14128
|
+
"log_scanner.sh"
|
|
14129
|
+
// Scans log files for errors/exceptions and creates kanban bug reports
|
|
13669
14130
|
];
|
|
13670
14131
|
/**
|
|
13671
14132
|
* Get the templates scripts directory from the package
|
|
@@ -16719,6 +17180,8 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16719
17180
|
defaultSubagent: this.context.subagent,
|
|
16720
17181
|
defaultMaxIterations: 50,
|
|
16721
17182
|
defaultModel: this.getDefaultModelForSubagent(this.context.subagent || "claude"),
|
|
17183
|
+
// Project metadata
|
|
17184
|
+
mainTask: this.context.task || "Project initialization",
|
|
16722
17185
|
// Logging settings
|
|
16723
17186
|
logLevel: "info",
|
|
16724
17187
|
verbose: false,
|
|
@@ -16798,8 +17261,10 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16798
17261
|
getDefaultModelForSubagent(subagent) {
|
|
16799
17262
|
const modelDefaults = {
|
|
16800
17263
|
claude: ":sonnet",
|
|
16801
|
-
codex: "
|
|
16802
|
-
|
|
17264
|
+
codex: ":codex",
|
|
17265
|
+
// Expands to gpt-5.3-codex in codex.py
|
|
17266
|
+
gemini: ":pro",
|
|
17267
|
+
// Expands to gemini-2.5-pro in gemini.py
|
|
16803
17268
|
cursor: "auto"
|
|
16804
17269
|
};
|
|
16805
17270
|
return modelDefaults[subagent] || modelDefaults.claude;
|
|
@@ -19149,8 +19614,8 @@ async function compactConfigFile(filePath, options = {}) {
|
|
|
19149
19614
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
19150
19615
|
const ext = path3__namespace.extname(filePath);
|
|
19151
19616
|
const basename11 = path3__namespace.basename(filePath, ext);
|
|
19152
|
-
const
|
|
19153
|
-
backupPath = path3__namespace.join(
|
|
19617
|
+
const dirname14 = path3__namespace.dirname(filePath);
|
|
19618
|
+
backupPath = path3__namespace.join(dirname14, `${basename11}.backup.${timestamp}${ext}`);
|
|
19154
19619
|
await fs3__default.default.writeFile(backupPath, originalContent, "utf-8");
|
|
19155
19620
|
}
|
|
19156
19621
|
const compactionAnalysis = analyzeMarkdownStructure(originalContent);
|
|
@@ -21616,7 +22081,7 @@ var LogViewer = ({
|
|
|
21616
22081
|
init_types();
|
|
21617
22082
|
async function exportLogs(logger2, filepath, options) {
|
|
21618
22083
|
try {
|
|
21619
|
-
const
|
|
22084
|
+
const fs24 = await import('fs-extra');
|
|
21620
22085
|
let entries = logger2.getRecentEntries(options.tail || 1e3);
|
|
21621
22086
|
if (options.level) {
|
|
21622
22087
|
const level = LogLevel[options.level.toUpperCase()];
|
|
@@ -21646,7 +22111,7 @@ async function exportLogs(logger2, filepath, options) {
|
|
|
21646
22111
|
},
|
|
21647
22112
|
entries
|
|
21648
22113
|
};
|
|
21649
|
-
await
|
|
22114
|
+
await fs24.writeFile(filepath, JSON.stringify(exportData, null, 2));
|
|
21650
22115
|
console.log(chalk15__default.default.green(`\u2705 Exported ${entries.length} log entries to: ${filepath}`));
|
|
21651
22116
|
} catch (error) {
|
|
21652
22117
|
console.error(chalk15__default.default.red(`\u274C Failed to export logs: ${error}`));
|
|
@@ -23536,6 +24001,125 @@ Location: ${ServiceInstaller.getServicesDir()}`));
|
|
|
23536
24001
|
return servicesCmd;
|
|
23537
24002
|
}
|
|
23538
24003
|
|
|
24004
|
+
// src/cli/commands/skills.ts
|
|
24005
|
+
init_version();
|
|
24006
|
+
init_skill_installer();
|
|
24007
|
+
function createSkillsCommand() {
|
|
24008
|
+
const skillsCmd = new commander.Command("skills").description("Manage agent skill files").addHelpText("after", `
|
|
24009
|
+
Examples:
|
|
24010
|
+
$ juno-code skills install Install skill files to project directories
|
|
24011
|
+
$ juno-code skills install --force Force reinstall all skill files
|
|
24012
|
+
$ juno-code skills list List skill groups and their files
|
|
24013
|
+
$ juno-code skills status Check installation status
|
|
24014
|
+
|
|
24015
|
+
Skill files are copied from the juno-code package into the project:
|
|
24016
|
+
- Codex skills -> .agents/skills/
|
|
24017
|
+
- Claude skills -> .claude/skills/
|
|
24018
|
+
|
|
24019
|
+
Skills are installed for ALL agents regardless of which subagent is selected.
|
|
24020
|
+
Existing files in the destination directories are preserved.
|
|
24021
|
+
`);
|
|
24022
|
+
skillsCmd.command("install").description("Install skill files to project directories").option("-f, --force", "Force reinstall even if files are up-to-date").action(async (options) => {
|
|
24023
|
+
try {
|
|
24024
|
+
const projectDir = process.cwd();
|
|
24025
|
+
if (!options.force) {
|
|
24026
|
+
const needsUpdate = await SkillInstaller.needsUpdate(projectDir);
|
|
24027
|
+
if (!needsUpdate) {
|
|
24028
|
+
console.log(chalk15__default.default.yellow("\u26A0 All skill files are up-to-date"));
|
|
24029
|
+
console.log(chalk15__default.default.dim(" Use --force to reinstall"));
|
|
24030
|
+
return;
|
|
24031
|
+
}
|
|
24032
|
+
}
|
|
24033
|
+
console.log(chalk15__default.default.blue("Installing skill files..."));
|
|
24034
|
+
const installed = await SkillInstaller.install(projectDir, false, options.force);
|
|
24035
|
+
if (installed) {
|
|
24036
|
+
console.log(chalk15__default.default.green("\n\u2713 Skill files installed successfully"));
|
|
24037
|
+
} else {
|
|
24038
|
+
console.log(chalk15__default.default.yellow("\u26A0 No skill files to install (templates may be empty)"));
|
|
24039
|
+
}
|
|
24040
|
+
} catch (error) {
|
|
24041
|
+
console.error(chalk15__default.default.red("\u2717 Installation failed:"));
|
|
24042
|
+
console.error(chalk15__default.default.red(error instanceof Error ? error.message : String(error)));
|
|
24043
|
+
process.exit(1);
|
|
24044
|
+
}
|
|
24045
|
+
});
|
|
24046
|
+
skillsCmd.command("list").alias("ls").description("List skill groups and their files").action(async () => {
|
|
24047
|
+
try {
|
|
24048
|
+
const projectDir = process.cwd();
|
|
24049
|
+
const groups = await SkillInstaller.listSkillGroups(projectDir);
|
|
24050
|
+
if (groups.length === 0) {
|
|
24051
|
+
console.log(chalk15__default.default.yellow("\u26A0 No skill groups configured"));
|
|
24052
|
+
return;
|
|
24053
|
+
}
|
|
24054
|
+
let hasAnyFiles = false;
|
|
24055
|
+
for (const group of groups) {
|
|
24056
|
+
console.log(chalk15__default.default.blue.bold(`
|
|
24057
|
+
${group.name} skills -> ${group.destDir}/`));
|
|
24058
|
+
if (group.files.length === 0) {
|
|
24059
|
+
console.log(chalk15__default.default.dim(" (no skill files bundled yet)"));
|
|
24060
|
+
continue;
|
|
24061
|
+
}
|
|
24062
|
+
hasAnyFiles = true;
|
|
24063
|
+
for (const file of group.files) {
|
|
24064
|
+
const statusIcon = !file.installed ? chalk15__default.default.red("\u2717") : file.upToDate ? chalk15__default.default.green("\u2713") : chalk15__default.default.yellow("\u21BB");
|
|
24065
|
+
const statusLabel = !file.installed ? chalk15__default.default.dim("not installed") : file.upToDate ? chalk15__default.default.dim("up-to-date") : chalk15__default.default.yellow("outdated");
|
|
24066
|
+
console.log(` ${statusIcon} ${file.name} ${statusLabel}`);
|
|
24067
|
+
}
|
|
24068
|
+
}
|
|
24069
|
+
if (!hasAnyFiles) {
|
|
24070
|
+
console.log(chalk15__default.default.dim("\nNo skill files bundled yet. Add files to src/templates/skills/<agent>/ to bundle skills."));
|
|
24071
|
+
}
|
|
24072
|
+
} catch (error) {
|
|
24073
|
+
console.error(chalk15__default.default.red("\u2717 Failed to list skills:"));
|
|
24074
|
+
console.error(chalk15__default.default.red(error instanceof Error ? error.message : String(error)));
|
|
24075
|
+
process.exit(1);
|
|
24076
|
+
}
|
|
24077
|
+
});
|
|
24078
|
+
skillsCmd.command("status").description("Check skill installation status").action(async () => {
|
|
24079
|
+
try {
|
|
24080
|
+
const projectDir = process.cwd();
|
|
24081
|
+
const groups = await SkillInstaller.listSkillGroups(projectDir);
|
|
24082
|
+
const skillGroups = SkillInstaller.getSkillGroups();
|
|
24083
|
+
console.log(chalk15__default.default.blue("Skills Status:\n"));
|
|
24084
|
+
console.log(chalk15__default.default.dim(" Skill groups:"));
|
|
24085
|
+
for (const sg of skillGroups) {
|
|
24086
|
+
console.log(chalk15__default.default.dim(` ${sg.name} -> ${sg.destDir}/`));
|
|
24087
|
+
}
|
|
24088
|
+
const needsUpdate = await SkillInstaller.needsUpdate(projectDir);
|
|
24089
|
+
console.log(`
|
|
24090
|
+
${needsUpdate ? chalk15__default.default.yellow("\u26A0 Updates available") : chalk15__default.default.green("\u2713 All skills up-to-date")}`);
|
|
24091
|
+
let totalFiles = 0;
|
|
24092
|
+
let installedFiles = 0;
|
|
24093
|
+
let outdatedFiles = 0;
|
|
24094
|
+
for (const group of groups) {
|
|
24095
|
+
for (const file of group.files) {
|
|
24096
|
+
totalFiles++;
|
|
24097
|
+
if (file.installed) {
|
|
24098
|
+
installedFiles++;
|
|
24099
|
+
if (!file.upToDate) {
|
|
24100
|
+
outdatedFiles++;
|
|
24101
|
+
}
|
|
24102
|
+
}
|
|
24103
|
+
}
|
|
24104
|
+
}
|
|
24105
|
+
if (totalFiles > 0) {
|
|
24106
|
+
console.log(chalk15__default.default.dim(`
|
|
24107
|
+
Files: ${installedFiles}/${totalFiles} installed, ${outdatedFiles} outdated`));
|
|
24108
|
+
} else {
|
|
24109
|
+
console.log(chalk15__default.default.dim("\n No skill files bundled yet"));
|
|
24110
|
+
}
|
|
24111
|
+
if (needsUpdate) {
|
|
24112
|
+
console.log(chalk15__default.default.dim("\n Run: juno-code skills install"));
|
|
24113
|
+
}
|
|
24114
|
+
} catch (error) {
|
|
24115
|
+
console.error(chalk15__default.default.red("\u2717 Failed to check status:"));
|
|
24116
|
+
console.error(chalk15__default.default.red(error instanceof Error ? error.message : String(error)));
|
|
24117
|
+
process.exit(1);
|
|
24118
|
+
}
|
|
24119
|
+
});
|
|
24120
|
+
return skillsCmd;
|
|
24121
|
+
}
|
|
24122
|
+
|
|
23539
24123
|
// src/cli/commands/completion.ts
|
|
23540
24124
|
init_version();
|
|
23541
24125
|
|
|
@@ -24801,10 +25385,10 @@ function setupMainCommand(program) {
|
|
|
24801
25385
|
const allOptions2 = { ...definedGlobalOptions, ...options };
|
|
24802
25386
|
if (allOptions2.tilCompletion || allOptions2.untilCompletion || allOptions2.runUntilCompletion || allOptions2.tillComplete) {
|
|
24803
25387
|
const { spawn: spawn4 } = await import('child_process');
|
|
24804
|
-
const
|
|
24805
|
-
const
|
|
24806
|
-
const scriptPath =
|
|
24807
|
-
if (!await
|
|
25388
|
+
const path25 = await import('path');
|
|
25389
|
+
const fs24 = await import('fs-extra');
|
|
25390
|
+
const scriptPath = path25.join(process.cwd(), ".juno_task", "scripts", "run_until_completion.sh");
|
|
25391
|
+
if (!await fs24.pathExists(scriptPath)) {
|
|
24808
25392
|
console.error(chalk15__default.default.red.bold("\n\u274C Error: run_until_completion.sh not found"));
|
|
24809
25393
|
console.error(chalk15__default.default.red(` Expected location: ${scriptPath}`));
|
|
24810
25394
|
console.error(chalk15__default.default.yellow('\n\u{1F4A1} Suggestion: Run "juno-code init" to initialize the project'));
|
|
@@ -24855,11 +25439,11 @@ function setupMainCommand(program) {
|
|
|
24855
25439
|
return;
|
|
24856
25440
|
}
|
|
24857
25441
|
if (!globalOptions.subagent && !options.prompt && !options.interactive && !options.interactivePrompt) {
|
|
24858
|
-
const
|
|
24859
|
-
const
|
|
25442
|
+
const fs24 = await import('fs-extra');
|
|
25443
|
+
const path25 = await import('path');
|
|
24860
25444
|
const cwd2 = process.cwd();
|
|
24861
|
-
const junoTaskDir =
|
|
24862
|
-
if (await
|
|
25445
|
+
const junoTaskDir = path25.join(cwd2, ".juno_task");
|
|
25446
|
+
if (await fs24.pathExists(junoTaskDir)) {
|
|
24863
25447
|
console.log(chalk15__default.default.blue.bold("\u{1F3AF} Juno Code - Auto-detected Initialized Project\n"));
|
|
24864
25448
|
try {
|
|
24865
25449
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -24876,12 +25460,12 @@ function setupMainCommand(program) {
|
|
|
24876
25460
|
allOptions2.subagent = config.defaultSubagent;
|
|
24877
25461
|
console.log(chalk15__default.default.gray(`\u{1F916} Using configured subagent: ${chalk15__default.default.cyan(config.defaultSubagent)}`));
|
|
24878
25462
|
}
|
|
24879
|
-
const promptFile =
|
|
24880
|
-
if (!allOptions2.prompt && await
|
|
25463
|
+
const promptFile = path25.join(junoTaskDir, "prompt.md");
|
|
25464
|
+
if (!allOptions2.prompt && await fs24.pathExists(promptFile)) {
|
|
24881
25465
|
allOptions2.prompt = promptFile;
|
|
24882
25466
|
console.log(chalk15__default.default.gray(`\u{1F4C4} Using default prompt: ${chalk15__default.default.cyan(".juno_task/prompt.md")}`));
|
|
24883
25467
|
}
|
|
24884
|
-
if (allOptions2.subagent && (allOptions2.prompt || await
|
|
25468
|
+
if (allOptions2.subagent && (allOptions2.prompt || await fs24.pathExists(promptFile))) {
|
|
24885
25469
|
console.log(chalk15__default.default.green("\u2713 Auto-detected project configuration\n"));
|
|
24886
25470
|
const { mainCommandHandler: mainCommandHandler3 } = await Promise.resolve().then(() => (init_main(), main_exports));
|
|
24887
25471
|
await mainCommandHandler3([], allOptions2, command);
|
|
@@ -25063,6 +25647,23 @@ async function main() {
|
|
|
25063
25647
|
console.error("[DEBUG] Script auto-update failed:", error instanceof Error ? error.message : String(error));
|
|
25064
25648
|
}
|
|
25065
25649
|
}
|
|
25650
|
+
try {
|
|
25651
|
+
const { SkillInstaller: SkillInstaller2 } = await Promise.resolve().then(() => (init_skill_installer(), skill_installer_exports));
|
|
25652
|
+
if (isForceUpdate) {
|
|
25653
|
+
console.log(chalk15__default.default.blue("\u{1F504} Force updating agent skill files..."));
|
|
25654
|
+
await SkillInstaller2.autoUpdate(process.cwd(), true);
|
|
25655
|
+
console.log(chalk15__default.default.green("\u2713 Agent skill files updated"));
|
|
25656
|
+
} else {
|
|
25657
|
+
const updated = await SkillInstaller2.autoUpdate(process.cwd());
|
|
25658
|
+
if (updated && (process.argv.includes("--verbose") || process.argv.includes("-v") || process.env.JUNO_CODE_DEBUG === "1")) {
|
|
25659
|
+
console.error("[DEBUG] Agent skill files auto-updated");
|
|
25660
|
+
}
|
|
25661
|
+
}
|
|
25662
|
+
} catch (error) {
|
|
25663
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
25664
|
+
console.error("[DEBUG] Skill auto-update failed:", error instanceof Error ? error.message : String(error));
|
|
25665
|
+
}
|
|
25666
|
+
}
|
|
25066
25667
|
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");
|
|
25067
25668
|
setupGlobalOptions(program);
|
|
25068
25669
|
const isVerbose = process.argv.includes("--verbose") || process.argv.includes("-v");
|
|
@@ -25070,10 +25671,10 @@ async function main() {
|
|
|
25070
25671
|
const isHelpOrVersion = process.argv.includes("--help") || process.argv.includes("-h") || process.argv.includes("--version") || process.argv.includes("-V");
|
|
25071
25672
|
const hasNoArguments = process.argv.length <= 2;
|
|
25072
25673
|
const isInitCommand = process.argv.includes("init");
|
|
25073
|
-
const
|
|
25074
|
-
const
|
|
25075
|
-
const junoTaskDir =
|
|
25076
|
-
const isInitialized = await
|
|
25674
|
+
const fs24 = await import('fs-extra');
|
|
25675
|
+
const path25 = await import('path');
|
|
25676
|
+
const junoTaskDir = path25.join(process.cwd(), ".juno_task");
|
|
25677
|
+
const isInitialized = await fs24.pathExists(junoTaskDir);
|
|
25077
25678
|
if (!isHelpOrVersion && !hasNoArguments && !isInitCommand && isInitialized) {
|
|
25078
25679
|
try {
|
|
25079
25680
|
const { validateStartupConfigs: validateStartupConfigs2 } = await Promise.resolve().then(() => (init_startup_validation(), startup_validation_exports));
|
|
@@ -25100,6 +25701,7 @@ async function main() {
|
|
|
25100
25701
|
configureHelpCommand(program);
|
|
25101
25702
|
setupConfigCommand(program);
|
|
25102
25703
|
program.addCommand(createServicesCommand());
|
|
25704
|
+
program.addCommand(createSkillsCommand());
|
|
25103
25705
|
setupCompletion(program);
|
|
25104
25706
|
setupAliases(program);
|
|
25105
25707
|
setupMainCommand(program);
|