oh-my-customcode 0.45.3 → 0.47.0

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 CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  **[한국어 문서 (Korean)](./README_ko.md)**
15
15
 
16
- 45 agents. 82 skills. 21 rules. One command.
16
+ 45 agents. 83 skills. 21 rules. One command.
17
17
 
18
18
  ```bash
19
19
  npm install -g oh-my-customcode && cd your-project && omcustom init
@@ -21,17 +21,17 @@ npm install -g oh-my-customcode && cd your-project && omcustom init
21
21
 
22
22
  ---
23
23
 
24
- ## What's New in v0.38.0
24
+ ## What's New in v0.46.0
25
25
 
26
26
  | Feature | Description |
27
27
  |---------|-------------|
28
- | **Interactive Init Wizard** | `omcustom init` now uses `@clack/prompts` for guided setup language, framework, team mode |
29
- | **PostCompact Hook** | Automatic rule reinforcement after context compaction (CC v2.1.76+) prevents rule amnesia |
30
- | **@omcustom/eval-core MVP** | LLM evaluation engine: session/turn/outcome collection, SQLite via Drizzle ORM, CLI interface |
31
- | **Codex-exec Auto-delegation** | Routing skills automatically delegate to Codex when available |
32
- | **CC v2.1.72~v2.1.76 Compatibility** | SessionEnd timeout config, HTML comment optimization, `autoMemoryDirectory`, full model ID support |
33
- | **Template Full Sync** | All 200+ template files are byte-identical to source |
34
- | **Hook System Hardening** | Duplicate recorder removal, context-budget field fix, orphan cleanup |
28
+ | **Rate Limit Monitoring** | Statusline now displays 5-hour rate limit usage with color-coded warnings (CC v2.1.80+) |
29
+ | **Skill Effort Override** | Skills can set `effort` frontmatter to override model effort level at invocation time |
30
+ | **Multi-project Web UI** | `omcustom serve` supports multi-project management with sidebar project selector |
31
+ | **Batch Update UI** | Web dashboard supports visual project update status and batch updates |
32
+ | **CC v2.1.72~v2.1.80 Compatibility** | Rate limits statusline, skill effort frontmatter, settings-based plugin source |
33
+ | **SDD Workflow** | Spec-Driven Development with `sdd/` folder hierarchy and planning-first gates |
34
+ | **Ambiguity Gate** | Pre-routing clarity scoring and clarification questions |
35
35
 
36
36
  ---
37
37
 
@@ -138,7 +138,7 @@ Each agent declares its tools, model, memory scope, and limitations in YAML fron
138
138
 
139
139
  ---
140
140
 
141
- ### Skills (82)
141
+ ### Skills (83)
142
142
 
143
143
  | Category | Count | Includes |
144
144
  |----------|-------|----------|
@@ -170,6 +170,9 @@ All commands are invoked inside the Claude Code conversation.
170
170
  | `/structured-dev-cycle` | 6-stage development: plan → verify → implement → verify → compound → done |
171
171
  | `/deep-plan` | Research-validated planning |
172
172
  | `/research` | 10-team parallel analysis with cross-verification |
173
+ | `/sdd-dev` | Spec-Driven Development workflow |
174
+ | `/ambiguity-gate` | Pre-routing ambiguity analysis |
175
+ | `/adversarial-review` | Attacker-mindset security code review |
173
176
 
174
177
  ### Agent Management
175
178
 
@@ -177,10 +180,17 @@ All commands are invoked inside the Claude Code conversation.
177
180
  |---------|-------------|
178
181
  | `/omcustom:analysis` | Analyze project, auto-configure agents and skills |
179
182
  | `/omcustom:create-agent` | Create a new agent |
180
- | `/omcustom:takeover` | Extract canonical spec from existing agent or skill |
183
+ | `/omcustom-takeover` | Extract canonical spec from existing agent or skill |
181
184
  | `/omcustom:audit-agents` | Audit agent dependencies |
182
185
  | `/omcustom:update-docs` | Sync project structure and documentation |
183
186
  | `/omcustom:sauron-watch` | Full structural verification (5+3 rounds) |
187
+ | `/omcustom-feedback` | Submit feedback as GitHub issue |
188
+
189
+ ### Web UI
190
+
191
+ | Command | What it does |
192
+ |---------|-------------|
193
+ | `/omcustom:web` | Control built-in Web UI (start, stop, status, open) |
184
194
 
185
195
  ### Package & Release
186
196
 
@@ -189,7 +199,7 @@ All commands are invoked inside the Claude Code conversation.
189
199
  | `/omcustom:npm-publish` | Publish to npm |
190
200
  | `/omcustom:npm-version` | Semantic versioning |
191
201
  | `/omcustom:npm-audit` | Dependency security audit |
192
- | `/omcustom:release-notes` | Generate release notes from git history |
202
+ | `/omcustom-release-notes` | Generate release notes from git history |
193
203
 
194
204
  ### Memory & System
195
205
 
@@ -198,6 +208,7 @@ All commands are invoked inside the Claude Code conversation.
198
208
  | `/memory-save` | Save session context |
199
209
  | `/memory-recall` | Search and recall memories |
200
210
  | `/omcustom:monitoring-setup` | OTel monitoring toggle |
211
+ | `/omcustom:loop` | Auto-continue background agent workflows (3-continue safety limit) |
201
212
  | `/omcustom:lists` | Show all commands |
202
213
  | `/omcustom:status` | System health check |
203
214
 
@@ -230,7 +241,7 @@ oh-my-customcode includes security and lifecycle hooks:
230
241
  | secret-filter | Bash, Read output | Detects AWS keys, API tokens, private keys, bearer tokens |
231
242
  | audit-log | Edit, Write, Bash, Agent | Append-only JSONL at `~/.claude/audit.jsonl` |
232
243
  | schema-validator | Write, Edit, Bash input | Validates tool inputs, flags dangerous patterns |
233
- | PostCompact | Context compaction | Reinjects enforced rules (R007–R018) — prevents rule amnesia |
244
+ | PostCompact | Context compaction | Reinjects enforced rules (R007–R018, R021) — prevents rule amnesia |
234
245
 
235
246
  Security hooks are advisory (exit 0). They warn but never block.
236
247
 
@@ -246,6 +257,10 @@ omcustom list # List components
246
257
  omcustom doctor # Verify installation
247
258
  omcustom doctor --fix # Auto-fix issues
248
259
  omcustom security # Scan for security issues
260
+ omcustom projects # List managed projects with version status
261
+ omcustom update --all # Batch update all outdated projects
262
+ omcustom serve # Start built-in Web UI
263
+ omcustom serve-stop # Stop Web UI
249
264
  ```
250
265
 
251
266
  ---
@@ -257,7 +272,7 @@ your-project/
257
272
  ├── CLAUDE.md # Entry point
258
273
  ├── .claude/
259
274
  │ ├── agents/ # 45 agent definitions
260
- │ ├── skills/ # 78 skill modules
275
+ │ ├── skills/ # 83 skill modules
261
276
  │ ├── rules/ # 21 governance rules (R000-R021)
262
277
  │ ├── hooks/ # 15 lifecycle hook scripts
263
278
  │ ├── schemas/ # Tool input validation schemas
package/dist/cli/index.js CHANGED
@@ -9323,7 +9323,7 @@ var init_package = __esm(() => {
9323
9323
  package_default = {
9324
9324
  name: "oh-my-customcode",
9325
9325
  workspaces: ["packages/*"],
9326
- version: "0.45.3",
9326
+ version: "0.47.0",
9327
9327
  description: "Batteries-included agent harness for Claude Code",
9328
9328
  type: "module",
9329
9329
  bin: {
@@ -9347,15 +9347,15 @@ var init_package = __esm(() => {
9347
9347
  },
9348
9348
  scripts: {
9349
9349
  dev: "bun run src/cli/index.ts",
9350
- build: "bun build src/cli/index.ts --outdir dist/cli --target node && bun build src/index.ts --outdir dist --target node",
9350
+ build: "bun build src/cli/index.ts --outdir dist/cli --target node && bun build src/index.ts --outdir dist --target node && bun run scripts/sync-source-lockfile.ts",
9351
9351
  test: "bun test",
9352
9352
  "test:unit": "bun test tests/unit",
9353
9353
  "test:integration": "bun test tests/integration",
9354
9354
  "test:e2e": "bun test tests/e2e",
9355
9355
  "test:coverage": "bun test --coverage",
9356
- lint: "biome check .",
9357
- "lint:fix": "biome check --write .",
9358
- format: "biome format --write .",
9356
+ lint: "biome check src/ tests/ scripts/",
9357
+ "lint:fix": "biome check --write src/ tests/ scripts/",
9358
+ format: "biome format --write src/ tests/ scripts/",
9359
9359
  typecheck: "tsc --noEmit",
9360
9360
  "docs:dev": "vitepress dev docs",
9361
9361
  "docs:build": "vitepress build docs",
@@ -9419,7 +9419,7 @@ __export(exports_projects, {
9419
9419
  default: () => projects_default
9420
9420
  });
9421
9421
  import { homedir as homedir2 } from "node:os";
9422
- import { basename as basename3, join as join9 } from "node:path";
9422
+ import { basename as basename3, dirname as dirname3, join as join9 } from "node:path";
9423
9423
  async function readLockFile(projectDir) {
9424
9424
  const lockFilePath = join9(projectDir, ".omcustom.lock.json");
9425
9425
  try {
@@ -9518,6 +9518,14 @@ async function findProjects(options = {}) {
9518
9518
  for (const dir2 of DEFAULT_SEARCH_DIRS) {
9519
9519
  searchPaths.push(join9(home, dir2));
9520
9520
  }
9521
+ if (!options.paths) {
9522
+ const cwd = process.cwd();
9523
+ if (!searchPaths.includes(cwd))
9524
+ searchPaths.push(cwd);
9525
+ const parent = dirname3(cwd);
9526
+ if (parent !== cwd && !searchPaths.includes(parent))
9527
+ searchPaths.push(parent);
9528
+ }
9521
9529
  if (options.paths) {
9522
9530
  searchPaths.push(...options.paths);
9523
9531
  }
@@ -9546,7 +9554,7 @@ function formatProjectsTable(projects, currentVersion) {
9546
9554
  if (projects.length === 0) {
9547
9555
  console.log(`
9548
9556
  oh-my-customcode가 적용된 프로젝트를 찾을 수 없습니다.`);
9549
- console.log(` 검색 경로: ~/workspace, ~/projects, ~/dev, ~/src, ~/code
9557
+ console.log(` 검색 경로: ~/workspace, ~/projects, ~/dev, ~/src, ~/code, ~/repos, ~/work, (현재 디렉토리 및 부모)
9550
9558
  `);
9551
9559
  return;
9552
9560
  }
@@ -24896,6 +24904,37 @@ var en_default = {
24896
24904
  }
24897
24905
  }
24898
24906
  },
24907
+ web: {
24908
+ description: "Manage the Web UI server (start, stop, status, open)",
24909
+ start: {
24910
+ description: "Start the Web UI server",
24911
+ portOption: "Port number",
24912
+ openOption: "Open browser automatically after start",
24913
+ foregroundOption: "Run in foreground (not detached)",
24914
+ started: "Web UI started: http://localhost:{{port}}",
24915
+ failed: "Failed to start Web UI server"
24916
+ },
24917
+ stop: {
24918
+ description: "Stop the Web UI server",
24919
+ stopped: "Web UI server stopped",
24920
+ notRunning: "Web UI server is not running"
24921
+ },
24922
+ status: {
24923
+ description: "Show Web UI server status",
24924
+ running: "Web UI is running: http://localhost:{{port}}",
24925
+ notRunning: "Web UI is not running",
24926
+ startHint: " Start with: omcustom web start"
24927
+ },
24928
+ open: {
24929
+ description: "Open the Web UI in the default browser",
24930
+ portOption: "Port number",
24931
+ notRunningWarn: "Web UI does not appear to be running. Start it with: omcustom web start"
24932
+ },
24933
+ deprecated: {
24934
+ serve: "[Deprecated] `omcustom serve` is deprecated. Use `omcustom web start` instead.",
24935
+ serveStop: "[Deprecated] `omcustom serve-stop` is deprecated. Use `omcustom web stop` instead."
24936
+ }
24937
+ },
24899
24938
  security: {
24900
24939
  description: "Scan for security issues in hooks, configs, and templates",
24901
24940
  verboseOption: "Show detailed scan results",
@@ -25247,6 +25286,37 @@ var ko_default = {
25247
25286
  }
25248
25287
  }
25249
25288
  },
25289
+ web: {
25290
+ description: "Web UI 서버 관리 (시작, 중지, 상태, 열기)",
25291
+ start: {
25292
+ description: "Web UI 서버 시작",
25293
+ portOption: "포트 번호",
25294
+ openOption: "시작 후 브라우저 자동 열기",
25295
+ foregroundOption: "포그라운드 실행 (백그라운드 분리 안 함)",
25296
+ started: "Web UI 시작됨: http://localhost:{{port}}",
25297
+ failed: "Web UI 서버 시작 실패"
25298
+ },
25299
+ stop: {
25300
+ description: "Web UI 서버 중지",
25301
+ stopped: "Web UI 서버가 중지되었습니다",
25302
+ notRunning: "Web UI 서버가 실행 중이 아닙니다"
25303
+ },
25304
+ status: {
25305
+ description: "Web UI 서버 상태 표시",
25306
+ running: "Web UI 실행 중: http://localhost:{{port}}",
25307
+ notRunning: "Web UI가 실행 중이 아닙니다",
25308
+ startHint: " 시작하려면: omcustom web start"
25309
+ },
25310
+ open: {
25311
+ description: "기본 브라우저에서 Web UI 열기",
25312
+ portOption: "포트 번호",
25313
+ notRunningWarn: "Web UI가 실행 중이지 않은 것 같습니다. 먼저 시작하세요: omcustom web start"
25314
+ },
25315
+ deprecated: {
25316
+ serve: "[Deprecated] `omcustom serve` is deprecated. Use `omcustom web start` instead.",
25317
+ serveStop: "[Deprecated] `omcustom serve-stop` is deprecated. Use `omcustom web stop` instead."
25318
+ }
25319
+ },
25250
25320
  security: {
25251
25321
  description: "훅, 설정, 템플릿의 보안 문제 검사",
25252
25322
  verboseOption: "상세 검사 결과 표시",
@@ -27738,13 +27808,15 @@ import { readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/
27738
27808
  import { join as join10 } from "node:path";
27739
27809
  var DEFAULT_PORT = 4321;
27740
27810
  var PID_FILE = join10(process.env.HOME ?? "~", ".omcustom-serve.pid");
27741
- function findServeBuildDir(projectRoot) {
27811
+ function findServeBuildDir(projectRoot, options) {
27742
27812
  const localBuild = join10(projectRoot, "packages", "serve", "build");
27743
27813
  if (existsSync2(join10(localBuild, "index.js")))
27744
27814
  return localBuild;
27745
- const npmBuild = join10(import.meta.dirname, "..", "..", "packages", "serve", "build");
27746
- if (existsSync2(join10(npmBuild, "index.js")))
27747
- return npmBuild;
27815
+ if (options?.skipNpmFallback !== true) {
27816
+ const npmBuild = join10(import.meta.dirname, "..", "..", "packages", "serve", "build");
27817
+ if (existsSync2(join10(npmBuild, "index.js")))
27818
+ return npmBuild;
27819
+ }
27748
27820
  return null;
27749
27821
  }
27750
27822
  async function isServeRunning() {
@@ -27762,11 +27834,11 @@ async function isServeRunning() {
27762
27834
  return false;
27763
27835
  }
27764
27836
  }
27765
- async function startServeBackground(projectRoot, port = DEFAULT_PORT) {
27837
+ async function startServeBackground(projectRoot, port = DEFAULT_PORT, buildDirOpts) {
27766
27838
  if (await isServeRunning()) {
27767
27839
  return;
27768
27840
  }
27769
- const buildDir = findServeBuildDir(projectRoot);
27841
+ const buildDir = findServeBuildDir(projectRoot, buildDirOpts);
27770
27842
  if (buildDir === null) {
27771
27843
  return;
27772
27844
  }
@@ -27776,7 +27848,7 @@ async function startServeBackground(projectRoot, port = DEFAULT_PORT) {
27776
27848
  OMCUSTOM_PORT: String(port),
27777
27849
  OMCUSTOM_HOST: "localhost",
27778
27850
  OMCUSTOM_ORIGIN: `http://localhost:${port}`,
27779
- OMCUSTOM_PROJECT_ROOT: projectRoot
27851
+ OMX_PROJECT_ROOT: projectRoot
27780
27852
  },
27781
27853
  stdio: "ignore",
27782
27854
  detached: true
@@ -28926,7 +28998,7 @@ async function initCommand(options) {
28926
28998
  }
28927
28999
 
28928
29000
  // src/cli/list.ts
28929
- import { basename as basename4, dirname as dirname3, join as join12, relative as relative3 } from "node:path";
29001
+ import { basename as basename4, dirname as dirname4, join as join12, relative as relative3 } from "node:path";
28930
29002
  init_fs();
28931
29003
  var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
28932
29004
  function parseKeyValue(line) {
@@ -29127,7 +29199,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
29127
29199
  const customSkillPaths = new Set(customComponents.filter((c) => c.type === "skill").map((c) => c.path));
29128
29200
  const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
29129
29201
  const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
29130
- const skillDir = dirname3(skillMdPath);
29202
+ const skillDir = dirname4(skillMdPath);
29131
29203
  const indexYamlPath = join12(skillDir, "index.yaml");
29132
29204
  const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
29133
29205
  const relativePath = relative3(targetDir, skillDir);
@@ -29664,51 +29736,54 @@ async function serveCommand(options) {
29664
29736
  console.error(`Invalid port: ${options.port}`);
29665
29737
  process.exit(1);
29666
29738
  }
29667
- const cwd = process.cwd();
29739
+ const cwd = options._projectRoot ?? process.cwd();
29740
+ const buildDirOpts = {
29741
+ skipNpmFallback: options._projectRoot !== undefined
29742
+ };
29668
29743
  if (options.foreground === true) {
29669
- runForeground(cwd, port);
29744
+ runForeground(cwd, port, buildDirOpts);
29670
29745
  return;
29671
29746
  }
29672
- await startServeBackground(cwd, port);
29747
+ await startServeBackground(cwd, port, buildDirOpts);
29673
29748
  const running = await isServeRunning();
29674
29749
  if (running) {
29675
- console.log(`Web UI started: http://127.0.0.1:${port}`);
29750
+ console.log(i18n.t("cli.web.start.started", { port }));
29676
29751
  if (options.open === true) {
29677
29752
  openBrowser(port);
29678
29753
  }
29679
29754
  } else {
29680
- console.error("Failed to start Web UI server");
29755
+ console.error(i18n.t("cli.web.start.failed"));
29681
29756
  process.exit(1);
29682
29757
  }
29683
29758
  }
29684
29759
  async function serveStopCommand() {
29685
29760
  const stopped = await stopServe();
29686
29761
  if (stopped) {
29687
- console.log("Web UI server stopped");
29762
+ console.log(i18n.t("cli.web.stop.stopped"));
29688
29763
  } else {
29689
- console.log("Web UI server is not running");
29764
+ console.log(i18n.t("cli.web.stop.notRunning"));
29690
29765
  }
29691
29766
  }
29692
- function runForeground(projectRoot, port) {
29693
- const buildDir = findServeBuildDir(projectRoot);
29767
+ function runForeground(projectRoot, port, buildDirOpts) {
29768
+ const buildDir = findServeBuildDir(projectRoot, buildDirOpts);
29694
29769
  if (buildDir === null) {
29695
29770
  console.error("Web UI build not found. Run: cd packages/serve && bun run build");
29696
29771
  process.exit(1);
29697
29772
  }
29698
- console.log(`Web UI: http://127.0.0.1:${port}`);
29773
+ console.log(`Web UI: http://localhost:${port}`);
29699
29774
  spawnSync2("node", [join13(buildDir, "index.js")], {
29700
29775
  env: {
29701
29776
  ...process.env,
29702
29777
  OMCUSTOM_PORT: String(port),
29703
29778
  OMCUSTOM_HOST: "localhost",
29704
29779
  OMCUSTOM_ORIGIN: `http://localhost:${port}`,
29705
- OMCUSTOM_PROJECT_ROOT: projectRoot
29780
+ OMX_PROJECT_ROOT: projectRoot
29706
29781
  },
29707
29782
  stdio: "inherit"
29708
29783
  });
29709
29784
  }
29710
29785
  function openBrowser(port) {
29711
- const url = `http://127.0.0.1:${port}`;
29786
+ const url = `http://localhost:${port}`;
29712
29787
  const platform = process.platform;
29713
29788
  if (platform === "darwin") {
29714
29789
  execFile("open", [url], () => {});
@@ -30493,6 +30568,36 @@ function printUpdateResults(result) {
30493
30568
  }
30494
30569
  }
30495
30570
 
30571
+ // src/cli/web-commands.ts
30572
+ async function webStartCommand(options) {
30573
+ await serveCommand(options);
30574
+ }
30575
+ async function webStopCommand() {
30576
+ await serveStopCommand();
30577
+ }
30578
+ async function webStatusCommand() {
30579
+ const running = await isServeRunning();
30580
+ if (running) {
30581
+ const port = process.env.OMCUSTOM_PORT ?? String(DEFAULT_PORT);
30582
+ console.log(i18n.t("cli.web.status.running", { port }));
30583
+ } else {
30584
+ console.log(i18n.t("cli.web.status.notRunning"));
30585
+ console.log(i18n.t("cli.web.status.startHint"));
30586
+ }
30587
+ }
30588
+ async function webOpenCommand(options) {
30589
+ const port = options.port !== undefined ? Number(options.port) : DEFAULT_PORT;
30590
+ if (!Number.isFinite(port) || port < 1 || port > 65535) {
30591
+ console.error(`Invalid port: ${options.port}`);
30592
+ process.exit(1);
30593
+ }
30594
+ const running = await isServeRunning();
30595
+ if (!running) {
30596
+ console.warn(i18n.t("cli.web.open.notRunningWarn"));
30597
+ }
30598
+ openBrowser(port);
30599
+ }
30600
+
30496
30601
  // src/cli/index.ts
30497
30602
  var require2 = createRequire2(import.meta.url);
30498
30603
  var packageJson = require2("../../package.json");
@@ -30518,10 +30623,28 @@ function createProgram() {
30518
30623
  const result = await securityCommand(options);
30519
30624
  process.exitCode = result.success ? 0 : 1;
30520
30625
  });
30521
- program2.command("serve").description("Start the web UI server").option("-p, --port <port>", "Port number", "4321").option("--open", "Open browser automatically").option("--foreground", "Run in foreground (not detached)").action(async (options) => {
30626
+ const web = program2.command("web").description(i18n.t("cli.web.description"));
30627
+ web.command("start").description(i18n.t("cli.web.start.description")).option("-p, --port <port>", i18n.t("cli.web.start.portOption"), "4321").option("--open", i18n.t("cli.web.start.openOption")).option("--foreground", i18n.t("cli.web.start.foregroundOption")).action(async (options) => {
30628
+ await webStartCommand(options);
30629
+ });
30630
+ web.command("stop").description(i18n.t("cli.web.stop.description")).action(async () => {
30631
+ await webStopCommand();
30632
+ });
30633
+ web.command("status").description(i18n.t("cli.web.status.description")).action(async () => {
30634
+ await webStatusCommand();
30635
+ });
30636
+ web.command("open").description(i18n.t("cli.web.open.description")).option("-p, --port <port>", i18n.t("cli.web.open.portOption"), "4321").action(async (options) => {
30637
+ await webOpenCommand(options);
30638
+ });
30639
+ web.action(async () => {
30640
+ await webStatusCommand();
30641
+ });
30642
+ program2.command("serve").description("(Deprecated) Start the Web UI server — use `omcustom web start` instead").option("-p, --port <port>", i18n.t("cli.web.start.portOption"), "4321").option("--open", i18n.t("cli.web.start.openOption")).option("--foreground", i18n.t("cli.web.start.foregroundOption")).action(async (options) => {
30643
+ console.warn(i18n.t("cli.web.deprecated.serve"));
30522
30644
  await serveCommand(options);
30523
30645
  });
30524
- program2.command("serve-stop").description("Stop the web UI server").action(async () => {
30646
+ program2.command("serve-stop").description("(Deprecated) Stop the Web UI server — use `omcustom web stop` instead").action(async () => {
30647
+ console.warn(i18n.t("cli.web.deprecated.serveStop"));
30525
30648
  await serveStopCommand();
30526
30649
  });
30527
30650
  program2.command("projects").description("List all projects on this machine where oh-my-customcode is installed").option("-f, --format <format>", "Output format: table, json, or simple", "table").option("--path <dir>", "Additional search directory (can be specified multiple times)", (val, prev) => [...prev, val], []).action(async (options) => {
@@ -30530,7 +30653,14 @@ function createProgram() {
30530
30653
  program2.hook("preAction", async (thisCommand, actionCommand) => {
30531
30654
  const opts = thisCommand.optsWithGlobals();
30532
30655
  const skipCheck = opts.skipVersionCheck || false;
30533
- if (actionCommand.name() === "init") {
30656
+ const cmdName = actionCommand.name();
30657
+ const parentName = actionCommand.parent?.name();
30658
+ const isServeCmd = cmdName === "serve" || cmdName === "serve-stop";
30659
+ const isWebCmd = cmdName === "web" || parentName === "web";
30660
+ if (isServeCmd || isWebCmd) {
30661
+ return;
30662
+ }
30663
+ if (cmdName === "init") {
30534
30664
  await maybeHandleSelfUpdateForInit({
30535
30665
  currentVersion: packageJson.version,
30536
30666
  skip: skipCheck
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oh-my-customcode",
3
3
  "workspaces": ["packages/*"],
4
- "version": "0.45.3",
4
+ "version": "0.47.0",
5
5
  "description": "Batteries-included agent harness for Claude Code",
6
6
  "type": "module",
7
7
  "bin": {
@@ -25,15 +25,15 @@
25
25
  },
26
26
  "scripts": {
27
27
  "dev": "bun run src/cli/index.ts",
28
- "build": "bun build src/cli/index.ts --outdir dist/cli --target node && bun build src/index.ts --outdir dist --target node",
28
+ "build": "bun build src/cli/index.ts --outdir dist/cli --target node && bun build src/index.ts --outdir dist --target node && bun run scripts/sync-source-lockfile.ts",
29
29
  "test": "bun test",
30
30
  "test:unit": "bun test tests/unit",
31
31
  "test:integration": "bun test tests/integration",
32
32
  "test:e2e": "bun test tests/e2e",
33
33
  "test:coverage": "bun test --coverage",
34
- "lint": "biome check .",
35
- "lint:fix": "biome check --write .",
36
- "format": "biome format --write .",
34
+ "lint": "biome check src/ tests/ scripts/",
35
+ "lint:fix": "biome check --write src/ tests/ scripts/",
36
+ "format": "biome format --write src/ tests/ scripts/",
37
37
  "typecheck": "tsc --noEmit",
38
38
  "docs:dev": "vitepress dev docs",
39
39
  "docs:build": "vitepress build docs",
@@ -136,9 +136,17 @@
136
136
  {
137
137
  "type": "command",
138
138
  "command": "bash .claude/hooks/scripts/task-outcome-recorder.sh"
139
+ },
140
+ {
141
+ "type": "command",
142
+ "command": "count_file=\"/tmp/.claude-loop-count-$PPID\"; if [ -f \"$count_file\" ]; then last_mod=$(stat -f%m \"$count_file\" 2>/dev/null || echo 0); now=$(date +%s); if [ $((now - last_mod)) -gt 60 ]; then echo 0 > \"$count_file\"; fi; fi; count=$(cat \"$count_file\" 2>/dev/null || echo 0); count=$((count + 1)); echo \"$count\" > \"$count_file\"; if [ \"$count\" -ge 4 ]; then echo '[AutoContinue] SAFETY: auto-continue limit (3) reached. Pausing.' >&2; fi; cat"
143
+ },
144
+ {
145
+ "type": "prompt",
146
+ "prompt": "A background subagent just completed. Check if there are pending workflow steps that depend on this result. If the previous subagent FAILED, do NOT auto-continue — report the failure and wait for user input. If the previous step succeeded and there are pending steps, proceed automatically. If no pending steps, report results and wait. Safety: The file /tmp/.claude-loop-count-$PPID tracks auto-continue count. After 3 consecutive auto-continues without user interaction, pause and ask the user before proceeding."
139
147
  }
140
148
  ],
141
- "description": "Record agent outcomes on subagent completion (complements PostToolUse Agent matcher)"
149
+ "description": "Record agent outcomes + auto-continue workflow on subagent completion"
142
150
  }
143
151
  ],
144
152
  "PostCompact": [
@@ -167,8 +167,11 @@ context: fork # Forked context for isolated execution
167
167
  version: 1.0.0 # Semantic version
168
168
  user-invocable: false # Whether user can invoke directly
169
169
  disable-model-invocation: true # Prevent model from auto-invoking
170
+ effort: medium # low | medium | high — overrides model effort level when invoked
170
171
  ```
171
172
 
173
+ When both an agent and its invoked skill specify `effort`, the skill's value takes precedence (more specific invocation-time setting).
174
+
172
175
  ### Skill Effectiveness Tracking
173
176
 
174
177
  Skills can optionally track effectiveness metrics via auto-populated fields:
@@ -41,10 +41,10 @@ Implemented in `.claude/hooks/hooks.json` (PreToolUse → Agent/Task matcher).
41
41
  ### Format
42
42
 
43
43
  ```
44
- {Cost} | {project} | {branch} | CTX:{usage}%
44
+ {Cost} | {project} | {branch} | RL:{rate_limit}% | CTX:{usage}%
45
45
  ```
46
46
 
47
- Example: `$0.05 | my-project | develop | CTX:42%`
47
+ Example: `$0.05 | my-project | develop | RL:45% | CTX:42%`
48
48
 
49
49
  ### Configuration
50
50
 
@@ -58,7 +58,7 @@ Example: `$0.05 | my-project | develop | CTX:42%`
58
58
  }
59
59
  ```
60
60
 
61
- Set in `.claude/settings.local.json`. The command receives JSON via stdin with model, workspace, context window, and cost data.
61
+ Set in `.claude/settings.local.json`. The command receives JSON via stdin with model, workspace, context window, cost, and rate limit data.
62
62
 
63
63
  ### Color Coding
64
64
 
@@ -67,10 +67,15 @@ Set in `.claude/settings.local.json`. The command receives JSON via stdin with m
67
67
  | Cost | < $1.00 | Green |
68
68
  | Cost | $1.00 - $4.99 | Yellow |
69
69
  | Cost | >= $5.00 | Red |
70
+ | Rate Limit | < 50% | Green |
71
+ | Rate Limit | 50-79% | Yellow |
72
+ | Rate Limit | >= 80% | Red |
70
73
  | Context | < 60% | Green |
71
74
  | Context | 60-79% | Yellow |
72
75
  | Context | >= 80% | Red |
73
76
 
77
+ The `RL:{rate_limit}%` segment only appears when Claude Code v2.1.80+ provides `rate_limits` data. On older versions, this segment is omitted.
78
+
74
79
  ## Integration
75
80
 
76
81
  Integrates with R007 (Agent ID), R008 (Tool ID), R009 (Parallel).
@@ -205,8 +205,8 @@ When the `--spec` flag is present, refactoring is guided by the target's canonic
205
205
 
206
206
  ### Workflow
207
207
 
208
- 1. **Load spec**: Read `.claude/specs/<agent-name>.spec.md` (generated by `/omcustom:takeover`)
209
- - If spec doesn't exist, run takeover first: `/omcustom:takeover <name>`
208
+ 1. **Load spec**: Read `.claude/specs/<agent-name>.spec.md` (generated by `/omcustom-takeover`)
209
+ - If spec doesn't exist, run takeover first: `/omcustom-takeover <name>`
210
210
  2. **Extract invariants**: Parse the spec's `## Invariants` section as pre-flight guard constraints
211
211
  3. **Refactor**: Perform normal refactoring (per existing workflow)
212
212
  4. **Verify invariants**: After refactoring, check each invariant still holds:
@@ -216,7 +216,7 @@ When the `--spec` flag is present, refactoring is guided by the target's canonic
216
216
  ├── ✓ Invariant 2: {description} — PASS
217
217
  └── ✗ Invariant 3: {description} — FAIL (reason)
218
218
  ```
219
- 5. **Regenerate spec**: If refactoring changed the contract, run `/omcustom:takeover <name>` to update
219
+ 5. **Regenerate spec**: If refactoring changed the contract, run `/omcustom-takeover <name>` to update
220
220
 
221
221
  ### When to Use
222
222
 
@@ -1,53 +1,82 @@
1
1
  ---
2
2
  name: omcustom-feedback
3
- description: Submit feedback about oh-my-customcode as a GitHub issue
3
+ description: Submit feedback about oh-my-customcode (supports anonymous submission)
4
4
  scope: harness
5
5
  user-invocable: true
6
6
  disable-model-invocation: true
7
- argument-hint: "[description or leave empty for interactive]"
7
+ argument-hint: "[description or leave empty for interactive] [--anonymous]"
8
8
  ---
9
9
 
10
10
  # Feedback Submitter
11
11
 
12
- Submit feedback about oh-my-customcode (bugs, features, improvements, questions) directly as a GitHub issue from the CLI session.
12
+ Submit feedback about oh-my-customcode (bugs, features, improvements, questions) directly from the CLI session. Supports anonymous submission via Airflow DAG when gh CLI is unavailable or when anonymity is requested.
13
13
 
14
14
  ## Purpose
15
15
 
16
- Lowers the barrier for submitting feedback by allowing users to create GitHub issues without leaving their terminal session. All feedback is filed to the `baekenough/oh-my-customcode` repository.
16
+ Lowers the barrier for submitting feedback by allowing users to create GitHub issues or submit anonymously — without leaving their terminal session. All feedback is filed to the `baekenough/oh-my-customcode` repository.
17
17
 
18
18
  ## Usage
19
19
 
20
20
  ```
21
21
  # Inline feedback
22
- /omcustom:feedback HUD display is missing during parallel agent spawn
22
+ /omcustom-feedback HUD display is missing during parallel agent spawn
23
+
24
+ # Anonymous submission
25
+ /omcustom:feedback --anonymous Something feels off with the routing
23
26
 
24
27
  # Interactive (no arguments)
25
- /omcustom:feedback
28
+ /omcustom-feedback
26
29
  ```
27
30
 
28
31
  ## Workflow
29
32
 
30
33
  ### Phase 1: Input Parsing
31
34
 
32
- If arguments are provided:
35
+ Check for `--anonymous` flag in the arguments:
36
+ - If `--anonymous` is present, set `ANONYMOUS=true` and strip the flag from the content
37
+ - Otherwise, set `ANONYMOUS=false`
38
+
39
+ If remaining arguments are provided:
33
40
  1. Analyze the content to auto-detect category (`bug`, `feature`, `improvement`, `question`)
34
41
  2. Use the content as the issue title (truncate to 80 chars if needed)
35
42
  3. Use the full content as the description body
36
43
 
37
- If no arguments:
44
+ If no arguments (or only `--anonymous`):
38
45
  1. Ask the user for category using AskUserQuestion: `[bug / feature / improvement / question]`
39
46
  2. Ask for title and optional detailed description (combine into a single prompt when possible)
40
47
 
41
- ### Phase 2: Preflight Check
48
+ ### Phase 2: Route Decision
42
49
 
43
- ```bash
44
- # Verify gh CLI is installed
45
- command -v gh >/dev/null 2>&1 || { echo "Error: gh CLI not installed. Install from https://cli.github.com/"; exit 1; }
50
+ Check environment and user intent:
46
51
 
47
- # Verify authentication
48
- gh auth status 2>&1 | grep -q "Logged in" || { echo "Error: Not authenticated. Run 'gh auth login' first."; exit 1; }
52
+ ```bash
53
+ # Check gh CLI availability
54
+ command -v gh >/dev/null 2>&1 && GH_AVAILABLE=true || GH_AVAILABLE=false
55
+
56
+ # Check gh authentication (only if gh is available)
57
+ if [ "$GH_AVAILABLE" = "true" ]; then
58
+ gh auth status >/dev/null 2>&1 && GH_AUTHED=true || GH_AUTHED=false
59
+ else
60
+ GH_AUTHED=false
61
+ fi
62
+
63
+ # Check curl availability
64
+ command -v curl >/dev/null 2>&1 && CURL_AVAILABLE=true || CURL_AVAILABLE=false
49
65
  ```
50
66
 
67
+ **Route A**: `gh` available + authenticated + NOT anonymous
68
+ - Use GitHub Issue creation (see Phase 4A)
69
+
70
+ **Route B**: anonymous OR not authenticated (gh available)
71
+ - Use `curl` to Airflow REST API (see Phase 4C)
72
+ - Phase 4B (workflow_dispatch) reserved for future use
73
+
74
+ **Route C**: `gh` NOT available (or Route B curl fallback)
75
+ - Use `curl` to Airflow REST API (see Phase 4C)
76
+
77
+ **Fallback**: Neither `gh` nor `curl` available
78
+ - Save feedback locally and inform the user (see Phase 4D)
79
+
51
80
  ### Phase 3: Environment Collection
52
81
 
53
82
  Collect environment info via Bash:
@@ -64,9 +93,16 @@ OS_INFO=$(uname -s 2>/dev/null || echo "unknown")
64
93
 
65
94
  # Project name
66
95
  PROJECT_NAME=$(basename "$(pwd)")
96
+
97
+ # Build project context string
98
+ PROJECT_CONTEXT="omcustom v${OMCUSTOM_VERSION}, Claude Code ${CLAUDE_VERSION}, ${OS_INFO}"
67
99
  ```
68
100
 
69
- ### Phase 4: Confirmation and Create
101
+ For anonymous submissions, do NOT include the project name. Offer to include project context as opt-in:
102
+ - Ask: "Include environment info (version, OS) in the anonymous report? [Y/n]"
103
+ - If declined, set `PROJECT_CONTEXT=""`
104
+
105
+ ### Phase 4A: GitHub Issue Creation (Route A — gh + authenticated + not anonymous)
70
106
 
71
107
  1. Show the user a preview of the issue to be created:
72
108
  ```
@@ -80,7 +116,7 @@ PROJECT_NAME=$(basename "$(pwd)")
80
116
 
81
117
  3. Ensure labels exist (defensive):
82
118
  ```bash
83
- gh label create feedback --description "User feedback via /omcustom:feedback" --color 0E8A16 --repo baekenough/oh-my-customcode 2>/dev/null || true
119
+ gh label create feedback --description "User feedback via /omcustom-feedback" --color 0E8A16 --repo baekenough/oh-my-customcode 2>/dev/null || true
84
120
  ```
85
121
 
86
122
  4. Create the issue using `--body-file` for safe markdown handling:
@@ -102,7 +138,7 @@ PROJECT_NAME=$(basename "$(pwd)")
102
138
  - Project: {project_name}
103
139
 
104
140
  ---
105
- *Submitted via `/omcustom:feedback`*
141
+ *Submitted via `/omcustom-feedback`*
106
142
  FEEDBACK_EOF
107
143
 
108
144
  # Create issue
@@ -120,18 +156,91 @@ PROJECT_NAME=$(basename "$(pwd)")
120
156
 
121
157
  6. Return the issue URL to the user
122
158
 
159
+ ### Phase 4B: Anonymous via GitHub Actions (Route B — FUTURE)
160
+
161
+ > **Note**: This route is reserved for future implementation. The `feedback-submission.yml` workflow does not yet exist. All anonymous/unauthenticated submissions currently fall through to Route C (Airflow REST API).
162
+
163
+ ```bash
164
+ gh workflow run feedback-submission.yml \
165
+ --repo baekenough/oh-my-customcode \
166
+ -f title="$TITLE" \
167
+ -f body="$BODY" \
168
+ -f feedback_type="$TYPE" \
169
+ -f anonymous=true \
170
+ -f project_context="$PROJECT_CONTEXT"
171
+ ```
172
+
173
+ On success, inform the user:
174
+ ```
175
+ [Done] Anonymous feedback submitted via GitHub Actions workflow.
176
+ ```
177
+
178
+ If `gh workflow run` fails (e.g., permissions), fall through to Route C.
179
+
180
+ ### Phase 4C: Anonymous via Airflow REST API (Route C — no gh)
181
+
182
+ ```bash
183
+ # Build JSON payload safely with jq
184
+ PAYLOAD=$(jq -n --arg title "$TITLE" --arg body "$BODY" --arg type "$TYPE" --arg ctx "$PROJECT_CONTEXT" \
185
+ '{"conf":{"title":$title,"body":$body,"feedback_type":$type,"anonymous":true,"submitter":"","project_context":$ctx}}')
186
+
187
+ curl -X POST "https://airflow.baekenough.com/api/v2/dags/omc_feedback_collector/dagRuns" \
188
+ -H "Content-Type: application/json" \
189
+ -d "$PAYLOAD" \
190
+ --silent --show-error \
191
+ --max-time 15
192
+ ```
193
+
194
+ On HTTP 200/201, inform the user:
195
+ ```
196
+ [Done] Anonymous feedback submitted via Airflow.
197
+ ```
198
+
199
+ On failure (non-2xx or network error), fall through to Fallback.
200
+
201
+ ### Phase 4D: Local Fallback (no gh, no curl, or all routes failed)
202
+
203
+ ```bash
204
+ mkdir -p ~/.omcustom/feedback
205
+ TIMESTAMP=$(date +%Y%m%dT%H%M%S)
206
+ FEEDBACK_FILE=~/.omcustom/feedback/${TIMESTAMP}.json
207
+
208
+ cat > "$FEEDBACK_FILE" << EOF
209
+ {
210
+ "title": "$TITLE",
211
+ "body": "$BODY",
212
+ "feedback_type": "$TYPE",
213
+ "anonymous": $ANONYMOUS,
214
+ "project_context": "$PROJECT_CONTEXT",
215
+ "saved_at": "$TIMESTAMP"
216
+ }
217
+ EOF
218
+ ```
219
+
220
+ Inform the user:
221
+ ```
222
+ [Saved] Feedback saved locally to ~/.omcustom/feedback/{timestamp}.json
223
+ Submit manually when connectivity is available:
224
+ - GitHub Issues: https://github.com/baekenough/oh-my-customcode/issues/new
225
+ - Or run /omcustom:feedback again when gh or curl is available
226
+ ```
227
+
123
228
  ### Category-to-Label Mapping
124
229
 
125
- | Category | GitHub Label |
126
- |----------|-------------|
127
- | bug | bug |
128
- | feature | enhancement |
129
- | improvement | enhancement |
130
- | question | question |
230
+ | Category | GitHub Label | Airflow feedback_type |
231
+ |----------|--------------|-----------------------|
232
+ | bug | bug | bug |
233
+ | feature | enhancement | feature |
234
+ | improvement | enhancement | improvement |
235
+ | question | question | question |
236
+ | (auto-detect fails) | (none) | general |
131
237
 
132
238
  ## Notes
133
239
 
134
- - Target repo is hardcoded to `baekenough/oh-my-customcode` feedback is always about omcustom itself
135
- - Requires `gh` CLI with authentication
136
- - Uses `--body-file` instead of `--body` to safely handle markdown with special characters
240
+ - Route A creates a visible GitHub issue attributed to the user's gh account
241
+ - Routes B and C submit anonymously — submitter identity is not recorded
242
+ - Route B requires `gh` but NOT authentication to the repo (public workflow)
243
+ - Route C requires `curl` (available on macOS/Linux by default)
244
+ - Fallback ensures no feedback is silently lost even in offline environments
137
245
  - `disable-model-invocation: true` ensures this skill only runs when explicitly invoked by the user
246
+ - Target repo is hardcoded to `baekenough/oh-my-customcode` — feedback is always about omcustom itself
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: omcustom-loop
3
+ description: Prevent session idle during background agent work via SubagentStop prompt hook auto-continuation
4
+ scope: core
5
+ version: 1.0.0
6
+ user-invocable: true
7
+ ---
8
+
9
+ # /omcustom:loop — Session Auto-Continuation
10
+
11
+ ## Overview
12
+
13
+ Prevents session idle when background subagents complete by using a `SubagentStop` prompt hook that nudges the orchestrator to check for pending workflow steps.
14
+
15
+ ## How It Works
16
+
17
+ 1. When a background subagent completes, Claude Code fires the `SubagentStop` event
18
+ 2. The prompt hook injects a message asking the orchestrator to check for pending steps
19
+ 3. If pending steps exist, the orchestrator proceeds automatically
20
+ 4. If no pending steps, it reports results and waits for user input
21
+
22
+ ## Safety Limits
23
+
24
+ - **3 consecutive auto-continues max**: After 3 automatic progressions without user interaction, the system pauses and asks the user before proceeding
25
+ - **stuck-detector integration**: If the same action repeats 3+ times, stuck-detector intervenes
26
+ - **cost-cap-advisor**: Cost monitoring continues during auto-continuation
27
+
28
+ ## Configuration
29
+
30
+ The hook is configured in `.claude/hooks/hooks.json` under `SubagentStop`. It works alongside the existing `task-outcome-recorder.sh` command hook.
31
+
32
+ ## Limitations
33
+
34
+ - **Platform constraint**: Claude Code's turn-based model means the prompt hook only fires when a subagent completes — it cannot wake the model from true idle state
35
+ - **Foreground agents preferred**: For guaranteed continuation, use foreground parallel agents (R009) instead of background agents
36
+ - **PoC status**: This is an experimental feature. If the prompt hook doesn't reliably trigger in all scenarios, fall back to foreground agent patterns
37
+
38
+ ## Usage
39
+
40
+ ```bash
41
+ /omcustom:loop # Show current auto-continuation status
42
+ /omcustom:loop status # Same as above
43
+ ```
44
+
45
+ The feature is active by default via hooks.json. No explicit activation needed.
@@ -17,8 +17,8 @@ Replaces the CI-based `release-notes.yml` workflow that previously used Claude A
17
17
  ## Usage
18
18
 
19
19
  ```
20
- /omcustom:release-notes 0.36.0
21
- /omcustom:release-notes 0.36.0 --previous-tag v0.35.3
20
+ /omcustom-release-notes 0.36.0
21
+ /omcustom-release-notes 0.36.0 --previous-tag v0.35.3
22
22
  ```
23
23
 
24
24
  ## Workflow
@@ -105,7 +105,7 @@ This skill is designed to be used during the release process:
105
105
 
106
106
  ```
107
107
  /omcustom:npm-version patch|minor|major -> version bump
108
- /omcustom:release-notes {version} -> generate notes
108
+ /omcustom-release-notes {version} -> generate notes
109
109
  mgr-gitnerd: gh release create -> create release with notes
110
110
  ```
111
111
 
@@ -17,8 +17,8 @@ When an agent or skill has evolved organically without a formal spec, `takeover`
17
17
  ## Usage
18
18
 
19
19
  ```
20
- /omcustom:takeover <agent-name>
21
- /omcustom:takeover <skill-name>
20
+ /omcustom-takeover <agent-name>
21
+ /omcustom-takeover <skill-name>
22
22
  ```
23
23
 
24
24
  ## Workflow
@@ -110,6 +110,6 @@ generated: <ISO-8601 timestamp>
110
110
  ## Notes
111
111
 
112
112
  - Specs are git-untracked (under `.claude/`)
113
- - Regenerate anytime with `/omcustom:takeover <name>`
113
+ - Regenerate anytime with `/omcustom-takeover <name>`
114
114
  - Used by `/dev-refactor --spec` for invariant-preserving refactoring
115
115
  - Advisory output — human review recommended before using as contract
@@ -11,7 +11,11 @@
11
11
  # "model": { "display_name": "claude-opus-4-6" },
12
12
  # "workspace": { "current_dir": "/path/to/project" },
13
13
  # "context_window": { "used_percentage": 42, "context_window_size": 200000 },
14
- # "cost": { "total_cost_usd": 0.05 }
14
+ # "cost": { "total_cost_usd": 0.05 },
15
+ # "rate_limits": { (v2.1.80+, optional)
16
+ # "five_hour": { "used_percentage": 10, "resets_at": 1773979200 },
17
+ # "seven_day": { "used_percentage": 90, "resets_at": 1773979200 }
18
+ # }
15
19
  # }
16
20
 
17
21
  # ---------------------------------------------------------------------------
@@ -27,13 +31,13 @@ if [[ -n "${NO_COLOR}" || "${TERM}" == "dumb" ]]; then
27
31
  COLOR_CTX_WARN=""
28
32
  COLOR_CTX_CRIT=""
29
33
  else
30
- COLOR_RESET="\033[0m"
31
- COLOR_OPUS="\033[1;35m" # Magenta bold
32
- COLOR_SONNET="\033[0;36m" # Cyan
33
- COLOR_HAIKU="\033[0;32m" # Green
34
- COLOR_CTX_OK="\033[0;32m" # Green (< 60%)
35
- COLOR_CTX_WARN="\033[0;33m" # Yellow (60-79%)
36
- COLOR_CTX_CRIT="\033[0;31m" # Red (>= 80%)
34
+ COLOR_RESET=$'\033[0m'
35
+ COLOR_OPUS=$'\033[1;35m' # Magenta bold
36
+ COLOR_SONNET=$'\033[0;36m' # Cyan
37
+ COLOR_HAIKU=$'\033[0;32m' # Green
38
+ COLOR_CTX_OK=$'\033[0;32m' # Green (< 60%)
39
+ COLOR_CTX_WARN=$'\033[0;33m' # Yellow (60-79%)
40
+ COLOR_CTX_CRIT=$'\033[0;31m' # Red (>= 80%)
37
41
  fi
38
42
 
39
43
  # ---------------------------------------------------------------------------
@@ -55,17 +59,23 @@ if [[ -z "$json" ]]; then
55
59
  exit 0
56
60
  fi
57
61
 
62
+ # Debug logging for CTX investigation
63
+ if [[ -n "${STATUSLINE_DEBUG}" ]]; then
64
+ printf '%s\n' "$json" >> "/tmp/.claude-statusline-debug-${PPID}.jsonl"
65
+ fi
66
+
58
67
  # ---------------------------------------------------------------------------
59
68
  # 4. Single jq call — extract all fields as TSV
60
- # Fields: model_name, project_dir, ctx_pct, ctx_size, cost_usd
69
+ # Fields: model_name, project_dir, ctx_pct, ctx_size, cost_usd, rl_5h_pct
61
70
  # ---------------------------------------------------------------------------
62
- IFS=$'\t' read -r model_name project_dir ctx_pct ctx_size cost_usd <<< "$(
71
+ IFS=$'\t' read -r model_name project_dir ctx_pct ctx_size cost_usd rl_5h_pct <<< "$(
63
72
  printf '%s' "$json" | jq -r '[
64
73
  (.model.display_name // "unknown"),
65
74
  (.workspace.current_dir // ""),
66
- (.context_window.used_percentage // 0),
75
+ (if .context_window.used != null and .context_window.total != null and .context_window.total > 0 then (.context_window.used / .context_window.total * 100) elif .context_window.used_percentage != null then .context_window.used_percentage else 0 end),
67
76
  (.context_window.context_window_size // 0),
68
- (.cost.total_cost_usd // 0)
77
+ (.cost.total_cost_usd // 0),
78
+ (.rate_limits.five_hour.used_percentage // -1)
69
79
  ] | @tsv'
70
80
  )"
71
81
 
@@ -73,7 +83,8 @@ IFS=$'\t' read -r model_name project_dir ctx_pct ctx_size cost_usd <<< "$(
73
83
  # 4b. Cost & context data bridge — write to temp file for hooks
74
84
  # ---------------------------------------------------------------------------
75
85
  COST_BRIDGE_FILE="/tmp/.claude-cost-${PPID}"
76
- printf '%s\t%s\t%s\n' "$cost_usd" "$ctx_pct" "$(date +%s)" > "$COST_BRIDGE_FILE" 2>/dev/null || true
86
+ _tmp="${COST_BRIDGE_FILE}.tmp.$$"
87
+ printf '%s\t%s\t%s\t%s\n' "$cost_usd" "$ctx_pct" "$(date +%s)" "$rl_5h_pct" > "$_tmp" 2>/dev/null && mv -f "$_tmp" "$COST_BRIDGE_FILE" 2>/dev/null || true
77
88
 
78
89
  # ---------------------------------------------------------------------------
79
90
  # 5. Model display name + color (bash 3.2 compatible case pattern matching)
@@ -236,6 +247,29 @@ fi
236
247
 
237
248
  ctx_display="CTX:${ctx_int}%"
238
249
 
250
+ # ---------------------------------------------------------------------------
251
+ # 9b. Rate limit percentage with color (v2.1.80+, optional)
252
+ # ---------------------------------------------------------------------------
253
+ rl_display=""
254
+ rl_color=""
255
+ # rl_5h_pct is -1 when rate_limits field is absent (pre-v2.1.80 compatibility)
256
+ rl_5h_int="${rl_5h_pct%%.*}"
257
+ # Ensure it's a valid integer (fallback to -1)
258
+ if ! [[ "$rl_5h_int" =~ ^-?[0-9]+$ ]]; then
259
+ rl_5h_int=-1
260
+ fi
261
+
262
+ if [[ "$rl_5h_int" -ge 0 ]]; then
263
+ rl_display="RL:${rl_5h_int}%"
264
+ if [[ "$rl_5h_int" -ge 80 ]]; then
265
+ rl_color="${COLOR_CTX_CRIT}" # Red (>= 80%)
266
+ elif [[ "$rl_5h_int" -ge 50 ]]; then
267
+ rl_color="${COLOR_CTX_WARN}" # Yellow (50-79%)
268
+ else
269
+ rl_color="${COLOR_CTX_OK}" # Green (< 50%)
270
+ fi
271
+ fi
272
+
239
273
  # ---------------------------------------------------------------------------
240
274
  # 10. Assemble and output the status line
241
275
  # ---------------------------------------------------------------------------
@@ -253,17 +287,25 @@ if [[ -n "$pr_display" ]]; then
253
287
  pr_segment=" | ${pr_display}"
254
288
  fi
255
289
 
290
+ # Build the RL segment (with separator) if present
291
+ rl_segment=""
292
+ if [[ -n "$rl_display" ]]; then
293
+ rl_segment=" | ${rl_color}${rl_display}${COLOR_RESET}"
294
+ fi
295
+
256
296
  if [[ -n "$git_branch" ]]; then
257
- printf "${cost_color}%s${COLOR_RESET} | %s | %s%s | ${ctx_color}%s${COLOR_RESET}\n" \
297
+ printf "${cost_color}%s${COLOR_RESET} | %s | %s%s%s | ${ctx_color}%s${COLOR_RESET}\n" \
258
298
  "$cost_display" \
259
299
  "$project_name" \
260
300
  "$branch_display" \
261
301
  "$pr_segment" \
302
+ "$rl_segment" \
262
303
  "$ctx_display"
263
304
  else
264
- printf "${cost_color}%s${COLOR_RESET} | %s%s | ${ctx_color}%s${COLOR_RESET}\n" \
305
+ printf "${cost_color}%s${COLOR_RESET} | %s%s%s | ${ctx_color}%s${COLOR_RESET}\n" \
265
306
  "$cost_display" \
266
307
  "$project_name" \
267
308
  "$pr_segment" \
309
+ "$rl_segment" \
268
310
  "$ctx_display"
269
311
  fi
@@ -101,7 +101,7 @@ oh-my-customcode로 구동됩니다.
101
101
  | `/omcustom:update-external` | 외부 소스에서 에이전트 업데이트 |
102
102
  | `/omcustom:audit-agents` | 에이전트 의존성 감사 |
103
103
  | `/omcustom:fix-refs` | 깨진 참조 수정 |
104
- | `/omcustom:takeover` | 기존 에이전트/스킬에서 canonical spec 추출 |
104
+ | `/omcustom-takeover` | 기존 에이전트/스킬에서 canonical spec 추출 |
105
105
  | `/adversarial-review` | 공격자 관점 보안 코드 리뷰 |
106
106
  | `/ambiguity-gate` | 요청 모호성 분석 및 명확화 질문 (ouroboros 패턴) |
107
107
  | `/dev-review` | 코드 베스트 프랙티스 리뷰 |
@@ -112,8 +112,8 @@ oh-my-customcode로 구동됩니다.
112
112
  | `/omcustom:npm-publish` | npm 레지스트리에 패키지 배포 |
113
113
  | `/omcustom:npm-version` | 시맨틱 버전 관리 |
114
114
  | `/omcustom:npm-audit` | 의존성 감사 |
115
- | `/omcustom:release-notes` | 릴리즈 노트 생성 (git 히스토리 기반) |
116
- | `/omcustom:feedback` | 사용자 피드백을 GitHub Issue로 등록 |
115
+ | `/omcustom-release-notes` | 릴리즈 노트 생성 (git 히스토리 기반) |
116
+ | `/omcustom-feedback` | 사용자 피드백을 GitHub Issue로 등록 |
117
117
  | `/codex-exec` | Codex CLI 프롬프트 실행 |
118
118
  | `/optimize-analyze` | 번들 및 성능 분석 |
119
119
  | `/optimize-bundle` | 번들 크기 최적화 |
@@ -122,6 +122,7 @@ oh-my-customcode로 구동됩니다.
122
122
  | `/deep-plan` | 연구 검증 기반 계획 수립 (research → plan → verify) |
123
123
  | `/omcustom:sauron-watch` | 전체 R017 검증 |
124
124
  | `/structured-dev-cycle` | 6단계 구조적 개발 사이클 (Plan → Verify → Implement → Verify → Compound → Done) |
125
+ | `/omcustom:loop` | 백그라운드 에이전트 자동 계속 실행 |
125
126
  | `/omcustom:lists` | 모든 사용 가능한 커맨드 표시 |
126
127
  | `/omcustom:status` | 시스템 상태 표시 |
127
128
  | `/omcustom:help` | 도움말 표시 |
@@ -132,12 +133,12 @@ oh-my-customcode로 구동됩니다.
132
133
  project/
133
134
  +-- CLAUDE.md # 진입점
134
135
  +-- .claude/
135
- | +-- agents/ # 서브에이전트 정의 (44 파일)
136
- | +-- skills/ # 스킬 (78 디렉토리)
136
+ | +-- agents/ # 서브에이전트 정의 (45 파일)
137
+ | +-- skills/ # 스킬 (83 디렉토리)
137
138
  | +-- rules/ # 전역 규칙 (R000-R021)
138
139
  | +-- hooks/ # 훅 스크립트 (보안, 검증, HUD)
139
140
  | +-- contexts/ # 컨텍스트 파일 (ecomode)
140
- +-- guides/ # 레퍼런스 문서 (27 토픽)
141
+ +-- guides/ # 레퍼런스 문서 (28 토픽)
141
142
  ```
142
143
 
143
144
  ## 오케스트레이션
@@ -185,14 +186,14 @@ oh-my-customcode는 소프트웨어 컴파일과 동일한 구조를 따릅니
185
186
  | SW Engineer/Frontend | 4 | fe-vercel-agent, fe-vuejs-agent, fe-svelte-agent, fe-flutter-agent |
186
187
  | SW Engineer/Tooling | 3 | tool-npm-expert, tool-optimizer, tool-bun-expert |
187
188
  | DE Engineer | 6 | de-airflow-expert, de-dbt-expert, de-spark-expert, de-kafka-expert, de-snowflake-expert, de-pipeline-expert |
188
- | SW Engineer/Database | 3 | db-supabase-expert, db-postgres-expert, db-redis-expert |
189
+ | SW Engineer/Database | 4 | db-supabase-expert, db-postgres-expert, db-redis-expert, db-alembic-expert |
189
190
  | Security | 1 | sec-codeql-expert |
190
191
  | SW Architect | 2 | arch-documenter, arch-speckit-agent |
191
192
  | Infra Engineer | 2 | infra-docker-expert, infra-aws-expert |
192
193
  | QA Team | 3 | qa-planner, qa-writer, qa-engineer |
193
194
  | Manager | 6 | mgr-creator, mgr-updater, mgr-supplier, mgr-gitnerd, mgr-sauron, mgr-claude-code-bible |
194
195
  | System | 2 | sys-memory-keeper, sys-naggy |
195
- | **총계** | **44** | |
196
+ | **총계** | **45** | |
196
197
 
197
198
  ## Agent Teams (MUST when enabled)
198
199
 
@@ -100,7 +100,7 @@ NO EXCEPTIONS. NO EXCUSES.
100
100
  | `/omcustom:update-external` | Update agents from external sources |
101
101
  | `/omcustom:audit-agents` | Audit agent dependencies |
102
102
  | `/omcustom:fix-refs` | Fix broken references |
103
- | `/omcustom:takeover` | Extract canonical spec from existing agent/skill |
103
+ | `/omcustom-takeover` | Extract canonical spec from existing agent/skill |
104
104
  | `/dev-review` | Review code for best practices |
105
105
  | `/dev-refactor` | Refactor code |
106
106
  | `/memory-save` | Save session context to claude-mem |
@@ -109,7 +109,7 @@ NO EXCEPTIONS. NO EXCUSES.
109
109
  | `/omcustom:npm-publish` | Publish package to npm registry |
110
110
  | `/omcustom:npm-version` | Manage semantic versions |
111
111
  | `/omcustom:npm-audit` | Audit dependencies |
112
- | `/omcustom:release-notes` | Generate release notes from git history |
112
+ | `/omcustom-release-notes` | Generate release notes from git history |
113
113
  | `/codex-exec` | Execute Codex CLI prompts |
114
114
  | `/optimize-analyze` | Analyze bundle and performance |
115
115
  | `/optimize-bundle` | Optimize bundle size |
@@ -100,7 +100,7 @@ oh-my-customcode로 구동됩니다.
100
100
  | `/omcustom:update-external` | 외부 소스에서 에이전트 업데이트 |
101
101
  | `/omcustom:audit-agents` | 에이전트 의존성 감사 |
102
102
  | `/omcustom:fix-refs` | 깨진 참조 수정 |
103
- | `/omcustom:takeover` | 기존 에이전트/스킬에서 canonical spec 추출 |
103
+ | `/omcustom-takeover` | 기존 에이전트/스킬에서 canonical spec 추출 |
104
104
  | `/dev-review` | 코드 베스트 프랙티스 리뷰 |
105
105
  | `/dev-refactor` | 코드 리팩토링 |
106
106
  | `/memory-save` | 세션 컨텍스트를 claude-mem에 저장 |
@@ -109,7 +109,7 @@ oh-my-customcode로 구동됩니다.
109
109
  | `/omcustom:npm-publish` | npm 레지스트리에 패키지 배포 |
110
110
  | `/omcustom:npm-version` | 시맨틱 버전 관리 |
111
111
  | `/omcustom:npm-audit` | 의존성 감사 |
112
- | `/omcustom:release-notes` | 릴리즈 노트 생성 (git 히스토리 기반) |
112
+ | `/omcustom-release-notes` | 릴리즈 노트 생성 (git 히스토리 기반) |
113
113
  | `/codex-exec` | Codex CLI 프롬프트 실행 |
114
114
  | `/optimize-analyze` | 번들 및 성능 분석 |
115
115
  | `/optimize-bundle` | 번들 크기 최적화 |
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.45.3",
2
+ "version": "0.47.0",
3
3
  "lastUpdated": "2026-03-16T00:00:00.000Z",
4
4
  "components": [
5
5
  {
@@ -18,7 +18,7 @@
18
18
  "name": "skills",
19
19
  "path": ".claude/skills",
20
20
  "description": "Reusable skill modules (includes slash commands)",
21
- "files": 82
21
+ "files": 83
22
22
  },
23
23
  {
24
24
  "name": "guides",