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.mjs
CHANGED
|
@@ -143,8 +143,8 @@ var init_types = __esm({
|
|
|
143
143
|
};
|
|
144
144
|
FileSystemError = class extends CLIError {
|
|
145
145
|
code = "FILESYSTEM_ERROR";
|
|
146
|
-
constructor(message,
|
|
147
|
-
super(
|
|
146
|
+
constructor(message, path25) {
|
|
147
|
+
super(path25 ? `${message}: ${path25}` : message);
|
|
148
148
|
this.suggestions = [
|
|
149
149
|
"Check file/directory permissions",
|
|
150
150
|
"Verify path exists and is accessible",
|
|
@@ -1291,6 +1291,8 @@ var init_config = __esm({
|
|
|
1291
1291
|
defaultBackend: BackendTypeSchema.describe("Default backend to use for task execution"),
|
|
1292
1292
|
defaultMaxIterations: z.number().int().min(1).max(1e3).describe("Default maximum number of iterations for task execution"),
|
|
1293
1293
|
defaultModel: z.string().optional().describe("Default model to use for the subagent"),
|
|
1294
|
+
// Project metadata
|
|
1295
|
+
mainTask: z.string().optional().describe("Main task objective for the project"),
|
|
1294
1296
|
// Logging settings
|
|
1295
1297
|
logLevel: LogLevelSchema.describe("Logging level for the application"),
|
|
1296
1298
|
logFile: z.string().optional().describe("Path to log file (optional)"),
|
|
@@ -1819,8 +1821,8 @@ ${helpText}
|
|
|
1819
1821
|
}
|
|
1820
1822
|
}
|
|
1821
1823
|
if (options.cwd) {
|
|
1822
|
-
const
|
|
1823
|
-
if (!await
|
|
1824
|
+
const fs24 = await import('fs-extra');
|
|
1825
|
+
if (!await fs24.pathExists(options.cwd)) {
|
|
1824
1826
|
throw new ValidationError(
|
|
1825
1827
|
`Working directory does not exist: ${options.cwd}`,
|
|
1826
1828
|
["Verify the path exists", "Use absolute paths to avoid ambiguity"]
|
|
@@ -3647,31 +3649,103 @@ function parseResetTime(message) {
|
|
|
3647
3649
|
}
|
|
3648
3650
|
return { resetTime, timezone };
|
|
3649
3651
|
}
|
|
3652
|
+
function parseCodexResetTime(message) {
|
|
3653
|
+
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;
|
|
3654
|
+
const match = message.match(resetPattern);
|
|
3655
|
+
if (!match) {
|
|
3656
|
+
return null;
|
|
3657
|
+
}
|
|
3658
|
+
const monthStr = match[1];
|
|
3659
|
+
const day = parseInt(match[2], 10);
|
|
3660
|
+
const year = parseInt(match[3], 10);
|
|
3661
|
+
let hours = parseInt(match[4], 10);
|
|
3662
|
+
const minutes = parseInt(match[5], 10);
|
|
3663
|
+
const ampm = match[6].toUpperCase();
|
|
3664
|
+
const MONTH_MAP = {
|
|
3665
|
+
"jan": 0,
|
|
3666
|
+
"january": 0,
|
|
3667
|
+
"feb": 1,
|
|
3668
|
+
"february": 1,
|
|
3669
|
+
"mar": 2,
|
|
3670
|
+
"march": 2,
|
|
3671
|
+
"apr": 3,
|
|
3672
|
+
"april": 3,
|
|
3673
|
+
"may": 4,
|
|
3674
|
+
"jun": 5,
|
|
3675
|
+
"june": 5,
|
|
3676
|
+
"jul": 6,
|
|
3677
|
+
"july": 6,
|
|
3678
|
+
"aug": 7,
|
|
3679
|
+
"august": 7,
|
|
3680
|
+
"sep": 8,
|
|
3681
|
+
"september": 8,
|
|
3682
|
+
"oct": 9,
|
|
3683
|
+
"october": 9,
|
|
3684
|
+
"nov": 10,
|
|
3685
|
+
"november": 10,
|
|
3686
|
+
"dec": 11,
|
|
3687
|
+
"december": 11
|
|
3688
|
+
};
|
|
3689
|
+
const month = MONTH_MAP[monthStr.toLowerCase()];
|
|
3690
|
+
if (month === void 0) {
|
|
3691
|
+
return null;
|
|
3692
|
+
}
|
|
3693
|
+
if (ampm === "PM" && hours !== 12) {
|
|
3694
|
+
hours += 12;
|
|
3695
|
+
} else if (ampm === "AM" && hours === 12) {
|
|
3696
|
+
hours = 0;
|
|
3697
|
+
}
|
|
3698
|
+
const resetTime = new Date(year, month, day, hours, minutes, 0, 0);
|
|
3699
|
+
const now = /* @__PURE__ */ new Date();
|
|
3700
|
+
if (resetTime.getTime() <= now.getTime()) {
|
|
3701
|
+
resetTime.setTime(resetTime.getTime() + 24 * 60 * 60 * 1e3);
|
|
3702
|
+
}
|
|
3703
|
+
return { resetTime };
|
|
3704
|
+
}
|
|
3650
3705
|
function detectQuotaLimit(message) {
|
|
3651
3706
|
if (!message || typeof message !== "string") {
|
|
3652
3707
|
return { detected: false };
|
|
3653
3708
|
}
|
|
3654
|
-
const
|
|
3655
|
-
|
|
3709
|
+
const claudePattern = /you'?ve hit your limit/i;
|
|
3710
|
+
const codexPattern = /you'?ve hit your usage limit/i;
|
|
3711
|
+
const isClaudeQuota = claudePattern.test(message) && !codexPattern.test(message);
|
|
3712
|
+
const isCodexQuota = codexPattern.test(message);
|
|
3713
|
+
if (!isClaudeQuota && !isCodexQuota) {
|
|
3656
3714
|
return { detected: false };
|
|
3657
3715
|
}
|
|
3658
|
-
const
|
|
3659
|
-
|
|
3716
|
+
const source = isCodexQuota ? "codex" : "claude";
|
|
3717
|
+
const parsedClaude = parseResetTime(message);
|
|
3718
|
+
if (parsedClaude) {
|
|
3660
3719
|
const now = /* @__PURE__ */ new Date();
|
|
3661
|
-
const sleepDurationMs = Math.max(0,
|
|
3720
|
+
const sleepDurationMs = Math.max(0, parsedClaude.resetTime.getTime() - now.getTime());
|
|
3662
3721
|
return {
|
|
3663
3722
|
detected: true,
|
|
3664
|
-
resetTime:
|
|
3723
|
+
resetTime: parsedClaude.resetTime,
|
|
3665
3724
|
sleepDurationMs,
|
|
3666
|
-
timezone:
|
|
3667
|
-
originalMessage: message
|
|
3725
|
+
timezone: parsedClaude.timezone,
|
|
3726
|
+
originalMessage: message,
|
|
3727
|
+
source
|
|
3728
|
+
};
|
|
3729
|
+
}
|
|
3730
|
+
const parsedCodex = parseCodexResetTime(message);
|
|
3731
|
+
if (parsedCodex) {
|
|
3732
|
+
const now = /* @__PURE__ */ new Date();
|
|
3733
|
+
const sleepDurationMs = Math.max(0, parsedCodex.resetTime.getTime() - now.getTime());
|
|
3734
|
+
return {
|
|
3735
|
+
detected: true,
|
|
3736
|
+
resetTime: parsedCodex.resetTime,
|
|
3737
|
+
sleepDurationMs,
|
|
3738
|
+
timezone: "local",
|
|
3739
|
+
originalMessage: message,
|
|
3740
|
+
source
|
|
3668
3741
|
};
|
|
3669
3742
|
}
|
|
3670
3743
|
return {
|
|
3671
3744
|
detected: true,
|
|
3672
3745
|
sleepDurationMs: 5 * 60 * 1e3,
|
|
3673
3746
|
// 5 minutes default
|
|
3674
|
-
originalMessage: message
|
|
3747
|
+
originalMessage: message,
|
|
3748
|
+
source
|
|
3675
3749
|
};
|
|
3676
3750
|
}
|
|
3677
3751
|
function formatDuration(ms) {
|
|
@@ -4196,8 +4270,68 @@ var init_shell_backend = __esm({
|
|
|
4196
4270
|
metadata
|
|
4197
4271
|
};
|
|
4198
4272
|
}
|
|
4273
|
+
if (subagentType === "codex") {
|
|
4274
|
+
const codexQuotaMessage = this.extractCodexQuotaMessage(result.output, result.error);
|
|
4275
|
+
if (codexQuotaMessage) {
|
|
4276
|
+
const quotaLimitInfo = detectQuotaLimit(codexQuotaMessage);
|
|
4277
|
+
if (quotaLimitInfo.detected) {
|
|
4278
|
+
const metadata = {
|
|
4279
|
+
structuredOutput: true,
|
|
4280
|
+
contentType: "application/json",
|
|
4281
|
+
rawOutput: result.output,
|
|
4282
|
+
quotaLimitInfo
|
|
4283
|
+
};
|
|
4284
|
+
const structuredPayload = {
|
|
4285
|
+
type: "result",
|
|
4286
|
+
subtype: "error",
|
|
4287
|
+
is_error: true,
|
|
4288
|
+
result: codexQuotaMessage,
|
|
4289
|
+
error: codexQuotaMessage,
|
|
4290
|
+
exit_code: result.exitCode,
|
|
4291
|
+
duration_ms: result.duration,
|
|
4292
|
+
quota_limit: quotaLimitInfo
|
|
4293
|
+
};
|
|
4294
|
+
return {
|
|
4295
|
+
content: JSON.stringify(structuredPayload),
|
|
4296
|
+
metadata
|
|
4297
|
+
};
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4199
4301
|
return { content: result.output, metadata: result.metadata };
|
|
4200
4302
|
}
|
|
4303
|
+
/**
|
|
4304
|
+
* Extract quota limit message from Codex stream output
|
|
4305
|
+
* Codex outputs JSON events like:
|
|
4306
|
+
* {"type": "error", "message": "You've hit your usage limit..."}
|
|
4307
|
+
* {"type": "turn.failed", "error": {"message": "You've hit your usage limit..."}}
|
|
4308
|
+
*/
|
|
4309
|
+
extractCodexQuotaMessage(output, stderr) {
|
|
4310
|
+
const sources = [output, stderr].filter(Boolean);
|
|
4311
|
+
for (const source of sources) {
|
|
4312
|
+
const lines = source.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
4313
|
+
for (const line of lines) {
|
|
4314
|
+
try {
|
|
4315
|
+
const parsed = JSON.parse(line);
|
|
4316
|
+
if (parsed?.type === "error" && parsed?.message) {
|
|
4317
|
+
if (/you'?ve hit your usage limit/i.test(parsed.message)) {
|
|
4318
|
+
return parsed.message;
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
if (parsed?.type === "turn.failed" && parsed?.error?.message) {
|
|
4322
|
+
if (/you'?ve hit your usage limit/i.test(parsed.error.message)) {
|
|
4323
|
+
return parsed.error.message;
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
} catch {
|
|
4327
|
+
if (/you'?ve hit your usage limit/i.test(line)) {
|
|
4328
|
+
return line;
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
4333
|
+
return null;
|
|
4334
|
+
}
|
|
4201
4335
|
/**
|
|
4202
4336
|
* Extract the last valid JSON object from a script's stdout to use as a structured payload fallback.
|
|
4203
4337
|
*/
|
|
@@ -5177,8 +5311,9 @@ var init_engine = __esm({
|
|
|
5177
5311
|
hour12: true,
|
|
5178
5312
|
timeZoneName: "short"
|
|
5179
5313
|
}) : "unknown";
|
|
5314
|
+
const sourceLabel = quotaInfo.source === "codex" ? "Codex" : "Claude";
|
|
5180
5315
|
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`);
|
|
5181
|
-
engineLogger.info(`\u2551
|
|
5316
|
+
engineLogger.info(`\u2551 ${sourceLabel} Quota Limit Reached${" ".repeat(44 - sourceLabel.length - " Quota Limit Reached".length)}\u2551`);
|
|
5182
5317
|
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`);
|
|
5183
5318
|
engineLogger.info(`\u2551 Quota resets at: ${resetTimeStr2.padEnd(44)}\u2551`);
|
|
5184
5319
|
engineLogger.info(`\u2551 Behavior: raise (exit immediately) \u2551`);
|
|
@@ -5188,7 +5323,7 @@ var init_engine = __esm({
|
|
|
5188
5323
|
engineLogger.info(`\u2551 Or in config.json: { "onHourlyLimit": "wait" } \u2551`);
|
|
5189
5324
|
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`);
|
|
5190
5325
|
this.emit("quota-limit:raise", { context, quotaInfo });
|
|
5191
|
-
throw new Error(
|
|
5326
|
+
throw new Error(`${sourceLabel} quota limit reached. Quota resets at ${resetTimeStr2}. Use --on-hourly-limit wait to auto-retry.`);
|
|
5192
5327
|
}
|
|
5193
5328
|
const waitTimeMs = quotaInfo.sleepDurationMs;
|
|
5194
5329
|
const maxWaitTimeMs = 12 * 60 * 60 * 1e3;
|
|
@@ -5204,8 +5339,9 @@ var init_engine = __esm({
|
|
|
5204
5339
|
timeZoneName: "short"
|
|
5205
5340
|
}) : "unknown";
|
|
5206
5341
|
const durationStr = formatDuration(waitTimeMs);
|
|
5342
|
+
const waitSourceLabel = quotaInfo.source === "codex" ? "Codex" : "Claude";
|
|
5207
5343
|
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`);
|
|
5208
|
-
engineLogger.info(`\u2551
|
|
5344
|
+
engineLogger.info(`\u2551 ${waitSourceLabel} Quota Limit Reached${" ".repeat(44 - waitSourceLabel.length - " Quota Limit Reached".length)}\u2551`);
|
|
5209
5345
|
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`);
|
|
5210
5346
|
engineLogger.info(`\u2551 Quota resets at: ${resetTimeStr.padEnd(44)}\u2551`);
|
|
5211
5347
|
engineLogger.info(`\u2551 Sleeping for: ${durationStr.padEnd(44)}\u2551`);
|
|
@@ -12664,12 +12800,12 @@ function safeConsoleOutput(message, options = {}) {
|
|
|
12664
12800
|
const capabilities = getTUICapabilities();
|
|
12665
12801
|
let output = message;
|
|
12666
12802
|
if (capabilities.hasColors && (color || bold)) {
|
|
12667
|
-
const
|
|
12668
|
-
if (color &&
|
|
12669
|
-
output =
|
|
12803
|
+
const chalk25 = __require("chalk");
|
|
12804
|
+
if (color && chalk25[color]) {
|
|
12805
|
+
output = chalk25[color](output);
|
|
12670
12806
|
}
|
|
12671
12807
|
if (bold) {
|
|
12672
|
-
output =
|
|
12808
|
+
output = chalk25.bold(output);
|
|
12673
12809
|
}
|
|
12674
12810
|
}
|
|
12675
12811
|
console[type](output);
|
|
@@ -13005,8 +13141,45 @@ var init_tui = __esm({
|
|
|
13005
13141
|
var main_exports = {};
|
|
13006
13142
|
__export(main_exports, {
|
|
13007
13143
|
createMainCommand: () => createMainCommand,
|
|
13144
|
+
getDefaultModelForSubagent: () => getDefaultModelForSubagent,
|
|
13145
|
+
isModelCompatibleWithSubagent: () => isModelCompatibleWithSubagent,
|
|
13008
13146
|
mainCommandHandler: () => mainCommandHandler
|
|
13009
13147
|
});
|
|
13148
|
+
function getDefaultModelForSubagent(subagent) {
|
|
13149
|
+
const modelDefaults = {
|
|
13150
|
+
claude: ":sonnet",
|
|
13151
|
+
codex: ":codex",
|
|
13152
|
+
// Expands to gpt-5.3-codex in codex.py
|
|
13153
|
+
gemini: ":pro",
|
|
13154
|
+
// Expands to gemini-2.5-pro in gemini.py
|
|
13155
|
+
cursor: "auto"
|
|
13156
|
+
};
|
|
13157
|
+
return modelDefaults[subagent] || modelDefaults.claude;
|
|
13158
|
+
}
|
|
13159
|
+
function isModelCompatibleWithSubagent(model, subagent) {
|
|
13160
|
+
if (!model.startsWith(":")) {
|
|
13161
|
+
return true;
|
|
13162
|
+
}
|
|
13163
|
+
const claudeShorthands = [":sonnet", ":haiku", ":opus"];
|
|
13164
|
+
const codexShorthands = [":codex", ":codex-mini", ":gpt-5", ":mini"];
|
|
13165
|
+
const geminiShorthands = [":pro", ":flash"];
|
|
13166
|
+
const isClaudeModel = claudeShorthands.includes(model) || model.startsWith(":claude");
|
|
13167
|
+
const isCodexModel = codexShorthands.includes(model) || model.startsWith(":gpt");
|
|
13168
|
+
const isGeminiModel = geminiShorthands.includes(model) || model.startsWith(":gemini");
|
|
13169
|
+
switch (subagent) {
|
|
13170
|
+
case "claude":
|
|
13171
|
+
return isClaudeModel || !isCodexModel && !isGeminiModel;
|
|
13172
|
+
case "codex":
|
|
13173
|
+
return isCodexModel || !isClaudeModel && !isGeminiModel;
|
|
13174
|
+
case "gemini":
|
|
13175
|
+
return isGeminiModel || !isClaudeModel && !isCodexModel;
|
|
13176
|
+
case "cursor":
|
|
13177
|
+
return true;
|
|
13178
|
+
// Cursor accepts any model
|
|
13179
|
+
default:
|
|
13180
|
+
return true;
|
|
13181
|
+
}
|
|
13182
|
+
}
|
|
13010
13183
|
async function mainCommandHandler(args, options, command) {
|
|
13011
13184
|
try {
|
|
13012
13185
|
const validSubagents = ["claude", "cursor", "codex", "gemini"];
|
|
@@ -13058,13 +13231,15 @@ async function mainCommandHandler(args, options, command) {
|
|
|
13058
13231
|
["Use -1 for unlimited iterations", "Use positive integers like 1, 5, or 10", "Example: -i 5"]
|
|
13059
13232
|
);
|
|
13060
13233
|
}
|
|
13234
|
+
const configModelIsValid = config.defaultModel && config.defaultSubagent === options.subagent && isModelCompatibleWithSubagent(config.defaultModel, options.subagent);
|
|
13235
|
+
const resolvedModel = options.model || (configModelIsValid ? config.defaultModel : void 0) || getDefaultModelForSubagent(options.subagent);
|
|
13061
13236
|
const executionRequest = createExecutionRequest({
|
|
13062
13237
|
instruction,
|
|
13063
13238
|
subagent: options.subagent,
|
|
13064
13239
|
backend: selectedBackend,
|
|
13065
13240
|
workingDirectory: config.workingDirectory,
|
|
13066
13241
|
maxIterations: options.maxIterations ?? config.defaultMaxIterations,
|
|
13067
|
-
model:
|
|
13242
|
+
model: resolvedModel,
|
|
13068
13243
|
agents: options.agents,
|
|
13069
13244
|
tools: options.tools,
|
|
13070
13245
|
allowedTools: options.allowedTools,
|
|
@@ -13586,6 +13761,289 @@ var init_main = __esm({
|
|
|
13586
13761
|
}
|
|
13587
13762
|
});
|
|
13588
13763
|
|
|
13764
|
+
// src/utils/skill-installer.ts
|
|
13765
|
+
var skill_installer_exports = {};
|
|
13766
|
+
__export(skill_installer_exports, {
|
|
13767
|
+
SkillInstaller: () => SkillInstaller
|
|
13768
|
+
});
|
|
13769
|
+
var SkillInstaller;
|
|
13770
|
+
var init_skill_installer = __esm({
|
|
13771
|
+
"src/utils/skill-installer.ts"() {
|
|
13772
|
+
init_version();
|
|
13773
|
+
SkillInstaller = class {
|
|
13774
|
+
/**
|
|
13775
|
+
* Skill groups define which template folders map to which project directories.
|
|
13776
|
+
* New agents can be added here without changing any other logic.
|
|
13777
|
+
*/
|
|
13778
|
+
static SKILL_GROUPS = [
|
|
13779
|
+
{ name: "codex", destDir: ".agents/skills" },
|
|
13780
|
+
{ name: "claude", destDir: ".claude/skills" }
|
|
13781
|
+
];
|
|
13782
|
+
/**
|
|
13783
|
+
* Get the templates skills directory from the package
|
|
13784
|
+
*/
|
|
13785
|
+
static getPackageSkillsDir() {
|
|
13786
|
+
const __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
|
|
13787
|
+
const candidates = [
|
|
13788
|
+
path3.join(__dirname2, "..", "..", "templates", "skills"),
|
|
13789
|
+
// dist (production)
|
|
13790
|
+
path3.join(__dirname2, "..", "templates", "skills")
|
|
13791
|
+
// src (development)
|
|
13792
|
+
];
|
|
13793
|
+
for (const skillsPath of candidates) {
|
|
13794
|
+
if (fs3.existsSync(skillsPath)) {
|
|
13795
|
+
return skillsPath;
|
|
13796
|
+
}
|
|
13797
|
+
}
|
|
13798
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13799
|
+
console.error("[DEBUG] SkillInstaller: Could not find templates/skills directory");
|
|
13800
|
+
console.error("[DEBUG] Tried:", candidates);
|
|
13801
|
+
}
|
|
13802
|
+
return null;
|
|
13803
|
+
}
|
|
13804
|
+
/**
|
|
13805
|
+
* Get list of skill files in a specific skill group template directory.
|
|
13806
|
+
* Returns paths relative to the group directory.
|
|
13807
|
+
*/
|
|
13808
|
+
static async getSkillFiles(groupDir) {
|
|
13809
|
+
if (!await fs3.pathExists(groupDir)) {
|
|
13810
|
+
return [];
|
|
13811
|
+
}
|
|
13812
|
+
const files = [];
|
|
13813
|
+
const walk = async (dir, prefix) => {
|
|
13814
|
+
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
13815
|
+
for (const entry of entries) {
|
|
13816
|
+
if (entry.name.startsWith(".") || entry.name === "__pycache__") {
|
|
13817
|
+
continue;
|
|
13818
|
+
}
|
|
13819
|
+
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
13820
|
+
if (entry.isDirectory()) {
|
|
13821
|
+
await walk(path3.join(dir, entry.name), relPath);
|
|
13822
|
+
} else {
|
|
13823
|
+
files.push(relPath);
|
|
13824
|
+
}
|
|
13825
|
+
}
|
|
13826
|
+
};
|
|
13827
|
+
await walk(groupDir, "");
|
|
13828
|
+
return files;
|
|
13829
|
+
}
|
|
13830
|
+
/**
|
|
13831
|
+
* Install skills for a single skill group.
|
|
13832
|
+
* Only copies skill files, does NOT delete or modify any other files in the destination.
|
|
13833
|
+
*
|
|
13834
|
+
* @param projectDir - The project root directory
|
|
13835
|
+
* @param group - The skill group to install
|
|
13836
|
+
* @param silent - If true, suppresses console output
|
|
13837
|
+
* @param force - If true, overwrite even if content is identical
|
|
13838
|
+
* @returns number of files installed or updated
|
|
13839
|
+
*/
|
|
13840
|
+
static async installGroup(projectDir, group, silent = true, force = false) {
|
|
13841
|
+
const debug = process.env.JUNO_CODE_DEBUG === "1";
|
|
13842
|
+
const packageSkillsDir = this.getPackageSkillsDir();
|
|
13843
|
+
if (!packageSkillsDir) {
|
|
13844
|
+
if (debug) {
|
|
13845
|
+
console.error("[DEBUG] SkillInstaller: Package skills directory not found");
|
|
13846
|
+
}
|
|
13847
|
+
return 0;
|
|
13848
|
+
}
|
|
13849
|
+
const sourceGroupDir = path3.join(packageSkillsDir, group.name);
|
|
13850
|
+
const destGroupDir = path3.join(projectDir, group.destDir);
|
|
13851
|
+
const skillFiles = await this.getSkillFiles(sourceGroupDir);
|
|
13852
|
+
if (skillFiles.length === 0) {
|
|
13853
|
+
if (debug) {
|
|
13854
|
+
console.error(`[DEBUG] SkillInstaller: No skill files found for group '${group.name}'`);
|
|
13855
|
+
}
|
|
13856
|
+
return 0;
|
|
13857
|
+
}
|
|
13858
|
+
await fs3.ensureDir(destGroupDir);
|
|
13859
|
+
let installed = 0;
|
|
13860
|
+
for (const relFile of skillFiles) {
|
|
13861
|
+
const srcPath = path3.join(sourceGroupDir, relFile);
|
|
13862
|
+
const destPath = path3.join(destGroupDir, relFile);
|
|
13863
|
+
const destParent = path3.dirname(destPath);
|
|
13864
|
+
await fs3.ensureDir(destParent);
|
|
13865
|
+
let shouldCopy = force;
|
|
13866
|
+
if (!shouldCopy) {
|
|
13867
|
+
if (!await fs3.pathExists(destPath)) {
|
|
13868
|
+
shouldCopy = true;
|
|
13869
|
+
} else {
|
|
13870
|
+
const [srcContent, destContent] = await Promise.all([
|
|
13871
|
+
fs3.readFile(srcPath, "utf-8"),
|
|
13872
|
+
fs3.readFile(destPath, "utf-8")
|
|
13873
|
+
]);
|
|
13874
|
+
if (srcContent !== destContent) {
|
|
13875
|
+
shouldCopy = true;
|
|
13876
|
+
}
|
|
13877
|
+
}
|
|
13878
|
+
}
|
|
13879
|
+
if (shouldCopy) {
|
|
13880
|
+
await fs3.copy(srcPath, destPath, { overwrite: true });
|
|
13881
|
+
if (relFile.endsWith(".sh") || relFile.endsWith(".py")) {
|
|
13882
|
+
await fs3.chmod(destPath, 493);
|
|
13883
|
+
}
|
|
13884
|
+
installed++;
|
|
13885
|
+
if (debug) {
|
|
13886
|
+
console.error(`[DEBUG] SkillInstaller: Installed ${group.name}/${relFile} -> ${destPath}`);
|
|
13887
|
+
}
|
|
13888
|
+
}
|
|
13889
|
+
}
|
|
13890
|
+
if (installed > 0 && !silent) {
|
|
13891
|
+
console.log(`\u2713 Installed ${installed} skill file(s) for ${group.name} -> ${group.destDir}`);
|
|
13892
|
+
}
|
|
13893
|
+
return installed;
|
|
13894
|
+
}
|
|
13895
|
+
/**
|
|
13896
|
+
* Install skills for all skill groups.
|
|
13897
|
+
* This copies skill files to the appropriate project directories while
|
|
13898
|
+
* preserving any existing files the user may have added.
|
|
13899
|
+
*
|
|
13900
|
+
* @param projectDir - The project root directory
|
|
13901
|
+
* @param silent - If true, suppresses console output
|
|
13902
|
+
* @param force - If true, overwrite even if content matches
|
|
13903
|
+
* @returns true if any skill files were installed or updated
|
|
13904
|
+
*/
|
|
13905
|
+
static async install(projectDir, silent = false, force = false) {
|
|
13906
|
+
const debug = process.env.JUNO_CODE_DEBUG === "1";
|
|
13907
|
+
let totalInstalled = 0;
|
|
13908
|
+
for (const group of this.SKILL_GROUPS) {
|
|
13909
|
+
try {
|
|
13910
|
+
const count = await this.installGroup(projectDir, group, silent, force);
|
|
13911
|
+
totalInstalled += count;
|
|
13912
|
+
} catch (error) {
|
|
13913
|
+
if (debug) {
|
|
13914
|
+
console.error(`[DEBUG] SkillInstaller: Error installing group '${group.name}':`, error);
|
|
13915
|
+
}
|
|
13916
|
+
if (!silent) {
|
|
13917
|
+
console.error(`\u26A0\uFE0F Failed to install skills for ${group.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13918
|
+
}
|
|
13919
|
+
}
|
|
13920
|
+
}
|
|
13921
|
+
if (totalInstalled > 0 && !silent) {
|
|
13922
|
+
console.log(`\u2713 Total: ${totalInstalled} skill file(s) installed/updated`);
|
|
13923
|
+
}
|
|
13924
|
+
return totalInstalled > 0;
|
|
13925
|
+
}
|
|
13926
|
+
/**
|
|
13927
|
+
* Auto-update skills on CLI startup.
|
|
13928
|
+
* Only installs/updates if the project is initialized (.juno_task exists).
|
|
13929
|
+
* Silently does nothing if no skill files are bundled or project is not initialized.
|
|
13930
|
+
*
|
|
13931
|
+
* @param projectDir - The project root directory
|
|
13932
|
+
* @param force - If true, force reinstall all skills
|
|
13933
|
+
* @returns true if any updates occurred
|
|
13934
|
+
*/
|
|
13935
|
+
static async autoUpdate(projectDir, force = false) {
|
|
13936
|
+
try {
|
|
13937
|
+
const debug = process.env.JUNO_CODE_DEBUG === "1";
|
|
13938
|
+
const junoTaskDir = path3.join(projectDir, ".juno_task");
|
|
13939
|
+
if (!await fs3.pathExists(junoTaskDir)) {
|
|
13940
|
+
return false;
|
|
13941
|
+
}
|
|
13942
|
+
if (debug) {
|
|
13943
|
+
console.error(`[DEBUG] SkillInstaller: Auto-updating skills (force=${force})`);
|
|
13944
|
+
}
|
|
13945
|
+
const updated = await this.install(projectDir, true, force);
|
|
13946
|
+
if (updated && debug) {
|
|
13947
|
+
console.error("[DEBUG] SkillInstaller: Skills auto-updated successfully");
|
|
13948
|
+
}
|
|
13949
|
+
return updated;
|
|
13950
|
+
} catch (error) {
|
|
13951
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13952
|
+
console.error("[DEBUG] SkillInstaller: autoUpdate error:", error instanceof Error ? error.message : String(error));
|
|
13953
|
+
}
|
|
13954
|
+
return false;
|
|
13955
|
+
}
|
|
13956
|
+
}
|
|
13957
|
+
/**
|
|
13958
|
+
* Check if any skills need to be installed or updated.
|
|
13959
|
+
*
|
|
13960
|
+
* @param projectDir - The project root directory
|
|
13961
|
+
* @returns true if any skills are missing or outdated
|
|
13962
|
+
*/
|
|
13963
|
+
static async needsUpdate(projectDir) {
|
|
13964
|
+
try {
|
|
13965
|
+
const junoTaskDir = path3.join(projectDir, ".juno_task");
|
|
13966
|
+
if (!await fs3.pathExists(junoTaskDir)) {
|
|
13967
|
+
return false;
|
|
13968
|
+
}
|
|
13969
|
+
const packageSkillsDir = this.getPackageSkillsDir();
|
|
13970
|
+
if (!packageSkillsDir) {
|
|
13971
|
+
return false;
|
|
13972
|
+
}
|
|
13973
|
+
for (const group of this.SKILL_GROUPS) {
|
|
13974
|
+
const sourceGroupDir = path3.join(packageSkillsDir, group.name);
|
|
13975
|
+
const destGroupDir = path3.join(projectDir, group.destDir);
|
|
13976
|
+
const skillFiles = await this.getSkillFiles(sourceGroupDir);
|
|
13977
|
+
for (const relFile of skillFiles) {
|
|
13978
|
+
const srcPath = path3.join(sourceGroupDir, relFile);
|
|
13979
|
+
const destPath = path3.join(destGroupDir, relFile);
|
|
13980
|
+
if (!await fs3.pathExists(destPath)) {
|
|
13981
|
+
return true;
|
|
13982
|
+
}
|
|
13983
|
+
const [srcContent, destContent] = await Promise.all([
|
|
13984
|
+
fs3.readFile(srcPath, "utf-8"),
|
|
13985
|
+
fs3.readFile(destPath, "utf-8")
|
|
13986
|
+
]);
|
|
13987
|
+
if (srcContent !== destContent) {
|
|
13988
|
+
return true;
|
|
13989
|
+
}
|
|
13990
|
+
}
|
|
13991
|
+
}
|
|
13992
|
+
return false;
|
|
13993
|
+
} catch {
|
|
13994
|
+
return false;
|
|
13995
|
+
}
|
|
13996
|
+
}
|
|
13997
|
+
/**
|
|
13998
|
+
* List all skill groups and their installation status.
|
|
13999
|
+
*
|
|
14000
|
+
* @param projectDir - The project root directory
|
|
14001
|
+
* @returns Array of skill group status objects
|
|
14002
|
+
*/
|
|
14003
|
+
static async listSkillGroups(projectDir) {
|
|
14004
|
+
const packageSkillsDir = this.getPackageSkillsDir();
|
|
14005
|
+
const results = [];
|
|
14006
|
+
for (const group of this.SKILL_GROUPS) {
|
|
14007
|
+
const sourceGroupDir = packageSkillsDir ? path3.join(packageSkillsDir, group.name) : "";
|
|
14008
|
+
const destGroupDir = path3.join(projectDir, group.destDir);
|
|
14009
|
+
const skillFiles = packageSkillsDir ? await this.getSkillFiles(sourceGroupDir) : [];
|
|
14010
|
+
const files = [];
|
|
14011
|
+
for (const relFile of skillFiles) {
|
|
14012
|
+
const srcPath = path3.join(sourceGroupDir, relFile);
|
|
14013
|
+
const destPath = path3.join(destGroupDir, relFile);
|
|
14014
|
+
const installed = await fs3.pathExists(destPath);
|
|
14015
|
+
let upToDate = false;
|
|
14016
|
+
if (installed) {
|
|
14017
|
+
try {
|
|
14018
|
+
const [srcContent, destContent] = await Promise.all([
|
|
14019
|
+
fs3.readFile(srcPath, "utf-8"),
|
|
14020
|
+
fs3.readFile(destPath, "utf-8")
|
|
14021
|
+
]);
|
|
14022
|
+
upToDate = srcContent === destContent;
|
|
14023
|
+
} catch {
|
|
14024
|
+
upToDate = false;
|
|
14025
|
+
}
|
|
14026
|
+
}
|
|
14027
|
+
files.push({ name: relFile, installed, upToDate });
|
|
14028
|
+
}
|
|
14029
|
+
results.push({
|
|
14030
|
+
name: group.name,
|
|
14031
|
+
destDir: group.destDir,
|
|
14032
|
+
files
|
|
14033
|
+
});
|
|
14034
|
+
}
|
|
14035
|
+
return results;
|
|
14036
|
+
}
|
|
14037
|
+
/**
|
|
14038
|
+
* Get the list of skill group configurations.
|
|
14039
|
+
*/
|
|
14040
|
+
static getSkillGroups() {
|
|
14041
|
+
return [...this.SKILL_GROUPS];
|
|
14042
|
+
}
|
|
14043
|
+
};
|
|
14044
|
+
}
|
|
14045
|
+
});
|
|
14046
|
+
|
|
13589
14047
|
// src/utils/script-installer.ts
|
|
13590
14048
|
var script_installer_exports = {};
|
|
13591
14049
|
__export(script_installer_exports, {
|
|
@@ -13629,8 +14087,11 @@ var init_script_installer = __esm({
|
|
|
13629
14087
|
"github.py",
|
|
13630
14088
|
// Unified GitHub integration (fetch, respond, sync)
|
|
13631
14089
|
// Claude Code hooks (stored in hooks/ subdirectory)
|
|
13632
|
-
"hooks/session_counter.sh"
|
|
14090
|
+
"hooks/session_counter.sh",
|
|
13633
14091
|
// Session message counter hook for warning about long sessions
|
|
14092
|
+
// Log scanning utility
|
|
14093
|
+
"log_scanner.sh"
|
|
14094
|
+
// Scans log files for errors/exceptions and creates kanban bug reports
|
|
13634
14095
|
];
|
|
13635
14096
|
/**
|
|
13636
14097
|
* Get the templates scripts directory from the package
|
|
@@ -16684,6 +17145,8 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16684
17145
|
defaultSubagent: this.context.subagent,
|
|
16685
17146
|
defaultMaxIterations: 50,
|
|
16686
17147
|
defaultModel: this.getDefaultModelForSubagent(this.context.subagent || "claude"),
|
|
17148
|
+
// Project metadata
|
|
17149
|
+
mainTask: this.context.task || "Project initialization",
|
|
16687
17150
|
// Logging settings
|
|
16688
17151
|
logLevel: "info",
|
|
16689
17152
|
verbose: false,
|
|
@@ -16763,8 +17226,10 @@ ${variables.EDITOR ? `using ${variables.EDITOR} as primary AI subagent` : ""}
|
|
|
16763
17226
|
getDefaultModelForSubagent(subagent) {
|
|
16764
17227
|
const modelDefaults = {
|
|
16765
17228
|
claude: ":sonnet",
|
|
16766
|
-
codex: "
|
|
16767
|
-
|
|
17229
|
+
codex: ":codex",
|
|
17230
|
+
// Expands to gpt-5.3-codex in codex.py
|
|
17231
|
+
gemini: ":pro",
|
|
17232
|
+
// Expands to gemini-2.5-pro in gemini.py
|
|
16768
17233
|
cursor: "auto"
|
|
16769
17234
|
};
|
|
16770
17235
|
return modelDefaults[subagent] || modelDefaults.claude;
|
|
@@ -19114,8 +19579,8 @@ async function compactConfigFile(filePath, options = {}) {
|
|
|
19114
19579
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
19115
19580
|
const ext = path3.extname(filePath);
|
|
19116
19581
|
const basename11 = path3.basename(filePath, ext);
|
|
19117
|
-
const
|
|
19118
|
-
backupPath = path3.join(
|
|
19582
|
+
const dirname14 = path3.dirname(filePath);
|
|
19583
|
+
backupPath = path3.join(dirname14, `${basename11}.backup.${timestamp}${ext}`);
|
|
19119
19584
|
await fs3.writeFile(backupPath, originalContent, "utf-8");
|
|
19120
19585
|
}
|
|
19121
19586
|
const compactionAnalysis = analyzeMarkdownStructure(originalContent);
|
|
@@ -21581,7 +22046,7 @@ var LogViewer = ({
|
|
|
21581
22046
|
init_types();
|
|
21582
22047
|
async function exportLogs(logger2, filepath, options) {
|
|
21583
22048
|
try {
|
|
21584
|
-
const
|
|
22049
|
+
const fs24 = await import('fs-extra');
|
|
21585
22050
|
let entries = logger2.getRecentEntries(options.tail || 1e3);
|
|
21586
22051
|
if (options.level) {
|
|
21587
22052
|
const level = LogLevel[options.level.toUpperCase()];
|
|
@@ -21611,7 +22076,7 @@ async function exportLogs(logger2, filepath, options) {
|
|
|
21611
22076
|
},
|
|
21612
22077
|
entries
|
|
21613
22078
|
};
|
|
21614
|
-
await
|
|
22079
|
+
await fs24.writeFile(filepath, JSON.stringify(exportData, null, 2));
|
|
21615
22080
|
console.log(chalk15.green(`\u2705 Exported ${entries.length} log entries to: ${filepath}`));
|
|
21616
22081
|
} catch (error) {
|
|
21617
22082
|
console.error(chalk15.red(`\u274C Failed to export logs: ${error}`));
|
|
@@ -23501,6 +23966,125 @@ Location: ${ServiceInstaller.getServicesDir()}`));
|
|
|
23501
23966
|
return servicesCmd;
|
|
23502
23967
|
}
|
|
23503
23968
|
|
|
23969
|
+
// src/cli/commands/skills.ts
|
|
23970
|
+
init_version();
|
|
23971
|
+
init_skill_installer();
|
|
23972
|
+
function createSkillsCommand() {
|
|
23973
|
+
const skillsCmd = new Command("skills").description("Manage agent skill files").addHelpText("after", `
|
|
23974
|
+
Examples:
|
|
23975
|
+
$ juno-code skills install Install skill files to project directories
|
|
23976
|
+
$ juno-code skills install --force Force reinstall all skill files
|
|
23977
|
+
$ juno-code skills list List skill groups and their files
|
|
23978
|
+
$ juno-code skills status Check installation status
|
|
23979
|
+
|
|
23980
|
+
Skill files are copied from the juno-code package into the project:
|
|
23981
|
+
- Codex skills -> .agents/skills/
|
|
23982
|
+
- Claude skills -> .claude/skills/
|
|
23983
|
+
|
|
23984
|
+
Skills are installed for ALL agents regardless of which subagent is selected.
|
|
23985
|
+
Existing files in the destination directories are preserved.
|
|
23986
|
+
`);
|
|
23987
|
+
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) => {
|
|
23988
|
+
try {
|
|
23989
|
+
const projectDir = process.cwd();
|
|
23990
|
+
if (!options.force) {
|
|
23991
|
+
const needsUpdate = await SkillInstaller.needsUpdate(projectDir);
|
|
23992
|
+
if (!needsUpdate) {
|
|
23993
|
+
console.log(chalk15.yellow("\u26A0 All skill files are up-to-date"));
|
|
23994
|
+
console.log(chalk15.dim(" Use --force to reinstall"));
|
|
23995
|
+
return;
|
|
23996
|
+
}
|
|
23997
|
+
}
|
|
23998
|
+
console.log(chalk15.blue("Installing skill files..."));
|
|
23999
|
+
const installed = await SkillInstaller.install(projectDir, false, options.force);
|
|
24000
|
+
if (installed) {
|
|
24001
|
+
console.log(chalk15.green("\n\u2713 Skill files installed successfully"));
|
|
24002
|
+
} else {
|
|
24003
|
+
console.log(chalk15.yellow("\u26A0 No skill files to install (templates may be empty)"));
|
|
24004
|
+
}
|
|
24005
|
+
} catch (error) {
|
|
24006
|
+
console.error(chalk15.red("\u2717 Installation failed:"));
|
|
24007
|
+
console.error(chalk15.red(error instanceof Error ? error.message : String(error)));
|
|
24008
|
+
process.exit(1);
|
|
24009
|
+
}
|
|
24010
|
+
});
|
|
24011
|
+
skillsCmd.command("list").alias("ls").description("List skill groups and their files").action(async () => {
|
|
24012
|
+
try {
|
|
24013
|
+
const projectDir = process.cwd();
|
|
24014
|
+
const groups = await SkillInstaller.listSkillGroups(projectDir);
|
|
24015
|
+
if (groups.length === 0) {
|
|
24016
|
+
console.log(chalk15.yellow("\u26A0 No skill groups configured"));
|
|
24017
|
+
return;
|
|
24018
|
+
}
|
|
24019
|
+
let hasAnyFiles = false;
|
|
24020
|
+
for (const group of groups) {
|
|
24021
|
+
console.log(chalk15.blue.bold(`
|
|
24022
|
+
${group.name} skills -> ${group.destDir}/`));
|
|
24023
|
+
if (group.files.length === 0) {
|
|
24024
|
+
console.log(chalk15.dim(" (no skill files bundled yet)"));
|
|
24025
|
+
continue;
|
|
24026
|
+
}
|
|
24027
|
+
hasAnyFiles = true;
|
|
24028
|
+
for (const file of group.files) {
|
|
24029
|
+
const statusIcon = !file.installed ? chalk15.red("\u2717") : file.upToDate ? chalk15.green("\u2713") : chalk15.yellow("\u21BB");
|
|
24030
|
+
const statusLabel = !file.installed ? chalk15.dim("not installed") : file.upToDate ? chalk15.dim("up-to-date") : chalk15.yellow("outdated");
|
|
24031
|
+
console.log(` ${statusIcon} ${file.name} ${statusLabel}`);
|
|
24032
|
+
}
|
|
24033
|
+
}
|
|
24034
|
+
if (!hasAnyFiles) {
|
|
24035
|
+
console.log(chalk15.dim("\nNo skill files bundled yet. Add files to src/templates/skills/<agent>/ to bundle skills."));
|
|
24036
|
+
}
|
|
24037
|
+
} catch (error) {
|
|
24038
|
+
console.error(chalk15.red("\u2717 Failed to list skills:"));
|
|
24039
|
+
console.error(chalk15.red(error instanceof Error ? error.message : String(error)));
|
|
24040
|
+
process.exit(1);
|
|
24041
|
+
}
|
|
24042
|
+
});
|
|
24043
|
+
skillsCmd.command("status").description("Check skill installation status").action(async () => {
|
|
24044
|
+
try {
|
|
24045
|
+
const projectDir = process.cwd();
|
|
24046
|
+
const groups = await SkillInstaller.listSkillGroups(projectDir);
|
|
24047
|
+
const skillGroups = SkillInstaller.getSkillGroups();
|
|
24048
|
+
console.log(chalk15.blue("Skills Status:\n"));
|
|
24049
|
+
console.log(chalk15.dim(" Skill groups:"));
|
|
24050
|
+
for (const sg of skillGroups) {
|
|
24051
|
+
console.log(chalk15.dim(` ${sg.name} -> ${sg.destDir}/`));
|
|
24052
|
+
}
|
|
24053
|
+
const needsUpdate = await SkillInstaller.needsUpdate(projectDir);
|
|
24054
|
+
console.log(`
|
|
24055
|
+
${needsUpdate ? chalk15.yellow("\u26A0 Updates available") : chalk15.green("\u2713 All skills up-to-date")}`);
|
|
24056
|
+
let totalFiles = 0;
|
|
24057
|
+
let installedFiles = 0;
|
|
24058
|
+
let outdatedFiles = 0;
|
|
24059
|
+
for (const group of groups) {
|
|
24060
|
+
for (const file of group.files) {
|
|
24061
|
+
totalFiles++;
|
|
24062
|
+
if (file.installed) {
|
|
24063
|
+
installedFiles++;
|
|
24064
|
+
if (!file.upToDate) {
|
|
24065
|
+
outdatedFiles++;
|
|
24066
|
+
}
|
|
24067
|
+
}
|
|
24068
|
+
}
|
|
24069
|
+
}
|
|
24070
|
+
if (totalFiles > 0) {
|
|
24071
|
+
console.log(chalk15.dim(`
|
|
24072
|
+
Files: ${installedFiles}/${totalFiles} installed, ${outdatedFiles} outdated`));
|
|
24073
|
+
} else {
|
|
24074
|
+
console.log(chalk15.dim("\n No skill files bundled yet"));
|
|
24075
|
+
}
|
|
24076
|
+
if (needsUpdate) {
|
|
24077
|
+
console.log(chalk15.dim("\n Run: juno-code skills install"));
|
|
24078
|
+
}
|
|
24079
|
+
} catch (error) {
|
|
24080
|
+
console.error(chalk15.red("\u2717 Failed to check status:"));
|
|
24081
|
+
console.error(chalk15.red(error instanceof Error ? error.message : String(error)));
|
|
24082
|
+
process.exit(1);
|
|
24083
|
+
}
|
|
24084
|
+
});
|
|
24085
|
+
return skillsCmd;
|
|
24086
|
+
}
|
|
24087
|
+
|
|
23504
24088
|
// src/cli/commands/completion.ts
|
|
23505
24089
|
init_version();
|
|
23506
24090
|
|
|
@@ -24766,10 +25350,10 @@ function setupMainCommand(program) {
|
|
|
24766
25350
|
const allOptions2 = { ...definedGlobalOptions, ...options };
|
|
24767
25351
|
if (allOptions2.tilCompletion || allOptions2.untilCompletion || allOptions2.runUntilCompletion || allOptions2.tillComplete) {
|
|
24768
25352
|
const { spawn: spawn4 } = await import('child_process');
|
|
24769
|
-
const
|
|
24770
|
-
const
|
|
24771
|
-
const scriptPath =
|
|
24772
|
-
if (!await
|
|
25353
|
+
const path25 = await import('path');
|
|
25354
|
+
const fs24 = await import('fs-extra');
|
|
25355
|
+
const scriptPath = path25.join(process.cwd(), ".juno_task", "scripts", "run_until_completion.sh");
|
|
25356
|
+
if (!await fs24.pathExists(scriptPath)) {
|
|
24773
25357
|
console.error(chalk15.red.bold("\n\u274C Error: run_until_completion.sh not found"));
|
|
24774
25358
|
console.error(chalk15.red(` Expected location: ${scriptPath}`));
|
|
24775
25359
|
console.error(chalk15.yellow('\n\u{1F4A1} Suggestion: Run "juno-code init" to initialize the project'));
|
|
@@ -24820,11 +25404,11 @@ function setupMainCommand(program) {
|
|
|
24820
25404
|
return;
|
|
24821
25405
|
}
|
|
24822
25406
|
if (!globalOptions.subagent && !options.prompt && !options.interactive && !options.interactivePrompt) {
|
|
24823
|
-
const
|
|
24824
|
-
const
|
|
25407
|
+
const fs24 = await import('fs-extra');
|
|
25408
|
+
const path25 = await import('path');
|
|
24825
25409
|
const cwd2 = process.cwd();
|
|
24826
|
-
const junoTaskDir =
|
|
24827
|
-
if (await
|
|
25410
|
+
const junoTaskDir = path25.join(cwd2, ".juno_task");
|
|
25411
|
+
if (await fs24.pathExists(junoTaskDir)) {
|
|
24828
25412
|
console.log(chalk15.blue.bold("\u{1F3AF} Juno Code - Auto-detected Initialized Project\n"));
|
|
24829
25413
|
try {
|
|
24830
25414
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -24841,12 +25425,12 @@ function setupMainCommand(program) {
|
|
|
24841
25425
|
allOptions2.subagent = config.defaultSubagent;
|
|
24842
25426
|
console.log(chalk15.gray(`\u{1F916} Using configured subagent: ${chalk15.cyan(config.defaultSubagent)}`));
|
|
24843
25427
|
}
|
|
24844
|
-
const promptFile =
|
|
24845
|
-
if (!allOptions2.prompt && await
|
|
25428
|
+
const promptFile = path25.join(junoTaskDir, "prompt.md");
|
|
25429
|
+
if (!allOptions2.prompt && await fs24.pathExists(promptFile)) {
|
|
24846
25430
|
allOptions2.prompt = promptFile;
|
|
24847
25431
|
console.log(chalk15.gray(`\u{1F4C4} Using default prompt: ${chalk15.cyan(".juno_task/prompt.md")}`));
|
|
24848
25432
|
}
|
|
24849
|
-
if (allOptions2.subagent && (allOptions2.prompt || await
|
|
25433
|
+
if (allOptions2.subagent && (allOptions2.prompt || await fs24.pathExists(promptFile))) {
|
|
24850
25434
|
console.log(chalk15.green("\u2713 Auto-detected project configuration\n"));
|
|
24851
25435
|
const { mainCommandHandler: mainCommandHandler3 } = await Promise.resolve().then(() => (init_main(), main_exports));
|
|
24852
25436
|
await mainCommandHandler3([], allOptions2, command);
|
|
@@ -25028,6 +25612,23 @@ async function main() {
|
|
|
25028
25612
|
console.error("[DEBUG] Script auto-update failed:", error instanceof Error ? error.message : String(error));
|
|
25029
25613
|
}
|
|
25030
25614
|
}
|
|
25615
|
+
try {
|
|
25616
|
+
const { SkillInstaller: SkillInstaller2 } = await Promise.resolve().then(() => (init_skill_installer(), skill_installer_exports));
|
|
25617
|
+
if (isForceUpdate) {
|
|
25618
|
+
console.log(chalk15.blue("\u{1F504} Force updating agent skill files..."));
|
|
25619
|
+
await SkillInstaller2.autoUpdate(process.cwd(), true);
|
|
25620
|
+
console.log(chalk15.green("\u2713 Agent skill files updated"));
|
|
25621
|
+
} else {
|
|
25622
|
+
const updated = await SkillInstaller2.autoUpdate(process.cwd());
|
|
25623
|
+
if (updated && (process.argv.includes("--verbose") || process.argv.includes("-v") || process.env.JUNO_CODE_DEBUG === "1")) {
|
|
25624
|
+
console.error("[DEBUG] Agent skill files auto-updated");
|
|
25625
|
+
}
|
|
25626
|
+
}
|
|
25627
|
+
} catch (error) {
|
|
25628
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
25629
|
+
console.error("[DEBUG] Skill auto-update failed:", error instanceof Error ? error.message : String(error));
|
|
25630
|
+
}
|
|
25631
|
+
}
|
|
25031
25632
|
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");
|
|
25032
25633
|
setupGlobalOptions(program);
|
|
25033
25634
|
const isVerbose = process.argv.includes("--verbose") || process.argv.includes("-v");
|
|
@@ -25035,10 +25636,10 @@ async function main() {
|
|
|
25035
25636
|
const isHelpOrVersion = process.argv.includes("--help") || process.argv.includes("-h") || process.argv.includes("--version") || process.argv.includes("-V");
|
|
25036
25637
|
const hasNoArguments = process.argv.length <= 2;
|
|
25037
25638
|
const isInitCommand = process.argv.includes("init");
|
|
25038
|
-
const
|
|
25039
|
-
const
|
|
25040
|
-
const junoTaskDir =
|
|
25041
|
-
const isInitialized = await
|
|
25639
|
+
const fs24 = await import('fs-extra');
|
|
25640
|
+
const path25 = await import('path');
|
|
25641
|
+
const junoTaskDir = path25.join(process.cwd(), ".juno_task");
|
|
25642
|
+
const isInitialized = await fs24.pathExists(junoTaskDir);
|
|
25042
25643
|
if (!isHelpOrVersion && !hasNoArguments && !isInitCommand && isInitialized) {
|
|
25043
25644
|
try {
|
|
25044
25645
|
const { validateStartupConfigs: validateStartupConfigs2 } = await Promise.resolve().then(() => (init_startup_validation(), startup_validation_exports));
|
|
@@ -25065,6 +25666,7 @@ async function main() {
|
|
|
25065
25666
|
configureHelpCommand(program);
|
|
25066
25667
|
setupConfigCommand(program);
|
|
25067
25668
|
program.addCommand(createServicesCommand());
|
|
25669
|
+
program.addCommand(createSkillsCommand());
|
|
25068
25670
|
setupCompletion(program);
|
|
25069
25671
|
setupAliases(program);
|
|
25070
25672
|
setupMainCommand(program);
|