jeo-code 0.7.15 → 0.7.16

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/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
  The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
8
8
 
9
9
 
10
+ ## [0.7.16] - 2026-06-25
11
+ _Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite._
12
+
13
+ ### Added
14
+ - **`jeo skills sync` installs and reconciles the bundled workflow skills into `~/.jeo/skills`.** New `syncBundledSkills(dir, { check, force })` (`src/commands/skills.ts`) reconciles the built-in `SKILLS` against a target dir — defaulting to `userSkillsDir()` (`~/.jeo/skills`, honoring `JEO_CONFIG_DIR`/`$HOME`, matching the highest-precedence flat dir in `skillDirs`). Missing skills are installed; a differing local copy is **preserved** by default (gjc `setup defaults` parity) unless `--force` overwrites it. `--check` is a pure drift report that writes nothing and sets a non-zero exit code on drift, so CI can gate on bundled-skill staleness. `--json` emits the structured `SkillSyncResult` (per-skill `status`/`action`, `drift`, `wrote`, `mode`). A new `bundledSkillFileContent` helper (`src/skills/catalog.ts`) defines the canonical on-disk bytes (raw `SKILL.md` when present, decorated render otherwise) shared by `sync` and the existing `--write` path. The `skills` command summary/usage (`src/cli/runner.ts`) and the list footer now advertise the subcommand.
15
+
16
+ ### Verified
17
+ - `bun run typecheck` clean; full suite green — 1960 pass / 0 fail across 233 files — including the new `test/skills-sync.test.ts` (fresh install, idempotent re-run, `--check` drift report without writing, default-preserve vs `--force` overwrite, single-file reinstall, non-zero `--check` exit via `JEO_CONFIG_DIR`, and `--json` shape).
18
+
19
+
10
20
  ## [0.7.15] - 2026-06-25
11
21
  _Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9._
12
22
 
package/README.ja.md CHANGED
@@ -205,11 +205,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
205
205
  ## 変更履歴 (Changelog)
206
206
 
207
207
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
208
+ - **[0.7.16]** (2026-06-25) — Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite.
208
209
  - **[0.7.15]** (2026-06-25) — Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9.
209
210
  - **[0.7.14]** (2026-06-25) — Quieter, cleaner output: a new `-q`/`--quiet` flag strips launch banners and courtesy logs, `/resume` no longer dumps walls of raw escaped JSON into scrollback, and a turn that edits files now reliably surfaces those paths to orchestrators.
210
211
  - **[0.7.9]** (2026-06-24) — Local model providers actually run, and OAuth/login hygiene: Ollama requests now carry an explicit `num_ctx` so jeo's large system prompt no longer overflows Ollama's small default window (the "ollama 모델이 안 돌아간다" blocker), LM Studio reasoning models that emit the whole reply on the reasoning channel are recovered instead of dying empty, GGUF chat templates that can't render the `tools` array are retried with native tools stripped, the OAuth success tab auto-closes with a clear hint, the effective-credential picker prefers a verified OAuth login over a stray API key, and abandoned idle `jeo` tmux REPL sessions are reaped on launch.
211
212
  - **[0.7.8]** (2026-06-24) — Long-session performance & resource hygiene: the detached memory-distill worker now always terminates itself (closing the `jeo memory-distill` orphan pileup that pinned each session's transcript in RSS), the live reasoning/output block re-wraps only its trailing window (O(tail) per frame instead of O(len²) over a long stream), the team loop stops reprinting the todo guide every iteration, and the readline caret stays aligned after an image attach.
212
- - **[0.7.7]** (2026-06-23) — Long-running-process hygiene & input robustness: backgrounded grandchildren (`next dev &`, daemons) are now reaped at the turn boundary via per-command process groups — closing the climbing `next-server` RSS leak — while the prompt gains recursive fuzzy `@path` search, ANSI-safe and idle-merged bracketed paste, a configurable OSC 52 clipboard cap, per-role ralplan model routing, and leaked-reasoning-tag stripping on salvaged answers.
213
213
 
214
214
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
215
215
  <!-- CHANGELOG:END -->
package/README.ko.md CHANGED
@@ -205,11 +205,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
205
205
  ## 변경 이력 (Changelog)
206
206
 
207
207
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
208
+ - **[0.7.16]** (2026-06-25) — Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite.
208
209
  - **[0.7.15]** (2026-06-25) — Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9.
209
210
  - **[0.7.14]** (2026-06-25) — Quieter, cleaner output: a new `-q`/`--quiet` flag strips launch banners and courtesy logs, `/resume` no longer dumps walls of raw escaped JSON into scrollback, and a turn that edits files now reliably surfaces those paths to orchestrators.
210
211
  - **[0.7.9]** (2026-06-24) — Local model providers actually run, and OAuth/login hygiene: Ollama requests now carry an explicit `num_ctx` so jeo's large system prompt no longer overflows Ollama's small default window (the "ollama 모델이 안 돌아간다" blocker), LM Studio reasoning models that emit the whole reply on the reasoning channel are recovered instead of dying empty, GGUF chat templates that can't render the `tools` array are retried with native tools stripped, the OAuth success tab auto-closes with a clear hint, the effective-credential picker prefers a verified OAuth login over a stray API key, and abandoned idle `jeo` tmux REPL sessions are reaped on launch.
211
212
  - **[0.7.8]** (2026-06-24) — Long-session performance & resource hygiene: the detached memory-distill worker now always terminates itself (closing the `jeo memory-distill` orphan pileup that pinned each session's transcript in RSS), the live reasoning/output block re-wraps only its trailing window (O(tail) per frame instead of O(len²) over a long stream), the team loop stops reprinting the todo guide every iteration, and the readline caret stays aligned after an image attach.
212
- - **[0.7.7]** (2026-06-23) — Long-running-process hygiene & input robustness: backgrounded grandchildren (`next dev &`, daemons) are now reaped at the turn boundary via per-command process groups — closing the climbing `next-server` RSS leak — while the prompt gains recursive fuzzy `@path` search, ANSI-safe and idle-merged bracketed paste, a configurable OSC 52 clipboard cap, per-role ralplan model routing, and leaked-reasoning-tag stripping on salvaged answers.
213
213
 
214
214
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
215
215
  <!-- CHANGELOG:END -->
package/README.md CHANGED
@@ -207,11 +207,11 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
207
207
  ## Changelog
208
208
 
209
209
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
210
+ - **[0.7.16]** (2026-06-25) — Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite.
210
211
  - **[0.7.15]** (2026-06-25) — Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9.
211
212
  - **[0.7.14]** (2026-06-25) — Quieter, cleaner output: a new `-q`/`--quiet` flag strips launch banners and courtesy logs, `/resume` no longer dumps walls of raw escaped JSON into scrollback, and a turn that edits files now reliably surfaces those paths to orchestrators.
212
213
  - **[0.7.9]** (2026-06-24) — Local model providers actually run, and OAuth/login hygiene: Ollama requests now carry an explicit `num_ctx` so jeo's large system prompt no longer overflows Ollama's small default window (the "ollama 모델이 안 돌아간다" blocker), LM Studio reasoning models that emit the whole reply on the reasoning channel are recovered instead of dying empty, GGUF chat templates that can't render the `tools` array are retried with native tools stripped, the OAuth success tab auto-closes with a clear hint, the effective-credential picker prefers a verified OAuth login over a stray API key, and abandoned idle `jeo` tmux REPL sessions are reaped on launch.
213
214
  - **[0.7.8]** (2026-06-24) — Long-session performance & resource hygiene: the detached memory-distill worker now always terminates itself (closing the `jeo memory-distill` orphan pileup that pinned each session's transcript in RSS), the live reasoning/output block re-wraps only its trailing window (O(tail) per frame instead of O(len²) over a long stream), the team loop stops reprinting the todo guide every iteration, and the readline caret stays aligned after an image attach.
214
- - **[0.7.7]** (2026-06-23) — Long-running-process hygiene & input robustness: backgrounded grandchildren (`next dev &`, daemons) are now reaped at the turn boundary via per-command process groups — closing the climbing `next-server` RSS leak — while the prompt gains recursive fuzzy `@path` search, ANSI-safe and idle-merged bracketed paste, a configurable OSC 52 clipboard cap, per-role ralplan model routing, and leaked-reasoning-tag stripping on salvaged answers.
215
215
 
216
216
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
217
217
  <!-- CHANGELOG:END -->
package/README.zh.md CHANGED
@@ -205,11 +205,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
205
205
  ## 更新日志 (Changelog)
206
206
 
207
207
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
208
+ - **[0.7.16]** (2026-06-25) — Bundled skills get a safe installer: `jeo skills sync` materializes the built-in workflow skills into `~/.jeo/skills`, preserving local edits by default, with a CI-friendly `--check` drift report and a `--force` overwrite.
208
209
  - **[0.7.15]** (2026-06-25) — Rollback release: the runtime source and test suite are restored to the **0.7.9** state. The intervening 0.7.10–0.7.14 runtime changes (quiet-mode/`-q`, `/resume` transcript rework, `mutatedFiles` plumbing, Ollama `num_ctx`, tmux REPL reaping, and related engine/provider/TUI edits) are reverted. Published as a new version because npm cannot re-issue an existing version number; `latest` now serves code identical to 0.7.9.
209
210
  - **[0.7.14]** (2026-06-25) — Quieter, cleaner output: a new `-q`/`--quiet` flag strips launch banners and courtesy logs, `/resume` no longer dumps walls of raw escaped JSON into scrollback, and a turn that edits files now reliably surfaces those paths to orchestrators.
210
211
  - **[0.7.9]** (2026-06-24) — Local model providers actually run, and OAuth/login hygiene: Ollama requests now carry an explicit `num_ctx` so jeo's large system prompt no longer overflows Ollama's small default window (the "ollama 모델이 안 돌아간다" blocker), LM Studio reasoning models that emit the whole reply on the reasoning channel are recovered instead of dying empty, GGUF chat templates that can't render the `tools` array are retried with native tools stripped, the OAuth success tab auto-closes with a clear hint, the effective-credential picker prefers a verified OAuth login over a stray API key, and abandoned idle `jeo` tmux REPL sessions are reaped on launch.
211
212
  - **[0.7.8]** (2026-06-24) — Long-session performance & resource hygiene: the detached memory-distill worker now always terminates itself (closing the `jeo memory-distill` orphan pileup that pinned each session's transcript in RSS), the live reasoning/output block re-wraps only its trailing window (O(tail) per frame instead of O(len²) over a long stream), the team loop stops reprinting the todo guide every iteration, and the readline caret stays aligned after an image attach.
212
- - **[0.7.7]** (2026-06-23) — Long-running-process hygiene & input robustness: backgrounded grandchildren (`next dev &`, daemons) are now reaped at the turn boundary via per-command process groups — closing the climbing `next-server` RSS leak — while the prompt gains recursive fuzzy `@path` search, ANSI-safe and idle-merged bracketed paste, a configurable OSC 52 clipboard cap, per-role ralplan model routing, and leaked-reasoning-tag stripping on salvaged answers.
213
213
 
214
214
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
215
215
  <!-- CHANGELOG:END -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jeo-code",
3
- "version": "0.7.15",
3
+ "version": "0.7.16",
4
4
  "description": "Clean, highly optimized AI coding agent using spec-first loop",
5
5
  "type": "module",
6
6
  "main": "src/cli.ts",
package/src/cli/runner.ts CHANGED
@@ -103,8 +103,8 @@ export const COMMANDS: readonly CommandSpec[] = [
103
103
  },
104
104
  {
105
105
  name: "skills",
106
- summary: "List bundled workflow skills (jeo skills <name> for details).",
107
- usage: "skills [name]",
106
+ summary: "List bundled workflow skills, or sync them into ~/.jeo/skills (jeo skills <name> for details).",
107
+ usage: "skills [list|read <name>|sync [--check|--force] [dir]|--write [dir]] [--json]",
108
108
  loader: async () => {
109
109
  const m = await import("../commands/skills");
110
110
  return args => m.runSkillsCommand(args);
@@ -1,7 +1,109 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
- import { SKILLS, getSkillFrom, formatSkill, loadSkills, skillDirs } from "../skills/catalog";
3
+ import * as os from "node:os";
4
+ import { SKILLS, getSkillFrom, formatSkill, loadSkills, skillDirs, bundledSkillFileContent } from "../skills/catalog";
4
5
  import { getLocalJeoDir } from "../agent/state";
6
+ import { jeoEnv } from "../util/env";
7
+
8
+ /** User-level bundled-skills install dir (`~/.jeo/skills`, honoring JEO_CONFIG_DIR /
9
+ * $HOME overrides) — the destination `jeo skills sync` reconciles against, matching
10
+ * the highest-precedence flat dir in {@link skillDirs}. */
11
+ export function userSkillsDir(): string {
12
+ const userHome = process.env.HOME || os.homedir();
13
+ const home = jeoEnv("CONFIG_DIR") || path.join(userHome, ".jeo");
14
+ return path.join(home, "skills");
15
+ }
16
+
17
+ export type SkillSyncStatus = "missing" | "up-to-date" | "differs";
18
+ export type SkillSyncAction = "installed" | "overwritten" | "preserved" | "unchanged" | "none";
19
+
20
+ export interface SkillSyncEntry {
21
+ name: string;
22
+ /** State of the on-disk file BEFORE any write this run. */
23
+ status: SkillSyncStatus;
24
+ /** What sync did (or, for --check, "none"). */
25
+ action: SkillSyncAction;
26
+ path: string;
27
+ }
28
+
29
+ export interface SkillSyncResult {
30
+ dir: string;
31
+ entries: SkillSyncEntry[];
32
+ /** true when any bundled skill is missing or differs from its on-disk copy. */
33
+ drift: boolean;
34
+ /** Number of files actually written this run. */
35
+ wrote: number;
36
+ }
37
+
38
+ /** Reconcile the bundled workflow skills against `dir` (default {@link userSkillsDir}).
39
+ * Default install preserves existing local files (gjc `setup defaults` parity):
40
+ * missing skills are installed, differing ones preserved unless `force`. `check`
41
+ * is a pure drift report — it writes nothing. Returns a structured result so the
42
+ * CLI wrapper owns all console output / exit codes. */
43
+ export async function syncBundledSkills(
44
+ dir: string,
45
+ opts: { check?: boolean; force?: boolean } = {},
46
+ ): Promise<SkillSyncResult> {
47
+ const entries: SkillSyncEntry[] = [];
48
+ let wrote = 0;
49
+ if (!opts.check) await fs.mkdir(dir, { recursive: true });
50
+ for (const s of SKILLS) {
51
+ const file = path.join(dir, `${s.name}.md`);
52
+ const want = bundledSkillFileContent(s);
53
+ let have: string | null = null;
54
+ try { have = await fs.readFile(file, "utf-8"); } catch { /* missing */ }
55
+ const status: SkillSyncStatus = have === null ? "missing" : have === want ? "up-to-date" : "differs";
56
+ let action: SkillSyncAction = "none";
57
+ if (!opts.check) {
58
+ if (status === "missing") {
59
+ await fs.writeFile(file, want, "utf-8");
60
+ action = "installed";
61
+ wrote++;
62
+ } else if (status === "differs") {
63
+ if (opts.force) {
64
+ await fs.writeFile(file, want, "utf-8");
65
+ action = "overwritten";
66
+ wrote++;
67
+ } else {
68
+ action = "preserved";
69
+ }
70
+ } else {
71
+ action = "unchanged";
72
+ }
73
+ }
74
+ entries.push({ name: s.name, status, action, path: file });
75
+ }
76
+ return { dir, entries, drift: entries.some(e => e.status !== "up-to-date"), wrote };
77
+ }
78
+
79
+ async function runSkillsSync(cleanArgs: string[], isJson: boolean, cwd: string): Promise<void> {
80
+ const check = cleanArgs.includes("--check");
81
+ const force = cleanArgs.includes("--force");
82
+ const dirArg = cleanArgs.slice(1).find(a => !a.startsWith("--"));
83
+ const dir = dirArg ? path.resolve(cwd, dirArg) : userSkillsDir();
84
+ const result = await syncBundledSkills(dir, { check, force });
85
+
86
+ if (isJson) {
87
+ console.log(JSON.stringify({ ...result, mode: check ? "check" : force ? "force" : "install" }, null, 2));
88
+ } else if (check) {
89
+ console.log(`\n=== jeo skills sync --check ===\nTarget: ${dir}\n`);
90
+ for (const e of result.entries) console.log(` ${e.status.padEnd(11)} ${e.name}`);
91
+ console.log(
92
+ result.drift
93
+ ? `\nDrift detected. Run 'jeo skills sync' to install missing skills, or '--force' to overwrite differing local copies.`
94
+ : `\nAll ${result.entries.length} bundled skills are in sync.`,
95
+ );
96
+ } else {
97
+ console.log(`\n=== jeo skills sync ===\nTarget: ${dir}\n`);
98
+ for (const e of result.entries) console.log(` ${e.action.padEnd(11)} ${e.name}`);
99
+ const preserved = result.entries.filter(e => e.action === "preserved").length;
100
+ console.log(`\nWrote ${result.wrote} file(s)${preserved ? `, preserved ${preserved} local copy(ies) (use --force to overwrite)` : ""}.`);
101
+ }
102
+
103
+ // --check is CI-usable: non-zero exit signals drift (gjc `setup defaults --check` parity).
104
+ if (check && result.drift) process.exitCode = 1;
105
+ }
106
+
5
107
 
6
108
  function editDistance(a: string, b: string): number {
7
109
  const m = a.length, n = b.length;
@@ -40,12 +142,20 @@ export async function runSkillsCommand(args: string[] = []): Promise<void> {
40
142
  await fs.mkdir(dir, { recursive: true });
41
143
  for (const s of SKILLS) {
42
144
  const file = path.join(dir, `${s.name}.md`);
43
- await fs.writeFile(file, s.raw || `# ${s.name}\n\n${formatSkill(s)}\n`, "utf-8");
145
+ await fs.writeFile(file, bundledSkillFileContent(s), "utf-8");
44
146
  }
45
147
  console.log(`Wrote ${SKILLS.length} skill docs to ${dir}`);
46
148
  return;
47
149
  }
48
150
 
151
+ // `jeo skills sync [--check|--force] [dir]` — bundled skill inspection & safe install
152
+ // (gjc `setup defaults --check/--force` parity): install missing bundled skills into
153
+ // ~/.jeo/skills, preserve existing local copies by default, and report drift.
154
+ if (cleanArgs[0] === "sync") {
155
+ await runSkillsSync(cleanArgs, isJson, cwd);
156
+ return;
157
+ }
158
+
49
159
  // List/lookup over the MERGED set (bundled + ~/.jeo/skills + ~/.agents/skills + project dirs), matching the REPL /skill.
50
160
  const skills = await loadSkills(cwd);
51
161
  const command = cleanArgs[0];
@@ -60,6 +170,7 @@ export async function runSkillsCommand(args: string[] = []): Promise<void> {
60
170
  console.log(` ${s.name.padEnd(16)} ${s.summary}`);
61
171
  }
62
172
  console.log("\nInvoke: /skill <name> [intent] · $<name> [intent] · skill-owned slash aliases (e.g. /speckit.plan)");
173
+ console.log("Install/inspect bundled defaults: 'jeo skills sync' (preserve local) · '--check' (drift report) · '--force' (overwrite local)");
63
174
  console.log("Discovery dirs (later wins on name clash; JEO_SKILLS_DIR adds more):");
64
175
  for (const d of skillDirs(cwd)) console.log(` ${d}`);
65
176
  console.log("");
@@ -70,6 +70,13 @@ export function formatSkill(s: SkillDoc): string {
70
70
  ].filter(Boolean).join("\n");
71
71
  }
72
72
 
73
+ /** Canonical on-disk content for a bundled skill — the exact bytes `jeo skills
74
+ * --write` / `jeo skills sync` materialize into a `<name>.md` file. Prefers the
75
+ * original SKILL.md text; falls back to a decorated render for skills without raw. */
76
+ export function bundledSkillFileContent(s: SkillDoc): string {
77
+ return s.raw || `# ${s.name}\n\n${formatSkill(s)}\n`;
78
+ }
79
+
73
80
  function compactSkillExecutionBrief(skill: SkillDoc): string {
74
81
  const aliases = skillSlashAliases(skill);
75
82
  let details = skill.details;