jeo-code 0.5.0 → 0.5.2
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.ja.md +2 -2
- package/README.ko.md +2 -2
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/package.json +1 -1
- package/src/commands/launch.ts +65 -3
- package/src/skills/catalog.ts +27 -1
package/README.ja.md
CHANGED
|
@@ -150,11 +150,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
|
|
|
150
150
|
## 変更履歴 (Changelog)
|
|
151
151
|
|
|
152
152
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
153
|
+
- **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
|
|
154
|
+
- **[0.5.1]** (2026-06-14) — cmd-mode `!<command>` shell escape — run a shell command without engaging the agent.
|
|
153
155
|
- **[0.5.0]** (2026-06-14) — Performance: workspace-scan, workflow-state, and DNA-Claw HUD caches; plus a credential-safety fix that never wipes OAuth over an invalid config.
|
|
154
156
|
- **[0.4.9]** (2026-06-14) — Live-frame width-clamp (content-sized height) replaces the constant-height approach, typed text shows during a running turn, and a docs/AGENTS refresh.
|
|
155
157
|
- **[0.4.8]** (2026-06-14) — Live-frame stability: constant-height live turn, renderer self-heal off-by-one fix, and frame-safe child-stdout sanitizing — no more duplicate model bar or torn escapes.
|
|
156
|
-
- **[0.4.7]** (2026-06-14) — Detached subagents + `subagent` control tool, live shaded in-flight output, registry-driven providers, fuller `read` budget, styled italics in the final report, and `gjc` retired.
|
|
157
|
-
- **[0.4.6]** (2026-06-14) — Width-correct forge cards for CJK/emoji, red borders on failed tool cards, aligned `ooo ralph` monitor HUD, and a per-theme user-card palette.
|
|
158
158
|
|
|
159
159
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
160
160
|
<!-- CHANGELOG:END -->
|
package/README.ko.md
CHANGED
|
@@ -150,11 +150,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
|
|
|
150
150
|
## 변경 이력 (Changelog)
|
|
151
151
|
|
|
152
152
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
153
|
+
- **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
|
|
154
|
+
- **[0.5.1]** (2026-06-14) — cmd-mode `!<command>` shell escape — run a shell command without engaging the agent.
|
|
153
155
|
- **[0.5.0]** (2026-06-14) — Performance: workspace-scan, workflow-state, and DNA-Claw HUD caches; plus a credential-safety fix that never wipes OAuth over an invalid config.
|
|
154
156
|
- **[0.4.9]** (2026-06-14) — Live-frame width-clamp (content-sized height) replaces the constant-height approach, typed text shows during a running turn, and a docs/AGENTS refresh.
|
|
155
157
|
- **[0.4.8]** (2026-06-14) — Live-frame stability: constant-height live turn, renderer self-heal off-by-one fix, and frame-safe child-stdout sanitizing — no more duplicate model bar or torn escapes.
|
|
156
|
-
- **[0.4.7]** (2026-06-14) — Detached subagents + `subagent` control tool, live shaded in-flight output, registry-driven providers, fuller `read` budget, styled italics in the final report, and `gjc` retired.
|
|
157
|
-
- **[0.4.6]** (2026-06-14) — Width-correct forge cards for CJK/emoji, red borders on failed tool cards, aligned `ooo ralph` monitor HUD, and a per-theme user-card palette.
|
|
158
158
|
|
|
159
159
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
160
160
|
<!-- CHANGELOG:END -->
|
package/README.md
CHANGED
|
@@ -150,11 +150,11 @@ Required npm token permissions (repository secret `NPM_TOKEN`):
|
|
|
150
150
|
## Changelog
|
|
151
151
|
|
|
152
152
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
153
|
+
- **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
|
|
154
|
+
- **[0.5.1]** (2026-06-14) — cmd-mode `!<command>` shell escape — run a shell command without engaging the agent.
|
|
153
155
|
- **[0.5.0]** (2026-06-14) — Performance: workspace-scan, workflow-state, and DNA-Claw HUD caches; plus a credential-safety fix that never wipes OAuth over an invalid config.
|
|
154
156
|
- **[0.4.9]** (2026-06-14) — Live-frame width-clamp (content-sized height) replaces the constant-height approach, typed text shows during a running turn, and a docs/AGENTS refresh.
|
|
155
157
|
- **[0.4.8]** (2026-06-14) — Live-frame stability: constant-height live turn, renderer self-heal off-by-one fix, and frame-safe child-stdout sanitizing — no more duplicate model bar or torn escapes.
|
|
156
|
-
- **[0.4.7]** (2026-06-14) — Detached subagents + `subagent` control tool, live shaded in-flight output, registry-driven providers, fuller `read` budget, styled italics in the final report, and `gjc` retired.
|
|
157
|
-
- **[0.4.6]** (2026-06-14) — Width-correct forge cards for CJK/emoji, red borders on failed tool cards, aligned `ooo ralph` monitor HUD, and a per-theme user-card palette.
|
|
158
158
|
|
|
159
159
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
160
160
|
<!-- CHANGELOG:END -->
|
package/README.zh.md
CHANGED
|
@@ -150,11 +150,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
|
|
|
150
150
|
## 更新日志 (Changelog)
|
|
151
151
|
|
|
152
152
|
<!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
|
|
153
|
+
- **[0.5.2]** (2026-06-14) — `$skill` prompt invocation with prefix/fuzzy suggestions, and a per-session input-box hue (amber in cmd-mode).
|
|
154
|
+
- **[0.5.1]** (2026-06-14) — cmd-mode `!<command>` shell escape — run a shell command without engaging the agent.
|
|
153
155
|
- **[0.5.0]** (2026-06-14) — Performance: workspace-scan, workflow-state, and DNA-Claw HUD caches; plus a credential-safety fix that never wipes OAuth over an invalid config.
|
|
154
156
|
- **[0.4.9]** (2026-06-14) — Live-frame width-clamp (content-sized height) replaces the constant-height approach, typed text shows during a running turn, and a docs/AGENTS refresh.
|
|
155
157
|
- **[0.4.8]** (2026-06-14) — Live-frame stability: constant-height live turn, renderer self-heal off-by-one fix, and frame-safe child-stdout sanitizing — no more duplicate model bar or torn escapes.
|
|
156
|
-
- **[0.4.7]** (2026-06-14) — Detached subagents + `subagent` control tool, live shaded in-flight output, registry-driven providers, fuller `read` budget, styled italics in the final report, and `gjc` retired.
|
|
157
|
-
- **[0.4.6]** (2026-06-14) — Width-correct forge cards for CJK/emoji, red borders on failed tool cards, aligned `ooo ralph` monitor HUD, and a per-theme user-card palette.
|
|
158
158
|
|
|
159
159
|
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
160
160
|
<!-- CHANGELOG:END -->
|
package/package.json
CHANGED
package/src/commands/launch.ts
CHANGED
|
@@ -66,7 +66,7 @@ import { stripMarkdown } from "../tui/components/markdown-text";
|
|
|
66
66
|
import { summarizeForgeInvocation } from "../tui/components/forge";
|
|
67
67
|
import { formatDuration, formatUsage } from "../tui/components/duration";
|
|
68
68
|
|
|
69
|
-
import { findTool, searchTool } from "../agent/tools";
|
|
69
|
+
import { findTool, searchTool, bashTool } from "../agent/tools";
|
|
70
70
|
import { loadProjectContext, withProjectContext } from "../agent/context-files";
|
|
71
71
|
import { maybeCompact, historyTokens } from "../agent/compaction";
|
|
72
72
|
import * as path from "node:path";
|
|
@@ -2148,6 +2148,27 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2148
2148
|
let uiTheme = resolveTheme(process.env);
|
|
2149
2149
|
let uiAccent = accentPaint(uiTheme);
|
|
2150
2150
|
let uiAccentShadow = accentShadowPaint(uiTheme);
|
|
2151
|
+
// Input-box border colors. Each opened session gets a DISTINCT hue (so several jeo
|
|
2152
|
+
// sessions are tellable apart at a glance), and cmd-mode (`!`) overrides it with a
|
|
2153
|
+
// caution amber so entering the shell escape is unmistakable.
|
|
2154
|
+
const SESSION_BOX_ACCENTS = ["#48dbfb", "#39ff14", "#a29bfe", "#1dd1a1", "#ff9ff3", "#54a0ff", "#ff6b81", "#c8d6e5"];
|
|
2155
|
+
const CMD_MODE_BOX_ACCENT = "#ffb300";
|
|
2156
|
+
const hexPaint = (hex: string) => (s: string) => chalk.hex(hex)(s);
|
|
2157
|
+
const hexShadowPaint = (hex: string) => (s: string) => chalk.dim(chalk.hex(hex)(s));
|
|
2158
|
+
// Per-process random start so different jeo processes differ at a glance; advanced on
|
|
2159
|
+
// each newly opened session (advanceSessionBoxColor) so consecutive sessions never match.
|
|
2160
|
+
let sessionBoxColorIdx = Math.floor(Math.random() * SESSION_BOX_ACCENTS.length);
|
|
2161
|
+
const advanceSessionBoxColor = (): void => {
|
|
2162
|
+
sessionBoxColorIdx = (sessionBoxColorIdx + 1) % SESSION_BOX_ACCENTS.length;
|
|
2163
|
+
};
|
|
2164
|
+
// Resolve the box painters for the current draft: cmd-mode amber when it starts with
|
|
2165
|
+
// `!`, else the per-session hue, else the theme accent (colorless theme / no session).
|
|
2166
|
+
const boxAccents = (line: string): { accent: (s: string) => string; shadow: (s: string) => string } => {
|
|
2167
|
+
if (!uiTheme.color) return { accent: uiAccent, shadow: uiAccentShadow };
|
|
2168
|
+
if (line.startsWith("!")) return { accent: hexPaint(CMD_MODE_BOX_ACCENT), shadow: hexShadowPaint(CMD_MODE_BOX_ACCENT) };
|
|
2169
|
+
if (sessionId) { const hex = SESSION_BOX_ACCENTS[sessionBoxColorIdx]!; return { accent: hexPaint(hex), shadow: hexShadowPaint(hex) }; }
|
|
2170
|
+
return { accent: uiAccent, shadow: uiAccentShadow };
|
|
2171
|
+
};
|
|
2151
2172
|
const refreshUiTheme = (): void => {
|
|
2152
2173
|
uiTheme = resolveTheme(process.env);
|
|
2153
2174
|
uiAccent = accentPaint(uiTheme);
|
|
@@ -2182,12 +2203,13 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2182
2203
|
// sits at the end of the text.
|
|
2183
2204
|
const rli = rl as unknown as { line?: string; cursor?: number };
|
|
2184
2205
|
const caret = rli.line === line && typeof rli.cursor === "number" ? rli.cursor : line.length;
|
|
2206
|
+
const { accent: boxAccent, shadow: boxShadow } = boxAccents(line);
|
|
2185
2207
|
const frame = renderInputFrame(line, {
|
|
2186
2208
|
cols,
|
|
2187
2209
|
color: true,
|
|
2188
2210
|
unicode: true,
|
|
2189
|
-
accent:
|
|
2190
|
-
accentShadow:
|
|
2211
|
+
accent: boxAccent,
|
|
2212
|
+
accentShadow: boxShadow,
|
|
2191
2213
|
cwdLabel: currentAtLabel(line),
|
|
2192
2214
|
attachmentLabel: pendingImages.length
|
|
2193
2215
|
? `⧉ ${pendingImages.length} image${pendingImages.length > 1 ? "s" : ""} attached — sent with the next message`
|
|
@@ -2998,9 +3020,29 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
2998
3020
|
if (pendingImages.length === 0) continue;
|
|
2999
3021
|
input = "Please look at the attached image(s)."; // image-only submit
|
|
3000
3022
|
}
|
|
3023
|
+
// gjc-parity shell escape: `!<cmd>` runs the command directly in cmd-mode and
|
|
3024
|
+
// prints its output WITHOUT engaging the agent (history untouched), like a REPL
|
|
3025
|
+
// shell escape. The user is explicitly driving their own shell, so the deep-interview
|
|
3026
|
+
// mutation guard (which gates the AGENT's tools) does not apply here.
|
|
3027
|
+
if (input.startsWith("!")) {
|
|
3028
|
+
const cmd = input.slice(1).trim();
|
|
3029
|
+
if (!cmd) {
|
|
3030
|
+
console.log("Usage: !<shell command> (run a command in cmd-mode; the agent and history are untouched)");
|
|
3031
|
+
continue;
|
|
3032
|
+
}
|
|
3033
|
+
try {
|
|
3034
|
+
const res = await bashTool(cmd, cwd);
|
|
3035
|
+
if (res.output) console.log(res.output);
|
|
3036
|
+
if (!res.success && res.error) console.log(chalk.red(res.error));
|
|
3037
|
+
} catch (err) {
|
|
3038
|
+
console.log(chalk.red(`! command failed: ${(err as Error).message}`));
|
|
3039
|
+
}
|
|
3040
|
+
continue;
|
|
3041
|
+
}
|
|
3001
3042
|
if (input === "/" || input === "/?" || input === "/help") {
|
|
3002
3043
|
logLines(formatSlashCommandList(input === "/help" ? "/" : input, skillSlashDetails));
|
|
3003
3044
|
console.log("Tools: read / write / edit / bash / find / search. Sessions persist to .jeo/sessions/.");
|
|
3045
|
+
console.log("Shell: !<command> runs a command in cmd-mode directly (agent/history untouched).");
|
|
3004
3046
|
const tip = getEvolutionTip(history.length, flags.maxSteps > 0 ? flags.maxSteps : initialStepLimit);
|
|
3005
3047
|
console.log(`\n${chalk.cyan("Evolutionary Tip:")} ${tip}`);
|
|
3006
3048
|
continue;
|
|
@@ -3042,6 +3084,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
3042
3084
|
history.length = 1;
|
|
3043
3085
|
if (!flags.noSession) {
|
|
3044
3086
|
sessionId = (await createSession(cwd)).id;
|
|
3087
|
+
advanceSessionBoxColor(); // distinct input-box hue per newly opened session
|
|
3045
3088
|
console.log(`(${verb} — new session ${sessionId})`);
|
|
3046
3089
|
} else {
|
|
3047
3090
|
sessionId = undefined;
|
|
@@ -4095,6 +4138,25 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
|
|
|
4095
4138
|
}
|
|
4096
4139
|
continue;
|
|
4097
4140
|
}
|
|
4141
|
+
// Unresolved `$skill` → suggest precisely, never silently send the typo to the model.
|
|
4142
|
+
// `$exact`/`$prefix` already ran above; a leftover `$word` is a missed skill attempt,
|
|
4143
|
+
// EXCEPT `$UPPERCASE` env-var-style tokens (e.g. `$HOME`), which pass through untouched.
|
|
4144
|
+
if (input.startsWith("$")) {
|
|
4145
|
+
const token = (input.split(/\s+/, 1)[0] ?? "").slice(1);
|
|
4146
|
+
if (token && !/^[A-Z_][A-Z0-9_]*$/.test(token)) {
|
|
4147
|
+
const lc = token.toLowerCase();
|
|
4148
|
+
const prefix = resolvedSkills.filter(s => s.name.toLowerCase().startsWith(lc));
|
|
4149
|
+
if (prefix.length) {
|
|
4150
|
+
console.log(`Ambiguous skill '$${token}'. Did you mean: ${prefix.slice(0, 6).map(s => `$${s.name}`).join(", ")}?`);
|
|
4151
|
+
} else {
|
|
4152
|
+
const names = resolvedSkills.map(s => `$${s.name}`);
|
|
4153
|
+
const shown = names.slice(0, 12).join(", ");
|
|
4154
|
+
const more = names.length > 12 ? ` … +${names.length - 12} more` : "";
|
|
4155
|
+
console.log(`No skill '$${token}'. ${names.length ? `Available: ${shown}${more}` : "No skills are loaded."} (Type $ to autocomplete.)`);
|
|
4156
|
+
}
|
|
4157
|
+
continue;
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4098
4160
|
// Unhandled slash attempt → suggest, don't send the typo to the model.
|
|
4099
4161
|
if (isSlashAttempt(input)) {
|
|
4100
4162
|
const m = matchSlash(input, [...completionContext().slashCommands]);
|
package/src/skills/catalog.ts
CHANGED
|
@@ -475,6 +475,32 @@ export function getSkillFrom(skills: SkillDoc[], name: string): SkillDoc | undef
|
|
|
475
475
|
return skills.find(s => s.name.toLowerCase() === name.toLowerCase());
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
+
/** The single skill whose name PREFIX-matches `query` (case-insensitive), or undefined
|
|
479
|
+
* when zero or many match. Lets `$te` precisely resolve to `$team` without full spelling. */
|
|
480
|
+
export function uniquePrefixSkill(skills: SkillDoc[], query: string): SkillDoc | undefined {
|
|
481
|
+
const q = query.toLowerCase();
|
|
482
|
+
if (!q) return undefined;
|
|
483
|
+
const hits = skills.filter(s => s.name.toLowerCase().startsWith(q));
|
|
484
|
+
return hits.length === 1 ? hits[0] : undefined;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Prefix-first, then fuzzy-subsequence skill suggestions for a `$query` that did NOT
|
|
488
|
+
* resolve — drives the REPL's clear "did you mean / available" feedback. */
|
|
489
|
+
export function suggestSkills(skills: SkillDoc[], query: string): SkillDoc[] {
|
|
490
|
+
const q = query.toLowerCase();
|
|
491
|
+
const prefix = skills.filter(s => s.name.toLowerCase().startsWith(q));
|
|
492
|
+
const seen = new Set(prefix.map(s => s.name));
|
|
493
|
+
const fuzzy = skills.filter(s => !seen.has(s.name) && skillNameSubsequence(q, s.name.toLowerCase()));
|
|
494
|
+
return [...prefix, ...fuzzy];
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** Order-preserving subsequence test (every char of `needle` appears in `hay` L→R). */
|
|
498
|
+
function skillNameSubsequence(needle: string, hay: string): boolean {
|
|
499
|
+
let i = 0;
|
|
500
|
+
for (let j = 0; j < hay.length && i < needle.length; j++) if (hay[j] === needle[i]) i++;
|
|
501
|
+
return i === needle.length;
|
|
502
|
+
}
|
|
503
|
+
|
|
478
504
|
/** Case-insensitive lookup by direct slash alias, e.g. `/speckit.plan`. */
|
|
479
505
|
export function getSkillBySlash(skills: SkillDoc[], command: string): SkillDoc | undefined {
|
|
480
506
|
const q = command.toLowerCase();
|
|
@@ -514,7 +540,7 @@ export function parseSkillInvocation(input: string, skills: SkillDoc[]): SkillIn
|
|
|
514
540
|
// only when a skill with that exact name is loaded — `$HOME is what?` or any
|
|
515
541
|
// unknown `$word` falls through to the model as an ordinary prompt.
|
|
516
542
|
if (command.length > 1 && command.startsWith("$")) {
|
|
517
|
-
const dollarSkill = getSkillFrom(skills, command.slice(1));
|
|
543
|
+
const dollarSkill = getSkillFrom(skills, command.slice(1)) ?? uniquePrefixSkill(skills, command.slice(1));
|
|
518
544
|
if (dollarSkill) {
|
|
519
545
|
return { skill: dollarSkill, intent: trimmed.slice(command.length).trim(), invokedAs: command };
|
|
520
546
|
}
|