oh-my-customcode 0.13.2 → 0.14.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
@@ -21,7 +21,7 @@ Like oh-my-zsh transformed shell customization, oh-my-customcode makes personali
21
21
 
22
22
  | Feature | Description |
23
23
  |---------|-------------|
24
- | **Batteries Included** | 42 agents, 52 skills, 22 guides, 18 rules, 1 hook, 4 contexts - ready to use out of the box |
24
+ | **Batteries Included** | 42 agents, 52 skills, 22 guides, 18 rules, 1 hook, 4 contexts, ontology graph - ready to use out of the box |
25
25
  | **Sub-Agent Model** | Supports hierarchical agent orchestration with specialized roles |
26
26
  | **Dead Simple Customization** | Create a folder + markdown file = new agent or skill |
27
27
  | **Mix and Match** | Use built-in components, create your own, or combine both |
@@ -125,111 +125,20 @@ Claude Code selects the appropriate model and parallelizes independent tasks (up
125
125
  | **QA** | 3 | qa-planner, qa-writer, qa-engineer |
126
126
  | **Total** | **42** | |
127
127
 
128
- Canonical agent IDs (`templates/.claude/agents/*.md`):
129
-
130
- ```text
131
- arch-documenter
132
- arch-speckit-agent
133
- be-express-expert
134
- be-fastapi-expert
135
- be-go-backend-expert
136
- be-nestjs-expert
137
- be-springboot-expert
138
- db-postgres-expert
139
- db-redis-expert
140
- db-supabase-expert
141
- de-airflow-expert
142
- de-dbt-expert
143
- de-kafka-expert
144
- de-pipeline-expert
145
- de-snowflake-expert
146
- de-spark-expert
147
- fe-svelte-agent
148
- fe-vercel-agent
149
- fe-vuejs-agent
150
- infra-aws-expert
151
- infra-docker-expert
152
- lang-golang-expert
153
- lang-java21-expert
154
- lang-kotlin-expert
155
- lang-python-expert
156
- lang-rust-expert
157
- lang-typescript-expert
158
- mgr-claude-code-bible
159
- mgr-creator
160
- mgr-gitnerd
161
- mgr-sauron
162
- mgr-supplier
163
- mgr-sync-checker
164
- mgr-updater
165
- qa-engineer
166
- qa-planner
167
- qa-writer
168
- sys-memory-keeper
169
- sys-naggy
170
- tool-bun-expert
171
- tool-npm-expert
172
- tool-optimizer
173
- ```
174
-
175
128
  ### Skills (52)
176
129
 
177
- Canonical skill IDs (`templates/.claude/skills/*/SKILL.md`):
178
-
179
- ```text
180
- airflow-best-practices
181
- audit-agents
182
- aws-best-practices
183
- claude-code-bible
184
- codex-exec
185
- create-agent
186
- dbt-best-practices
187
- de-lead-routing
188
- dev-lead-routing
189
- dev-refactor
190
- dev-review
191
- docker-best-practices
192
- fastapi-best-practices
193
- fix-refs
194
- go-backend-best-practices
195
- go-best-practices
196
- help
197
- intent-detection
198
- kafka-best-practices
199
- kotlin-best-practices
200
- lists
201
- memory-management
202
- memory-recall
203
- memory-save
204
- monitoring-setup
205
- npm-audit
206
- npm-publish
207
- npm-version
208
- optimize-analyze
209
- optimize-bundle
210
- optimize-report
211
- pipeline-architecture-patterns
212
- postgres-best-practices
213
- python-best-practices
214
- qa-lead-routing
215
- react-best-practices
216
- redis-best-practices
217
- result-aggregation
218
- rust-best-practices
219
- sauron-watch
220
- secretary-routing
221
- snowflake-best-practices
222
- spark-best-practices
223
- springboot-best-practices
224
- status
225
- supabase-postgres-best-practices
226
- typescript-best-practices
227
- update-docs
228
- update-external
229
- vercel-deploy
230
- web-design-guidelines
231
- writing-clearly-and-concisely
232
- ```
130
+ | Category | Count | Skills |
131
+ |----------|-------|--------|
132
+ | **Routing** | 4 | secretary-routing, dev-lead-routing, de-lead-routing, qa-lead-routing |
133
+ | **Best Practices** | 18 | go-best-practices, python-best-practices, typescript-best-practices, kotlin-best-practices, rust-best-practices, react-best-practices, fastapi-best-practices, springboot-best-practices, go-backend-best-practices, docker-best-practices, aws-best-practices, postgres-best-practices, supabase-postgres-best-practices, redis-best-practices, airflow-best-practices, dbt-best-practices, kafka-best-practices, snowflake-best-practices |
134
+ | **Development** | 5 | dev-review, dev-refactor, create-agent, intent-detection, web-design-guidelines |
135
+ | **Data Engineering** | 2 | spark-best-practices, pipeline-architecture-patterns |
136
+ | **Optimization** | 3 | optimize-analyze, optimize-bundle, optimize-report |
137
+ | **Memory** | 3 | memory-save, memory-recall, memory-management |
138
+ | **Package Management** | 3 | npm-publish, npm-version, npm-audit |
139
+ | **Operations** | 7 | update-docs, update-external, audit-agents, fix-refs, sauron-watch, monitoring-setup, claude-code-bible |
140
+ | **Utilities** | 5 | lists, help, status, result-aggregation, writing-clearly-and-concisely |
141
+ | **Deploy** | 2 | vercel-deploy, codex-exec |
233
142
 
234
143
  ### Guides (22)
235
144
 
@@ -260,9 +169,20 @@ Shared context files for cross-agent knowledge and mode configurations.
260
169
 
261
170
  ### Packages
262
171
 
263
- | Package | Version | Description |
264
- |---------|---------|-------------|
265
- | [ontology-rag](packages/ontology-rag/) | 0.3.0 | Ontology+RAG context engine for intelligent context loading. Reduces token usage by 75-95% through hierarchical loading, graph-based routing, and adaptive compression. |
172
+ #### [ontology-rag](packages/ontology-rag/)
173
+
174
+ Ontology+RAG context engine for intelligent agent context loading.
175
+
176
+ | Feature | Description |
177
+ |---------|-------------|
178
+ | **Ontology Loading** | Parse YAML ontologies (agents, skills, rules) |
179
+ | **Graph Traversal** | Navigate dependency graphs with BFS and PageRank |
180
+ | **Semantic Routing** | LLM-based agent selection with keyword fallback |
181
+ | **Hybrid Search** | 4-signal ranking (keyword, graph, community, importance) |
182
+ | **Token Budget** | Adaptive budget management — reduces token usage by 75-95% |
183
+ | **MCP Server** | Direct integration with Claude Code via MCP protocol |
184
+
185
+ Automatically configured during `omcustom init` when [uv](https://docs.astral.sh/uv/) is available.
266
186
 
267
187
  ---
268
188
 
@@ -277,6 +197,7 @@ Shared context files for cross-agent knowledge and mode configurations.
277
197
  | `omcustom list agents` | List agents only |
278
198
  | `omcustom doctor` | Verify installation health |
279
199
  | `omcustom doctor --fix` | Auto-fix common issues |
200
+ | `omcustom security` | Scan for security issues in hooks and configs |
280
201
 
281
202
  **Global Options:**
282
203
  | Option | Description |
@@ -308,6 +229,12 @@ your-project/
308
229
  │ ├── react-best-practices/
309
230
  │ ├── secretary-routing/
310
231
  │ └── ...
232
+ ├── ontology/ # Ontology knowledge graph for RAG context
233
+ │ ├── schema.yaml
234
+ │ ├── agents.yaml
235
+ │ ├── skills.yaml
236
+ │ ├── rules.yaml
237
+ │ └── graphs/
311
238
  └── guides/ # Reference docs (22 total)
312
239
  ```
313
240
 
package/dist/cli/index.js CHANGED
@@ -11852,6 +11852,30 @@ var en_default = {
11852
11852
  fail: "Some index.yaml files are invalid"
11853
11853
  }
11854
11854
  }
11855
+ },
11856
+ security: {
11857
+ description: "Scan for security issues in hooks, configs, and templates",
11858
+ verboseOption: "Show detailed scan results",
11859
+ scanning: "Running security scan...",
11860
+ passed: "Security scan passed! No issues found.",
11861
+ failed: "Security issues detected.",
11862
+ summary: "Security: {{pass}} passed, {{warn}} warnings, {{fail}} failed",
11863
+ checks: {
11864
+ hooks: {
11865
+ pass: "Hook scripts are safe",
11866
+ warn: "Hook scripts have potential concerns",
11867
+ fail: "Hook scripts contain dangerous patterns"
11868
+ },
11869
+ secrets: {
11870
+ pass: "No secrets found in configuration files",
11871
+ fail: "Secrets or credentials found in configuration"
11872
+ },
11873
+ integrity: {
11874
+ pass: "Template file permissions are secure",
11875
+ warn: "Some files have overly permissive settings",
11876
+ fail: "Security-sensitive files found in project"
11877
+ }
11878
+ }
11855
11879
  }
11856
11880
  },
11857
11881
  init: {
@@ -12144,6 +12168,30 @@ var ko_default = {
12144
12168
  fail: "일부 index.yaml 파일이 잘못됨"
12145
12169
  }
12146
12170
  }
12171
+ },
12172
+ security: {
12173
+ description: "훅, 설정, 템플릿의 보안 문제 검사",
12174
+ verboseOption: "상세 검사 결과 표시",
12175
+ scanning: "보안 검사 실행 중...",
12176
+ passed: "보안 검사 통과! 문제가 발견되지 않았습니다.",
12177
+ failed: "보안 문제가 발견되었습니다.",
12178
+ summary: "보안: {{pass}}개 통과, {{warn}}개 경고, {{fail}}개 실패",
12179
+ checks: {
12180
+ hooks: {
12181
+ pass: "훅 스크립트가 안전합니다",
12182
+ warn: "훅 스크립트에 잠재적 우려사항이 있습니다",
12183
+ fail: "훅 스크립트에 위험한 패턴이 포함되어 있습니다"
12184
+ },
12185
+ secrets: {
12186
+ pass: "설정 파일에 비밀 정보가 없습니다",
12187
+ fail: "설정 파일에서 비밀 정보 또는 인증 정보가 발견되었습니다"
12188
+ },
12189
+ integrity: {
12190
+ pass: "템플릿 파일 권한이 안전합니다",
12191
+ warn: "일부 파일의 권한이 과도하게 허용되어 있습니다",
12192
+ fail: "보안에 민감한 파일이 프로젝트에 존재합니다"
12193
+ }
12194
+ }
12147
12195
  }
12148
12196
  },
12149
12197
  init: {
@@ -13083,6 +13131,7 @@ var CLAUDE_LAYOUT = {
13083
13131
  ".claude/contexts",
13084
13132
  ".claude/agents",
13085
13133
  ".claude/skills",
13134
+ ".claude/ontology",
13086
13135
  "guides"
13087
13136
  ]
13088
13137
  };
@@ -13963,7 +14012,7 @@ async function install(options) {
13963
14012
  return result;
13964
14013
  }
13965
14014
  function getAllComponents() {
13966
- return ["rules", "agents", "skills", "guides", "hooks", "contexts"];
14015
+ return ["rules", "agents", "skills", "guides", "hooks", "contexts", "ontology"];
13967
14016
  }
13968
14017
  async function installComponent(targetDir, component, options) {
13969
14018
  if (component === "entry-md") {
@@ -14635,6 +14684,320 @@ async function listCommand(type = "all", options = {}) {
14635
14684
  }
14636
14685
  }
14637
14686
 
14687
+ // src/cli/security.ts
14688
+ import { constants as constants2, promises as fs2 } from "node:fs";
14689
+ import path2 from "node:path";
14690
+ async function pathExists2(targetPath) {
14691
+ try {
14692
+ await fs2.access(targetPath, constants2.F_OK);
14693
+ return true;
14694
+ } catch {
14695
+ return false;
14696
+ }
14697
+ }
14698
+ function isValidUtf8Text(content) {
14699
+ try {
14700
+ content.toString("utf-8");
14701
+ return true;
14702
+ } catch {
14703
+ return false;
14704
+ }
14705
+ }
14706
+ async function findAllFiles(dir2) {
14707
+ const results = [];
14708
+ try {
14709
+ const entries = await fs2.readdir(dir2, { withFileTypes: true });
14710
+ for (const entry of entries) {
14711
+ const fullPath = path2.join(dir2, entry.name);
14712
+ if (entry.isDirectory()) {
14713
+ const subResults = await findAllFiles(fullPath);
14714
+ results.push(...subResults);
14715
+ } else if (entry.isFile()) {
14716
+ results.push(fullPath);
14717
+ }
14718
+ }
14719
+ } catch {}
14720
+ return results;
14721
+ }
14722
+ var DANGEROUS_PATTERNS = [
14723
+ {
14724
+ pattern: /rm\s+-rf\s+[/~]/,
14725
+ name: "rm -rf with root/home path",
14726
+ severity: "fail"
14727
+ },
14728
+ {
14729
+ pattern: /curl\s+.*\|\s*(bash|sh|eval)/,
14730
+ name: "curl pipe to shell",
14731
+ severity: "fail"
14732
+ },
14733
+ {
14734
+ pattern: /wget\s+.*\|\s*(bash|sh|eval)/,
14735
+ name: "wget pipe to shell",
14736
+ severity: "fail"
14737
+ },
14738
+ { pattern: /\bsudo\b/, name: "sudo usage", severity: "warn" },
14739
+ { pattern: /chmod\s+777/, name: "chmod 777", severity: "warn" },
14740
+ { pattern: /\beval\s*\(/, name: "eval() usage", severity: "warn" },
14741
+ {
14742
+ pattern: /\$\{.*:-.*\}.*>\s*\/etc/,
14743
+ name: "write to /etc",
14744
+ severity: "fail"
14745
+ },
14746
+ {
14747
+ pattern: /base64\s+(-d|--decode).*\|\s*(bash|sh)/,
14748
+ name: "base64 decode to shell",
14749
+ severity: "fail"
14750
+ }
14751
+ ];
14752
+ function extractCommands(hooks) {
14753
+ const commands = [];
14754
+ if (!hooks || typeof hooks !== "object")
14755
+ return commands;
14756
+ for (const hookName in hooks) {
14757
+ const hook = hooks[hookName];
14758
+ if (hook && typeof hook === "object") {
14759
+ for (const eventName in hook) {
14760
+ const event = hook[eventName];
14761
+ if (Array.isArray(event)) {
14762
+ for (const item of event) {
14763
+ if (typeof item === "object" && item && "command" in item) {
14764
+ commands.push(String(item.command));
14765
+ }
14766
+ }
14767
+ }
14768
+ }
14769
+ }
14770
+ }
14771
+ return commands;
14772
+ }
14773
+ function scanCommands(commands) {
14774
+ const findings = [];
14775
+ let worstSeverity = "pass";
14776
+ for (const command of commands) {
14777
+ for (const { pattern, name, severity } of DANGEROUS_PATTERNS) {
14778
+ if (pattern.test(command)) {
14779
+ findings.push(`${name}: ${command.substring(0, 80)}${command.length > 80 ? "..." : ""}`);
14780
+ if (severity === "fail") {
14781
+ worstSeverity = "fail";
14782
+ } else if (severity === "warn" && worstSeverity === "pass") {
14783
+ worstSeverity = "warn";
14784
+ }
14785
+ }
14786
+ }
14787
+ }
14788
+ return { findings, worstSeverity };
14789
+ }
14790
+ async function checkHookScripts(targetDir, rootDir = ".claude") {
14791
+ const hooksFile = path2.join(targetDir, rootDir, "hooks", "hooks.json");
14792
+ const exists2 = await pathExists2(hooksFile);
14793
+ if (!exists2) {
14794
+ return {
14795
+ name: "Hook scripts",
14796
+ status: "pass",
14797
+ message: i18n.t("cli.security.checks.hooks.pass"),
14798
+ fixable: false
14799
+ };
14800
+ }
14801
+ try {
14802
+ const content = await fs2.readFile(hooksFile, "utf-8");
14803
+ const hooks = JSON.parse(content);
14804
+ const commands = extractCommands(hooks);
14805
+ const { findings, worstSeverity } = scanCommands(commands);
14806
+ if (findings.length > 0) {
14807
+ const message = worstSeverity === "fail" ? i18n.t("cli.security.checks.hooks.fail") : i18n.t("cli.security.checks.hooks.warn");
14808
+ return {
14809
+ name: "Hook scripts",
14810
+ status: worstSeverity,
14811
+ message: `${message} (${findings.length} issues)`,
14812
+ fixable: false,
14813
+ details: findings
14814
+ };
14815
+ }
14816
+ return {
14817
+ name: "Hook scripts",
14818
+ status: "pass",
14819
+ message: i18n.t("cli.security.checks.hooks.pass"),
14820
+ fixable: false
14821
+ };
14822
+ } catch (error2) {
14823
+ return {
14824
+ name: "Hook scripts",
14825
+ status: "warn",
14826
+ message: `Failed to parse hooks.json: ${error2 instanceof Error ? error2.message : String(error2)}`,
14827
+ fixable: false
14828
+ };
14829
+ }
14830
+ }
14831
+ async function checkConfigSecrets(targetDir, rootDir = ".claude") {
14832
+ const configDir = path2.join(targetDir, rootDir);
14833
+ const exists2 = await pathExists2(configDir);
14834
+ if (!exists2) {
14835
+ return {
14836
+ name: "Config secrets",
14837
+ status: "pass",
14838
+ message: i18n.t("cli.security.checks.secrets.pass"),
14839
+ fixable: false
14840
+ };
14841
+ }
14842
+ const SECRET_PATTERNS = [
14843
+ {
14844
+ pattern: /(?:AWS_SECRET|AWS_ACCESS_KEY|AWS_SESSION)[_A-Z]*\s*[=:]\s*['"]?[A-Za-z0-9/+=]{20,}/,
14845
+ name: "AWS credential"
14846
+ },
14847
+ {
14848
+ pattern: /(?:GITHUB_TOKEN|GH_TOKEN|GITHUB_PAT)\s*[=:]\s*['"]?(?:ghp_|gho_|ghs_|ghr_|github_pat_)[A-Za-z0-9_]+/,
14849
+ name: "GitHub token"
14850
+ },
14851
+ {
14852
+ pattern: /(?:sk-|sk_live_|sk_test_)[A-Za-z0-9]{20,}/,
14853
+ name: "API secret key (sk-*)"
14854
+ },
14855
+ {
14856
+ pattern: /(?:password|passwd|secret)\s*[=:]\s*['"]?[^\s'"]{8,}/i,
14857
+ name: "Hardcoded password/secret"
14858
+ },
14859
+ {
14860
+ pattern: /-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----/,
14861
+ name: "Private key"
14862
+ }
14863
+ ];
14864
+ const files = await findAllFiles(configDir);
14865
+ const findings = [];
14866
+ for (const file of files) {
14867
+ try {
14868
+ const content = await fs2.readFile(file);
14869
+ if (!isValidUtf8Text(content)) {
14870
+ continue;
14871
+ }
14872
+ const text = content.toString("utf-8");
14873
+ for (const { pattern, name } of SECRET_PATTERNS) {
14874
+ if (pattern.test(text)) {
14875
+ const relativePath = path2.relative(targetDir, file);
14876
+ findings.push(`${relativePath}: ${name}`);
14877
+ }
14878
+ }
14879
+ } catch {}
14880
+ }
14881
+ if (findings.length > 0) {
14882
+ return {
14883
+ name: "Config secrets",
14884
+ status: "fail",
14885
+ message: `${i18n.t("cli.security.checks.secrets.fail")} (${findings.length} found)`,
14886
+ fixable: false,
14887
+ details: findings
14888
+ };
14889
+ }
14890
+ return {
14891
+ name: "Config secrets",
14892
+ status: "pass",
14893
+ message: i18n.t("cli.security.checks.secrets.pass"),
14894
+ fixable: false
14895
+ };
14896
+ }
14897
+ async function checkEnvFiles(targetDir) {
14898
+ const findings = [];
14899
+ let severity = "pass";
14900
+ const envFiles = [".env", ".env.local", ".env.production", ".env.development"];
14901
+ for (const envFile of envFiles) {
14902
+ const envPath = path2.join(targetDir, envFile);
14903
+ if (await pathExists2(envPath)) {
14904
+ findings.push(`Security-sensitive file found: ${envFile}`);
14905
+ severity = "fail";
14906
+ }
14907
+ }
14908
+ return { findings, severity };
14909
+ }
14910
+ async function checkShellPermissions(targetDir, shellScripts) {
14911
+ const findings = [];
14912
+ let severity = "pass";
14913
+ for (const script of shellScripts) {
14914
+ try {
14915
+ const stats = await fs2.stat(script);
14916
+ const mode = stats.mode & 511;
14917
+ const relativePath = path2.relative(targetDir, script);
14918
+ if (mode === 511) {
14919
+ findings.push(`Overly permissive permissions (777): ${relativePath}`);
14920
+ if (severity === "pass") {
14921
+ severity = "warn";
14922
+ }
14923
+ } else if (mode & 2) {
14924
+ findings.push(`World-writable: ${relativePath}`);
14925
+ if (severity === "pass") {
14926
+ severity = "warn";
14927
+ }
14928
+ }
14929
+ } catch {}
14930
+ }
14931
+ return { findings, severity };
14932
+ }
14933
+ async function checkTemplateIntegrity(targetDir) {
14934
+ let worstSeverity = "pass";
14935
+ const allFindings = [];
14936
+ const envCheck = await checkEnvFiles(targetDir);
14937
+ allFindings.push(...envCheck.findings);
14938
+ if (envCheck.severity === "fail") {
14939
+ worstSeverity = "fail";
14940
+ }
14941
+ const allFiles = await findAllFiles(targetDir);
14942
+ const shellScripts = allFiles.filter((f) => f.endsWith(".sh"));
14943
+ const permCheck = await checkShellPermissions(targetDir, shellScripts);
14944
+ allFindings.push(...permCheck.findings);
14945
+ if (permCheck.severity === "warn" && worstSeverity === "pass") {
14946
+ worstSeverity = "warn";
14947
+ }
14948
+ if (allFindings.length > 0) {
14949
+ const message = worstSeverity === "fail" ? i18n.t("cli.security.checks.integrity.fail") : i18n.t("cli.security.checks.integrity.warn");
14950
+ return {
14951
+ name: "Template integrity",
14952
+ status: worstSeverity,
14953
+ message: `${message} (${allFindings.length} issues)`,
14954
+ fixable: false,
14955
+ details: allFindings
14956
+ };
14957
+ }
14958
+ return {
14959
+ name: "Template integrity",
14960
+ status: "pass",
14961
+ message: i18n.t("cli.security.checks.integrity.pass"),
14962
+ fixable: false
14963
+ };
14964
+ }
14965
+ async function securityCommand(_options = {}) {
14966
+ const targetDir = process.cwd();
14967
+ console.log(i18n.t("cli.security.scanning"));
14968
+ console.log("");
14969
+ const layout = getProviderLayout();
14970
+ const checks = await Promise.all([
14971
+ checkHookScripts(targetDir, layout.rootDir),
14972
+ checkConfigSecrets(targetDir, layout.rootDir),
14973
+ checkTemplateIntegrity(targetDir)
14974
+ ]);
14975
+ for (const check of checks) {
14976
+ printCheck(check);
14977
+ }
14978
+ const passCount = checks.filter((c) => c.status === "pass").length;
14979
+ const warnCount = checks.filter((c) => c.status === "warn").length;
14980
+ const failCount = checks.filter((c) => c.status === "fail").length;
14981
+ console.log("");
14982
+ if (failCount === 0 && warnCount === 0) {
14983
+ console.log(i18n.t("cli.security.passed"));
14984
+ } else {
14985
+ console.log(i18n.t("cli.security.failed"));
14986
+ }
14987
+ console.log(i18n.t("cli.security.summary", {
14988
+ pass: passCount,
14989
+ warn: warnCount,
14990
+ fail: failCount
14991
+ }));
14992
+ return {
14993
+ success: failCount === 0,
14994
+ checks,
14995
+ passCount,
14996
+ warnCount,
14997
+ failCount
14998
+ };
14999
+ }
15000
+
14638
15001
  // src/core/updater.ts
14639
15002
  import { join as join8 } from "node:path";
14640
15003
 
@@ -14807,11 +15170,11 @@ function getEntryTemplateName2(language) {
14807
15170
  return language === "ko" ? `${baseName}.md.ko` : `${baseName}.md.en`;
14808
15171
  }
14809
15172
  async function backupFile(filePath) {
14810
- const fs2 = await import("node:fs/promises");
15173
+ const fs3 = await import("node:fs/promises");
14811
15174
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
14812
15175
  const backupPath = `${filePath}.backup-${timestamp}`;
14813
15176
  if (await fileExists(filePath)) {
14814
- await fs2.copyFile(filePath, backupPath);
15177
+ await fs3.copyFile(filePath, backupPath);
14815
15178
  debug("update.file_backed_up", { path: filePath, backup: backupPath });
14816
15179
  }
14817
15180
  }
@@ -14987,7 +15350,7 @@ async function checkForUpdates(targetDir) {
14987
15350
  };
14988
15351
  }
14989
15352
  function getAllUpdateComponents() {
14990
- return ["rules", "agents", "skills", "guides", "hooks", "contexts"];
15353
+ return ["rules", "agents", "skills", "guides", "hooks", "contexts", "ontology"];
14991
15354
  }
14992
15355
  async function getLatestVersion() {
14993
15356
  const layout = getProviderLayout();
@@ -15023,8 +15386,8 @@ async function updateComponent(targetDir, component, customizations, options, co
15023
15386
  skipPaths.push(cc.path);
15024
15387
  }
15025
15388
  }
15026
- const path2 = await import("node:path");
15027
- const normalizedSkipPaths = skipPaths.map((p) => path2.relative(destPath, join8(targetDir, p)));
15389
+ const path3 = await import("node:path");
15390
+ const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join8(targetDir, p)));
15028
15391
  await copyDirectory(srcPath, destPath, {
15029
15392
  overwrite: true,
15030
15393
  skipPaths: normalizedSkipPaths.length > 0 ? normalizedSkipPaths : undefined
@@ -15045,7 +15408,7 @@ function getComponentPath2(component) {
15045
15408
  async function backupInstallation(targetDir) {
15046
15409
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
15047
15410
  const backupDir = join8(targetDir, `.omcustom-backup-${timestamp}`);
15048
- const fs2 = await import("node:fs/promises");
15411
+ const fs3 = await import("node:fs/promises");
15049
15412
  await ensureDirectory(backupDir);
15050
15413
  const layout = getProviderLayout();
15051
15414
  const dirsToBackup = [layout.rootDir, "guides"];
@@ -15058,7 +15421,7 @@ async function backupInstallation(targetDir) {
15058
15421
  }
15059
15422
  const entryPath = join8(targetDir, layout.entryFile);
15060
15423
  if (await fileExists(entryPath)) {
15061
- await fs2.copyFile(entryPath, join8(backupDir, layout.entryFile));
15424
+ await fs3.copyFile(entryPath, join8(backupDir, layout.entryFile));
15062
15425
  }
15063
15426
  return backupDir;
15064
15427
  }
@@ -15131,8 +15494,8 @@ function printUpdateResults(result) {
15131
15494
  console.log(i18n.t("cli.update.preservedFiles", { count: result.preservedFiles.length }));
15132
15495
  }
15133
15496
  if (result.backedUpPaths.length > 0) {
15134
- for (const path2 of result.backedUpPaths) {
15135
- console.log(i18n.t("cli.update.backupCreated", { path: path2 }));
15497
+ for (const path3 of result.backedUpPaths) {
15498
+ console.log(i18n.t("cli.update.backupCreated", { path: path3 }));
15136
15499
  }
15137
15500
  }
15138
15501
  for (const warning of result.warnings) {
@@ -15169,6 +15532,10 @@ function createProgram() {
15169
15532
  program2.command("doctor").description(i18n.t("cli.doctor.description")).option("--fix", i18n.t("cli.doctor.fixOption")).action(async (options) => {
15170
15533
  await doctorCommand(options);
15171
15534
  });
15535
+ program2.command("security").description(i18n.t("cli.security.description")).option("--verbose", i18n.t("cli.security.verboseOption")).action(async (options) => {
15536
+ const result = await securityCommand(options);
15537
+ process.exitCode = result.success ? 0 : 1;
15538
+ });
15172
15539
  program2.hook("preAction", async (thisCommand, actionCommand) => {
15173
15540
  const opts = thisCommand.optsWithGlobals();
15174
15541
  const skipCheck = opts.skipVersionCheck || false;
package/dist/index.js CHANGED
@@ -771,6 +771,7 @@ var CLAUDE_LAYOUT = {
771
771
  ".claude/contexts",
772
772
  ".claude/agents",
773
773
  ".claude/skills",
774
+ ".claude/ontology",
774
775
  "guides"
775
776
  ]
776
777
  };
@@ -929,7 +930,7 @@ async function getTemplateManifest() {
929
930
  };
930
931
  }
931
932
  function getAllComponents() {
932
- return ["rules", "agents", "skills", "guides", "hooks", "contexts"];
933
+ return ["rules", "agents", "skills", "guides", "hooks", "contexts", "ontology"];
933
934
  }
934
935
  async function installComponent(targetDir, component, options) {
935
936
  if (component === "entry-md") {
@@ -1409,7 +1410,7 @@ async function preserveCustomizations(targetDir, customizations) {
1409
1410
  return preserved;
1410
1411
  }
1411
1412
  function getAllUpdateComponents() {
1412
- return ["rules", "agents", "skills", "guides", "hooks", "contexts"];
1413
+ return ["rules", "agents", "skills", "guides", "hooks", "contexts", "ontology"];
1413
1414
  }
1414
1415
  async function getLatestVersion() {
1415
1416
  const layout = getProviderLayout();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-customcode",
3
- "version": "0.13.2",
3
+ "version": "0.14.0",
4
4
  "description": "Batteries-included agent harness for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.2.0",
3
- "lastUpdated": "2026-02-17T07:30:00.000Z",
2
+ "version": "0.3.0",
3
+ "lastUpdated": "2026-02-18T00:00:00.000Z",
4
4
  "components": [
5
5
  {
6
6
  "name": "rules",
@@ -37,6 +37,12 @@
37
37
  "path": ".claude/contexts",
38
38
  "description": "Context configuration files",
39
39
  "files": 4
40
+ },
41
+ {
42
+ "name": "ontology",
43
+ "path": ".claude/ontology",
44
+ "description": "Ontology knowledge graph for RAG-powered agent context",
45
+ "files": 8
40
46
  }
41
47
  ],
42
48
  "source": "https://github.com/baekenough/oh-my-customcode"