jhste-skills 0.3.7 → 0.4.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.ja.md +16 -0
  3. package/README.ko.md +16 -0
  4. package/README.md +17 -1
  5. package/README.zh.md +16 -0
  6. package/cli/global.mjs +169 -0
  7. package/cli/index.mjs +2 -1
  8. package/cli/install-actions/bridge-writer.mjs +38 -1
  9. package/cli/install-actions/skills.mjs +69 -4
  10. package/cli/postinstall-global-refresh.mjs +57 -0
  11. package/cli/shared/files.mjs +16 -0
  12. package/cli/shared/templates.mjs +21 -0
  13. package/cli/sync-core.mjs +2 -1
  14. package/cli/uninstall.mjs +1 -40
  15. package/docs/CLI.md +20 -0
  16. package/package.json +2 -1
  17. package/scripts/docs-check-data.mjs +13 -7
  18. package/scripts/docs-check.mjs +3 -3
  19. package/scripts/smoke/connect-scenarios.mjs +2 -2
  20. package/scripts/smoke/global-scenarios.mjs +127 -0
  21. package/scripts/smoke/helpers.mjs +25 -0
  22. package/scripts/smoke/install-scenarios.mjs +6 -15
  23. package/scripts/smoke/mode-scenarios.mjs +2 -2
  24. package/scripts/smoke-test.mjs +3 -1
  25. package/skills/_shared/evidence-discipline.md +30 -0
  26. package/skills/_shared/issue-candidate.md +23 -0
  27. package/skills/_shared/scope-discipline.md +13 -0
  28. package/skills/_shared/solid-lens.md +38 -0
  29. package/skills/ask-jhste/SKILL.md +37 -0
  30. package/skills/grill-me/SKILL.md +3 -2
  31. package/skills/jhste-architecture-review/SKILL.md +3 -5
  32. package/skills/jhste-architecture-review/references/architecture-review.md +3 -33
  33. package/skills/jhste-code-quality/SKILL.md +3 -2
  34. package/skills/jhste-code-quality/references/code-quality.md +1 -1
  35. package/skills/jhste-crawler-automation/SKILL.md +1 -1
  36. package/skills/jhste-db-api-boundary/SKILL.md +1 -1
  37. package/skills/jhste-engineering-groundwork/SKILL.md +24 -95
  38. package/skills/jhste-long-running-work-loop/SKILL.md +25 -113
  39. package/skills/jhste-red-team-review/SKILL.md +23 -82
  40. package/skills/jhste-red-team-review/references/red-team-review.md +2 -21
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 - 2026-07-01
4
+
5
+ ### Added
6
+ - Added `skills/_shared/` shared companion resources (`solid-lens`, `evidence-discipline`, `issue-candidate`, `scope-discipline`) as the single source for cross-cutting doctrine, cited by the jhste skills instead of restating it per skill.
7
+ - Added a first-class shared-resource install concept: directories under `skills/` starting with `_` are not skills (excluded from listing, selection, and missing-skill checks) but are copied alongside skills so installed `../_shared/...` references never dangle.
8
+ - Added `jhste-skills global` — advisory-only, user-level setup for Codex, Claude Code, and OpenCode that installs skills to `~/.jhste/skills` and writes marker-managed bridges into `~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, and `~/.config/opencode/AGENTS.md`, with no git hooks or per-repo files; `--uninstall` reverses it.
9
+ - Added `ask-jhste`, a user-invoked router for choosing the right jhste skill/workflow without adding always-on model context.
10
+ - Added OpenCode support to global setup via `~/.config/opencode/AGENTS.md`.
11
+ - Added a safe global-only npm postinstall refresh: after a user has opted into `jhste-skills global`, later `npm update -g jhste-skills` refreshes managed global skill copies and existing managed global bridges without creating a new setup.
12
+
13
+ ### Changed
14
+ - Trimmed jhste skill descriptions and SKILL bodies for lower operating-context cost while preserving behavior; SKILL files now own the decision procedure and cite `_shared` doctrine, and reference files are examples/lookup only.
15
+ - Reduced `docs-check` to load-bearing behavior anchors (pinned once at their source) instead of exact prose/section titles; de-duplicated the triplicated issue-candidate pin to the shared doctrine.
16
+ - Deduplicated the `## Workflow` section in `AGENTS.md` against the managed bridge block.
17
+ - Extracted shared `removeManagedSkills`/manifest loading into `install-actions/skills.mjs` for reuse by `uninstall` and `global`.
18
+ - Made `grill-me` a user-invoked alias for `grilling` to reduce duplicate invocation surface while preserving the personal pressure-test entrypoint.
19
+ - Made `global` skip bridge writes when skill installation is blocked, and preflight `_shared` unmanaged conflicts before copying selected skills.
20
+ - Synced README translations (ko/zh/ja) with global setup, `ask-jhste`, and `_shared` companion-resource docs.
21
+ - Hardened `global` option validation, refused vendor-only global installs, reported invalid-manifest uninstall failures, and rejected unsafe manifest entry names before managed skill removal.
22
+
23
+ ### Validation
24
+ - `npm test` passed (12 stages, including new `_shared` install integrity and `global` command smoke coverage).
25
+
3
26
  ## 0.3.7 - 2026-06-30
4
27
 
5
28
  ### Added
package/README.ja.md CHANGED
@@ -81,6 +81,19 @@ jhste-skills install
81
81
 
82
82
  一度だけ試すなら `npx`、普段使う CLI として置いておきたいなら `npm install -g` を使ってください。
83
83
 
84
+ ### Global setup(Codex + Claude Code + OpenCode、advisory-only)
85
+
86
+ リポジトリごとのファイルや git hooks を作らず、すべてのリポジトリで skills を使いたい場合は、ユーザーレベルで一度だけ設定します。
87
+
88
+ ```bash
89
+ npm install -g jhste-skills
90
+ jhste-skills global
91
+ ```
92
+
93
+ このコマンドは skills と shared companion resources を `~/.jhste/skills` にコピーし、各 agent の global instruction file(`~/.claude/CLAUDE.md`、`~/.codex/AGENTS.md`、`~/.config/opencode/AGENTS.md`)に marker-managed bridge block を書き込みます。ファイルがなければ作成します。git hooks やリポジトリごとのファイルは書き込まず、guard は advisory のままです(`jhste-skills guard --scope changed`)。対象 agent は `--agents codex,claude,opencode` で選択でき、`jhste-skills global --uninstall` で削除できます。
94
+
95
+ この一度の global setup 後は、以降の `npm update -g jhste-skills` で managed skill copies と既存の managed global bridge blocks が安全に更新されます。agent やオプションを変更したい場合だけ `jhste-skills global` を再実行してください。
96
+
84
97
  デフォルトインストールは Normal mode を使います。
85
98
 
86
99
  - bundled skills 全体をインストールします: jhste core skills + vendored workflow skills。
@@ -162,6 +175,7 @@ Custom - 効果ベースの質問でセットアップ範囲を選択
162
175
  | Skill | いつ使うか | 何を減らすか |
163
176
  |---|---|---|
164
177
  | [`setup`](skills/setup/SKILL.md)<br>install/connect/update が既存プロジェクト指示を上書きしないようにする安全セットアップ skill | kit をインストール、またはリポジトリに接続するとき | Unsafe overwrite, unmanaged hook conflict, repo instruction replacement |
178
+ | [`ask-jhste`](skills/ask-jhste/SKILL.md)<br>適切な jhste skill や workflow を選ぶ user-invoked router | 次にどの jhste skill/workflow を使うべきか迷うとき | Wrong workflow selection, unnecessary always-on context, accidental side effects from routing |
165
179
  | [`jhste-engineering-groundwork`](skills/jhste-engineering-groundwork/SKILL.md)<br>コード変更前に goal、premise、scope、boundary、failure path を検証する pre-change groundwork skill | non-trivial code change の前 | Blind agreement, scope creep, unverified assumption, unclear boundary |
166
180
  | [`jhste-code-quality`](skills/jhste-code-quality/SKILL.md)<br>input validation、observable failure handling、secret-safe logging、oversized-file review の skill | external input、failure handling、logging、env/config、code-quality review path を触るとき | Unvalidated input, silent failure, secret logging, oversized file |
167
181
  | [`jhste-architecture-review`](skills/jhste-architecture-review/SKILL.md)<br>module boundary、side-effect placement、SOLID-informed design risk を確認する architecture review skill | module boundary、app structure、side-effect placement、responsibility split を変更するとき | Pass-through abstraction, mixed responsibility, side-effect leakage |
@@ -270,3 +284,5 @@ Release acceptance notes は [`docs/ACCEPTANCE_CHECK.md`](docs/ACCEPTANCE_CHECK.
270
284
  - non-trivial work を完了と呼ぶ前に red-team code review を行う。
271
285
 
272
286
  高速な agent には guardrail が必要です。`jhste-skills` は agent に repo-respecting engineering workflow を提供します。
287
+
288
+ Skills は `skills/_shared/` の cross-cutting doctrine(SOLID lens、evidence discipline、issue-candidate protocol、scope discipline)を共有します。`skills/` 配下で名前が `_` から始まるディレクトリは skill ではなく shared companion resource です。skill listing、selection、missing-skill checks からは除外されますが、どの skill をインストールしても一緒にコピーされるため、インストール済み artifact 内の cross-skill `../_shared/...` references が dangling になりません。
package/README.ko.md CHANGED
@@ -82,6 +82,19 @@ jhste-skills install
82
82
 
83
83
  한 번만 실행해보고 싶다면 `npx`를 쓰면 되고, 평소에 계속 사용할 CLI로 두고 싶다면 `npm install -g`를 쓰면 됩니다.
84
84
 
85
+ ### Global setup (Codex + Claude Code + OpenCode, advisory-only)
86
+
87
+ 레포별 파일이나 git hook 없이 모든 레포에서 skills를 쓰고 싶다면, 사용자 수준에서 한 번 설정하세요.
88
+
89
+ ```bash
90
+ npm install -g jhste-skills
91
+ jhste-skills global
92
+ ```
93
+
94
+ 이 명령은 skills와 shared companion resources를 `~/.jhste/skills`에 복사하고, 각 agent의 global instruction file(`~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, `~/.config/opencode/AGENTS.md`)에 marker-managed bridge block을 씁니다. 파일이 없으면 생성합니다. Git hook이나 레포별 파일은 쓰지 않으며, guard는 advisory 상태로 남습니다(`jhste-skills guard --scope changed`). Agent는 `--agents codex,claude,opencode`로 선택하고, `jhste-skills global --uninstall`로 제거할 수 있습니다.
95
+
96
+ 이 최초 global setup 이후에는 `npm update -g jhste-skills`만으로 managed skill copy와 기존 managed global bridge block이 안전하게 새로고침됩니다. Agent나 옵션을 바꾸고 싶을 때만 `jhste-skills global`을 다시 실행하세요.
97
+
85
98
  기본 설치는 Normal mode를 사용합니다.
86
99
 
87
100
  - bundled skills 전체를 설치합니다: jhste core skills + vendored workflow skills.
@@ -165,6 +178,7 @@ Custom - 효과 중심 질문을 통해 설치 범위를 직접 선택
165
178
  | Skill | 언제 쓰나 | 무엇을 줄여주나 |
166
179
  |---|---|---|
167
180
  | [`setup`](skills/setup/SKILL.md)<br>설치, 연결, 업데이트가 기존 프로젝트 지침을 덮어쓰지 않도록 하는 안전 설치 스킬 | kit를 설치하거나 레포에 연결할 때 | unsafe overwrite, unmanaged hook conflict, repo instruction replacement |
181
+ | [`ask-jhste`](skills/ask-jhste/SKILL.md)<br>적절한 jhste skill이나 workflow를 고르는 user-invoked router | 다음에 어떤 jhste skill/workflow를 써야 할지 확실하지 않을 때 | wrong workflow selection, unnecessary always-on context, accidental side effects from routing |
168
182
  | [`jhste-engineering-groundwork`](skills/jhste-engineering-groundwork/SKILL.md)<br>코드 변경 전 목표, 전제, scope, boundary, failure path, final behavior predicate를 검증하는 pre-change groundwork 스킬 | non-trivial code change 전 | blind agreement, scope creep, unverified assumption, unclear boundary |
169
183
  | [`jhste-code-quality`](skills/jhste-code-quality/SKILL.md)<br>입력 검증, 관측 가능한 실패 처리, secret-safe logging, oversized-file review 스킬 | external input, failure handling, logging, env/config, cleanup/search-replace, code-quality review path를 만질 때 | unvalidated input, silent failure, secret logging, unsafe broad cleanup, oversized file |
170
184
  | [`jhste-architecture-review`](skills/jhste-architecture-review/SKILL.md)<br>모듈 경계, side effect 위치, SOLID-informed design risk를 검토하는 아키텍처 리뷰 스킬 | module boundary, app structure, side-effect placement, responsibility split 변경 시 | pass-through abstraction, mixed responsibility, side-effect leakage |
@@ -273,3 +287,5 @@ Release acceptance notes는 [`docs/ACCEPTANCE_CHECK.md`](docs/ACCEPTANCE_CHECK.m
273
287
  - non-trivial work를 완료라고 말하기 전에 red-team code review를 수행합니다.
274
288
 
275
289
  빠른 agent에는 guardrail이 필요합니다. `jhste-skills`는 agent에게 repo-respecting engineering workflow를 제공합니다.
290
+
291
+ Skills는 `skills/_shared/`의 cross-cutting doctrine(SOLID lens, evidence discipline, issue-candidate protocol, scope discipline)을 공유합니다. `skills/` 아래에서 이름이 `_`로 시작하는 디렉터리는 skill이 아니라 shared companion resource입니다. skill listing, selection, missing-skill check에서는 제외되지만, 어떤 skill을 설치하든 함께 복사되어 설치된 artifact 안에서 cross-skill `../_shared/...` reference가 끊어지지 않습니다.
package/README.md CHANGED
@@ -82,7 +82,20 @@ jhste-skills install
82
82
 
83
83
  Use `npx` when you want a one-off run without a global install. Use `npm install -g` when you want `jhste-skills` available as a normal shell command.
84
84
 
85
- The default install uses Normal mode.
85
+ ### Global setup (Codex + Claude Code + OpenCode, advisory-only)
86
+
87
+ If you want the skills available in every repository without per-repo files or git hooks, set up once at the user level:
88
+
89
+ ```bash
90
+ npm install -g jhste-skills
91
+ jhste-skills global
92
+ ```
93
+
94
+ This copies the skills (and shared companion resources) to `~/.jhste/skills` and writes a marker-managed bridge block into each agent's global instruction file (`~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, `~/.config/opencode/AGENTS.md`), creating them if needed. No git hooks and no per-repo files are written; guard stays advisory (`jhste-skills guard --scope changed`). Choose agents with `--agents codex,claude,opencode`, and remove everything with `jhste-skills global --uninstall`.
95
+
96
+ After this one-time global setup, later `npm update -g jhste-skills` runs a safe global-only refresh of the managed skill copies and existing managed global bridge blocks. Re-run `jhste-skills global` when you want to change agents or options.
97
+
98
+ The default (per-repo) install uses Normal mode.
86
99
 
87
100
  - Installs all bundled skills: jhste core skills + vendored workflow skills.
88
101
  - Creates `.jhste/profile.yaml` when missing; `--force` refreshes generated/managed profiles, while modified profiles require `--force --allow-profile-overwrite`.
@@ -165,6 +178,7 @@ These are the jhste-authored guardrail skills. They are installed by default as
165
178
  | Skill | Use it when | What it helps reduce |
166
179
  |---|---|---|
167
180
  | [`setup`](skills/setup/SKILL.md)<br>A safe setup skill that prevents install/connect/update flows from overwriting existing project instructions | Installing or connecting the kit to a repository | Unsafe overwrite, unmanaged hook conflict, repo instruction replacement |
181
+ | [`ask-jhste`](skills/ask-jhste/SKILL.md)<br>A user-invoked router for choosing the right jhste skill or workflow | You are not sure which jhste skill or workflow to use next | Wrong workflow selection, unnecessary always-on context, accidental side effects from routing |
168
182
  | [`jhste-engineering-groundwork`](skills/jhste-engineering-groundwork/SKILL.md)<br>A pre-change groundwork skill that verifies goal, premise, scope, boundary, failure path, and final behavior predicates before code edits | Before non-trivial code changes | Blind agreement, scope creep, unverified assumptions, unclear boundaries |
169
183
  | [`jhste-code-quality`](skills/jhste-code-quality/SKILL.md)<br>A code-quality skill for input validation, observable failure handling, secret-safe logging, and oversized-file review | Touching external input, failure handling, logging, env/config, cleanup/search-replace, or code-quality review paths | Unvalidated input, silent failure, secret logging, unsafe broad cleanup, oversized files |
170
184
  | [`jhste-architecture-review`](skills/jhste-architecture-review/SKILL.md)<br>An architecture review skill for module boundaries, side-effect placement, and SOLID-informed design risks | Changing module boundaries, app structure, side-effect placement, or responsibility splits | Pass-through abstraction, mixed responsibility, side-effect leakage |
@@ -274,4 +288,6 @@ See [`docs/ACCEPTANCE_CHECK.md`](docs/ACCEPTANCE_CHECK.md) for release acceptanc
274
288
 
275
289
  Fast agents need guardrails. `jhste-skills` gives them a repo-respecting engineering workflow.
276
290
 
291
+ Skills share cross-cutting doctrine (SOLID lens, evidence discipline, issue-candidate protocol, scope discipline) from `skills/_shared/`. Directories under `skills/` whose name starts with `_` are shared companion resources, not skills: they are excluded from skill listing, selection, and missing-skill checks, but are copied alongside the skills whenever any skill is installed so cross-skill `../_shared/...` references never dangle in the installed artifact.
292
+
277
293
  Installed skill directories are tracked with `.jhste-skills-manifest.json`. `--force` refreshes manifest-managed skill copies and generated/managed profiles; modified profiles need `--force --allow-profile-overwrite`; overwriting unmanaged differing skill directories still requires the separate `--allow-unmanaged-skill-overwrite` flag after review. `sync` and `update` can also adopt additional known jhste skills into an already managed skills directory so older mixed installs can be reconciled without a manual overwrite flag. Legacy managed renames are also reconciled during `sync` and `update`, so older managed installs that still have `diagnose` or `jhste-engineering-judgment` are migrated to `diagnosing-bugs` or `jhste-engineering-groundwork`. Managed copies of retired `write-a-skill` are removed rather than kept as a compatibility fallback.
package/README.zh.md CHANGED
@@ -81,6 +81,19 @@ jhste-skills install
81
81
 
82
82
  如果只想临时运行一次,请使用 `npx`。如果希望把 `jhste-skills` 作为常用 shell 命令,请使用 `npm install -g`。
83
83
 
84
+ ### Global setup(Codex + Claude Code + OpenCode,advisory-only)
85
+
86
+ 如果你希望在所有仓库中使用 skills,但不写入每个仓库的文件或 git hooks,可以在用户级别设置一次:
87
+
88
+ ```bash
89
+ npm install -g jhste-skills
90
+ jhste-skills global
91
+ ```
92
+
93
+ 该命令会把 skills 和 shared companion resources 复制到 `~/.jhste/skills`,并在每个 agent 的 global instruction file(`~/.claude/CLAUDE.md`、`~/.codex/AGENTS.md`、`~/.config/opencode/AGENTS.md`)中写入 marker-managed bridge block;文件不存在时会创建。它不会写入 git hooks 或每仓库文件;guard 保持 advisory(`jhste-skills guard --scope changed`)。可用 `--agents codex,claude,opencode` 选择 agent,用 `jhste-skills global --uninstall` 移除。
94
+
95
+ 完成这一次 global setup 后,之后运行 `npm update -g jhste-skills` 会安全刷新 managed skill copies 和已有 managed global bridge blocks。只有在需要更改 agent 或选项时才重新运行 `jhste-skills global`。
96
+
84
97
  默认安装使用 Normal mode。
85
98
 
86
99
  - 安装全部 bundled skills:jhste core skills + vendored workflow skills。
@@ -162,6 +175,7 @@ Custom - 通过面向效果的问题自定义安装范围
162
175
  | Skill | 何时使用 | 帮助减少什么 |
163
176
  |---|---|---|
164
177
  | [`setup`](skills/setup/SKILL.md)<br>安全安装 skill,避免 install/connect/update 覆盖现有项目说明 | 安装 kit 或连接仓库时 | Unsafe overwrite, unmanaged hook conflict, repo instruction replacement |
178
+ | [`ask-jhste`](skills/ask-jhste/SKILL.md)<br>用于选择正确 jhste skill 或 workflow 的 user-invoked router | 不确定下一步该用哪个 jhste skill/workflow 时 | Wrong workflow selection, unnecessary always-on context, accidental side effects from routing |
165
179
  | [`jhste-engineering-groundwork`](skills/jhste-engineering-groundwork/SKILL.md)<br>pre-change groundwork skill,在改代码前验证目标、前提、scope、boundary 和 failure path | non-trivial code change 前 | Blind agreement, scope creep, unverified assumption, unclear boundary |
166
180
  | [`jhste-code-quality`](skills/jhste-code-quality/SKILL.md)<br>检查 input validation、observable failure handling、secret-safe logging 和 oversized-file review 的 skill | 触及 external input、failure handling、logging、env/config 或 code-quality review path 时 | Unvalidated input, silent failure, secret logging, oversized file |
167
181
  | [`jhste-architecture-review`](skills/jhste-architecture-review/SKILL.md)<br>检查 module boundary、side-effect placement 和 SOLID-informed design risk 的架构 review skill | 修改 module boundary、app structure、side-effect placement 或 responsibility split 时 | Pass-through abstraction, mixed responsibility, side-effect leakage |
@@ -270,3 +284,5 @@ Release acceptance notes 请参考 [`docs/ACCEPTANCE_CHECK.md`](docs/ACCEPTANCE_
270
284
  - 在称 non-trivial work 完成前执行 red-team code review。
271
285
 
272
286
  快速的 agent 需要 guardrail。`jhste-skills` 为它们提供 repo-respecting engineering workflow。
287
+
288
+ Skills 共享来自 `skills/_shared/` 的 cross-cutting doctrine(SOLID lens、evidence discipline、issue-candidate protocol、scope discipline)。`skills/` 下名称以 `_` 开头的目录是 shared companion resources,不是 skills:它们会从 skill listing、selection 和 missing-skill checks 中排除,但安装任何 skill 时都会一起复制,确保已安装 artifact 中的 cross-skill `../_shared/...` references 不会悬空。
package/cli/global.mjs ADDED
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { ask, parseArgs } from './shared.mjs';
5
+ import { installSkills, removeManagedSkills } from './install-actions/skills.mjs';
6
+ import { removeManagedGlobalBridge, writeManagedGlobalBridge } from './install-actions/bridge-writer.mjs';
7
+
8
+ // Agent-neutral global setup: one canonical skills dir + a marker-managed bridge in
9
+ // each agent's global instruction file. Advisory only — no per-repo files, no git hooks.
10
+ const AGENT_TARGETS = {
11
+ claude: path.join(os.homedir(), '.claude', 'CLAUDE.md'),
12
+ codex: path.join(os.homedir(), '.codex', 'AGENTS.md'),
13
+ opencode: path.join(os.homedir(), '.config', 'opencode', 'AGENTS.md'),
14
+ };
15
+
16
+ function usage() {
17
+ console.log(`jhste-skills global
18
+ Install jhste skills once at the user level for Codex and Claude Code. Advisory only.
19
+
20
+ Usage:
21
+ jhste-skills global [--agents codex,claude,opencode] [--skill-set core|all] [--skills-dir <path>] [--force] [--yes]
22
+ jhste-skills global --uninstall [--agents codex,claude,opencode] [--skills-dir <path>] [--keep-skills] [--yes]
23
+
24
+ Notes:
25
+ Skills (and shared companion resources) are copied to --skills-dir (default ~/.jhste/skills).
26
+ A marker-managed bridge block is written to each agent's global instruction file:
27
+ claude -> ~/.claude/CLAUDE.md
28
+ codex -> ~/.codex/AGENTS.md
29
+ opencode -> ~/.config/opencode/AGENTS.md
30
+ Manifest-managed skills are refreshed automatically; --force also overwrites
31
+ unmanaged differing directories after review.
32
+ No git hooks and no per-repo files are written; guard stays advisory.
33
+ Use --claude-file / --codex-file / --opencode-file to override a target path.
34
+ --yes skips the confirmation prompt.
35
+ `);
36
+ }
37
+
38
+ function fail(message) {
39
+ console.error(`jhste-skills global: ${message}`);
40
+ process.exit(1);
41
+ }
42
+
43
+ function parseAgents(value, errors) {
44
+ if (value === undefined) return ['codex', 'claude', 'opencode'];
45
+ if (typeof value !== 'string') {
46
+ errors.push('--agents requires a comma-separated value.');
47
+ return ['codex', 'claude', 'opencode'];
48
+ }
49
+ const list = String(value).split(',').map((item) => item.trim().toLowerCase()).filter(Boolean);
50
+ const unknown = list.filter((name) => !Object.prototype.hasOwnProperty.call(AGENT_TARGETS, name));
51
+ if (unknown.length) errors.push(`--agents supports only codex,claude,opencode; unknown: ${unknown.join(', ')}`);
52
+ return list.length ? [...new Set(list)] : ['codex', 'claude', 'opencode'];
53
+ }
54
+
55
+ function agentTargets(agents, args) {
56
+ const overrides = { claude: args['claude-file'], codex: args['codex-file'], opencode: args['opencode-file'] };
57
+ return agents.map((agent) => ({
58
+ agent,
59
+ file: typeof overrides[agent] === 'string' ? path.resolve(overrides[agent]) : AGENT_TARGETS[agent],
60
+ }));
61
+ }
62
+
63
+ function readBoolean(args, key, errors) {
64
+ if (!Object.prototype.hasOwnProperty.call(args, key)) return false;
65
+ if (args[key] !== true) errors.push(`--${key} does not take a value.`);
66
+ return args[key] === true;
67
+ }
68
+
69
+ function readString(args, key, errors) {
70
+ if (!Object.prototype.hasOwnProperty.call(args, key)) return undefined;
71
+ if (typeof args[key] !== 'string') {
72
+ errors.push(`--${key} requires a value.`);
73
+ return undefined;
74
+ }
75
+ return args[key];
76
+ }
77
+
78
+ async function confirmGlobalWrite(args, action, targets, skillsDir) {
79
+ if (args.yes === true || args.y === true) return true;
80
+ console.log(`jhste-skills global ${action}:`);
81
+ console.log(` skills: ${skillsDir}`);
82
+ for (const { agent, file } of targets) console.log(` ${agent}: ${file}`);
83
+ if (!process.stdin.isTTY) {
84
+ console.error(`Non-interactive global ${action} requires explicit --yes or -y; refusing to change files.`);
85
+ process.exit(3);
86
+ }
87
+ const answer = await ask(`Continue global ${action}? [y/N] `);
88
+ if (answer.toLowerCase() === 'y') return true;
89
+ console.log(`Global ${action} cancelled. No changes made.`);
90
+ return false;
91
+ }
92
+
93
+ async function main(argv) {
94
+ const args = parseArgs(argv);
95
+ if (args.help || args.h) return usage();
96
+ const errors = [];
97
+ const supported = new Set(['agents', 'skill-set', 'skills-dir', 'claude-file', 'codex-file', 'opencode-file', 'force', 'yes', 'y', 'uninstall', 'keep-skills', 'help', 'h', '_']);
98
+ for (const key of Object.keys(args)) {
99
+ if (!supported.has(key)) errors.push(`unknown option --${key}.`);
100
+ }
101
+ if (Array.isArray(args._) && args._.length) errors.push(`unexpected positional argument: ${args._[0]}`);
102
+
103
+ const uninstall = readBoolean(args, 'uninstall', errors);
104
+ const keepSkills = readBoolean(args, 'keep-skills', errors);
105
+ const force = readBoolean(args, 'force', errors);
106
+ readBoolean(args, 'yes', errors);
107
+ readBoolean(args, 'y', errors);
108
+ const skillSetRaw = readString(args, 'skill-set', errors);
109
+ const skillSet = skillSetRaw ? skillSetRaw.toLowerCase() : 'all';
110
+ if (!['core', 'all'].includes(skillSet)) errors.push('global --skill-set must be core or all because the global bridge requires core workflow skills.');
111
+ const skillsDirInput = readString(args, 'skills-dir', errors);
112
+ readString(args, 'claude-file', errors);
113
+ readString(args, 'codex-file', errors);
114
+ readString(args, 'opencode-file', errors);
115
+ const skillsDir = path.resolve(skillsDirInput || path.join(os.homedir(), '.jhste', 'skills'));
116
+ const agents = parseAgents(args.agents, errors);
117
+ if (errors.length) return fail(errors.join('\n'));
118
+
119
+ const targets = agentTargets(agents, args);
120
+ if (!(await confirmGlobalWrite(args, uninstall ? 'uninstall' : 'install', targets, skillsDir))) return;
121
+
122
+ if (uninstall) {
123
+ console.log('jhste-skills global uninstall:');
124
+ if (!keepSkills) {
125
+ const result = removeManagedSkills(skillsDir);
126
+ const removed = result.skills?.filter((item) => item.status === 'removed').length ?? 0;
127
+ console.log(` skills: ${result.status}${result.status === 'removed-managed' ? ` (${removed} removed from ${skillsDir})` : ''}`);
128
+ if (result.status === 'invalid-manifest') {
129
+ console.log(` blocked: ${result.reason || result.status} (${result.path})`);
130
+ console.log(' bridges: skipped because skills uninstall did not complete');
131
+ process.exitCode = 3;
132
+ return;
133
+ }
134
+ } else {
135
+ console.log(` skills: kept (${skillsDir})`);
136
+ }
137
+ for (const { agent, file } of targets) {
138
+ const result = removeManagedGlobalBridge(file);
139
+ console.log(` ${agent}: ${result.status} (${file})`);
140
+ }
141
+ return;
142
+ }
143
+
144
+ // `global` owns the canonical skills dir, so it refreshes manifest-managed skills
145
+ // automatically (idempotent: unchanged when identical) and adopts known jhste skills,
146
+ // while still protecting unmanaged differing directories unless --force is given.
147
+ const skillResults = installSkills(skillsDir, { force: true, adoptKnownSkills: true, allowUnmanagedOverwrite: force, skillSet });
148
+ const blocked = skillResults.filter((item) => ['skipped-unmanaged-different', 'invalid-manifest'].includes(item.status));
149
+ const copied = skillResults.filter((item) => ['created', 'overwritten-managed', 'adopted-managed', 'unchanged'].includes(item.status)).length;
150
+
151
+ console.log('jhste-skills global install:');
152
+ console.log(` skills: ${copied} managed under ${skillsDir}${blocked.length ? ` (${blocked.length} skipped; rerun with --force after review)` : ''}`);
153
+ if (blocked.length) {
154
+ for (const item of blocked) {
155
+ console.log(` blocked: ${item.reason || item.status} (${item.destination || item.source})`);
156
+ }
157
+ console.log(' bridges: skipped because skills install did not complete');
158
+ process.exitCode = 3;
159
+ return;
160
+ }
161
+ for (const { agent, file } of targets) {
162
+ const result = writeManagedGlobalBridge(file);
163
+ console.log(` ${agent}: ${result.status} (${file})`);
164
+ }
165
+ console.log('\nAdvisory only: no git hooks and no per-repo files were written.');
166
+ console.log('Run guard manually anytime: jhste-skills guard --scope changed');
167
+ }
168
+
169
+ main(process.argv.slice(2));
package/cli/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url';
5
5
 
6
6
  const cliDir = path.dirname(fileURLToPath(import.meta.url));
7
7
  const [command, ...rest] = process.argv.slice(2);
8
- const commands = new Set(['install', 'connect', 'sync', 'update', 'deep-scan', 'guard', 'hooks', 'tune', 'baseline', 'uninstall']);
8
+ const commands = new Set(['install', 'connect', 'global', 'sync', 'update', 'deep-scan', 'guard', 'hooks', 'tune', 'baseline', 'uninstall']);
9
9
 
10
10
  if (!command || command === '--help' || command === '-h') {
11
11
  console.log(`jhste-skills
@@ -13,6 +13,7 @@ if (!command || command === '--help' || command === '-h') {
13
13
  Usage:
14
14
  jhste-skills install [--mode minimal|normal|full|custom] [--yes] [--repo <path>]
15
15
  jhste-skills connect [--mode normal|full|custom] [--repo <path>]
16
+ jhste-skills global [--agents codex,claude,opencode] [--skill-set core|all] [--uninstall]
16
17
  jhste-skills sync [--repo <path>] [--skills-dir <path>] [--yes]
17
18
  jhste-skills update [--repo <path>] [--skills-dir <path>] [--yes]
18
19
  jhste-skills deep-scan [--repo <path>]
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { BRIDGE_BLOCK, BRIDGE_END, BRIDGE_START, MANAGED_BRIDGE_BLOCK, readIfExists } from '../shared.mjs';
3
+ import { BRIDGE_BLOCK, BRIDGE_END, BRIDGE_START, MANAGED_BRIDGE_BLOCK, MANAGED_GLOBAL_BRIDGE_BLOCK, ensureDir, readIfExists } from '../shared.mjs';
4
4
 
5
5
  function escapeRegExp(value) {
6
6
  return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -27,6 +27,43 @@ export function bridgeStatus(repoRoot, fileName) {
27
27
  return { fileName, path: target, status: 'will-append-managed' };
28
28
  }
29
29
 
30
+ // Write/refresh the agent-neutral global bridge in an absolute instruction file,
31
+ // creating parent directories and the file when absent. Marker-managed and idempotent.
32
+ export function writeManagedGlobalBridge(targetPath) {
33
+ ensureDir(path.dirname(targetPath));
34
+ const existing = readIfExists(targetPath);
35
+ if (existing === null) {
36
+ fs.writeFileSync(targetPath, `${MANAGED_GLOBAL_BRIDGE_BLOCK}\n`);
37
+ return { status: 'created', path: targetPath };
38
+ }
39
+ if (existing.includes(BRIDGE_START) && existing.includes(BRIDGE_END)) {
40
+ const pattern = new RegExp(`${escapeRegExp(BRIDGE_START)}[\\s\\S]*?${escapeRegExp(BRIDGE_END)}`);
41
+ const updated = existing.replace(pattern, MANAGED_GLOBAL_BRIDGE_BLOCK);
42
+ if (updated === existing) return { status: 'unchanged', path: targetPath };
43
+ fs.writeFileSync(targetPath, updated);
44
+ return { status: 'updated-managed', path: targetPath };
45
+ }
46
+ const prefix = existing.endsWith('\n') ? existing : `${existing}\n`;
47
+ fs.writeFileSync(targetPath, `${prefix}\n${MANAGED_GLOBAL_BRIDGE_BLOCK}\n`);
48
+ return { status: 'appended-managed', path: targetPath };
49
+ }
50
+
51
+ // Remove a marker-managed bridge block from an absolute instruction file. Deletes the
52
+ // file only if it becomes empty and was created solely for the bridge.
53
+ export function removeManagedGlobalBridge(targetPath) {
54
+ const existing = readIfExists(targetPath);
55
+ if (existing === null) return { status: 'absent', path: targetPath };
56
+ if (!(existing.includes(BRIDGE_START) && existing.includes(BRIDGE_END))) return { status: 'no-managed-block', path: targetPath };
57
+ const pattern = new RegExp(`\\n?${escapeRegExp(BRIDGE_START)}[\\s\\S]*?${escapeRegExp(BRIDGE_END)}\\n?`);
58
+ const updated = existing.replace(pattern, '\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n');
59
+ if (updated.trim() === '') {
60
+ fs.rmSync(targetPath, { force: true });
61
+ return { status: 'removed-empty-file', path: targetPath };
62
+ }
63
+ fs.writeFileSync(targetPath, updated);
64
+ return { status: 'removed-managed-block', path: targetPath };
65
+ }
66
+
30
67
  export function writeManagedBridge(repoRoot, fileName) {
31
68
  const target = path.join(repoRoot, fileName);
32
69
  const existing = readIfExists(target);
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { directoryDigest, ensureDir, KIT_ROOT, listDirectories, nowIso } from '../shared.mjs';
3
+ import { directoryDigest, ensureDir, KIT_ROOT, listSharedResourceNames, listSkillDirectories, nowIso } from '../shared.mjs';
4
4
  import { readJsonFile, validateJsonObject, validateStringArray } from '../json-file.mjs';
5
5
 
6
6
  export const SKILLS_MANIFEST_NAME = '.jhste-skills-manifest.json';
@@ -26,7 +26,7 @@ function vendoredSkillNames() {
26
26
 
27
27
  export function skillNamesForSet(skillSet) {
28
28
  const sourceRoot = path.join(KIT_ROOT, 'skills');
29
- const all = listDirectories(sourceRoot).filter((name) => !Object.prototype.hasOwnProperty.call(LEGACY_SKILL_RENAMES, name));
29
+ const all = listSkillDirectories(sourceRoot).filter((name) => !Object.prototype.hasOwnProperty.call(LEGACY_SKILL_RENAMES, name));
30
30
  const vendored = vendoredSkillNames();
31
31
  if (skillSet === 'all') return all;
32
32
  if (skillSet === 'vendor') return all.filter((name) => vendored.has(name));
@@ -44,6 +44,58 @@ export function installedSkillStatus(skillsDir, skillSet) {
44
44
  return { expected, missing };
45
45
  }
46
46
 
47
+ // Single source for manifest-based removal, shared by `uninstall` and `global`.
48
+ // Removes every manifest-managed directory (skills and shared companion resources
49
+ // such as `_shared`), then the manifest, then best-effort empty-dir cleanup.
50
+ export function loadInstalledManifest(skillsDir) {
51
+ const file = manifestPath(skillsDir);
52
+ if (!fs.existsSync(file)) return { manifest: null, path: file };
53
+ try {
54
+ const manifest = JSON.parse(fs.readFileSync(file, 'utf8'));
55
+ if (!manifest || typeof manifest !== 'object' || manifest.managed_by !== MANIFEST_MANAGED_BY || typeof manifest.skills !== 'object' || Array.isArray(manifest.skills)) {
56
+ return { invalid: true, path: file, reason: `${SKILLS_MANIFEST_NAME} is not a valid ${MANIFEST_MANAGED_BY} manifest.` };
57
+ }
58
+ return { manifest, path: file };
59
+ } catch (error) {
60
+ return { invalid: true, path: file, reason: error instanceof Error ? error.message : String(error) };
61
+ }
62
+ }
63
+
64
+ export function removeManagedSkills(skillsDir) {
65
+ const loaded = loadInstalledManifest(skillsDir);
66
+ if (loaded.invalid) return { status: 'invalid-manifest', path: loaded.path, reason: loaded.reason, skills: [] };
67
+ if (!loaded.manifest) return { status: 'no-manifest', path: loaded.path, skills: [] };
68
+ for (const name of Object.keys(loaded.manifest.skills || {})) {
69
+ if (!name || name !== path.basename(name) || name === '.' || name === '..') {
70
+ return {
71
+ status: 'invalid-manifest',
72
+ path: loaded.path,
73
+ reason: `${SKILLS_MANIFEST_NAME} contains an invalid managed skill name: ${name}`,
74
+ skills: [],
75
+ };
76
+ }
77
+ }
78
+ const skills = [];
79
+ for (const name of Object.keys(loaded.manifest.skills || {}).sort()) {
80
+ const dir = path.join(skillsDir, name);
81
+ if (!fs.existsSync(dir)) {
82
+ skills.push({ name, status: 'absent' });
83
+ continue;
84
+ }
85
+ fs.rmSync(dir, { recursive: true, force: true });
86
+ skills.push({ name, status: 'removed' });
87
+ }
88
+ fs.rmSync(loaded.path, { force: true });
89
+ try {
90
+ if (fs.existsSync(skillsDir) && fs.readdirSync(skillsDir).length === 0) fs.rmdirSync(skillsDir);
91
+ const parent = path.dirname(skillsDir);
92
+ if (fs.existsSync(parent) && fs.readdirSync(parent).length === 0) fs.rmdirSync(parent);
93
+ } catch {
94
+ // Empty-dir cleanup is best-effort only.
95
+ }
96
+ return { status: 'removed-managed', path: loaded.path, skills };
97
+ }
98
+
47
99
  function manifestPath(skillsDir) {
48
100
  return path.join(skillsDir, SKILLS_MANIFEST_NAME);
49
101
  }
@@ -201,6 +253,7 @@ export function installSkills(skillsDir, {
201
253
  const sourceRoot = path.join(KIT_ROOT, 'skills');
202
254
  ensureDir(skillsDir);
203
255
  const selected = (Array.isArray(skillSet) ? skillSet : skillNamesForSet(skillSet)).map((name) => canonicalSkillName(name));
256
+ const shared = selected.length ? listSharedResourceNames(sourceRoot) : [];
204
257
  const currentManifest = loadSkillsManifest(skillsDir);
205
258
  if (currentManifest?.invalid) return [{ status: 'invalid-manifest', source: '', destination: manifestPath(skillsDir), reason: currentManifest.reason }];
206
259
  const nextManifest = currentManifest || { managed_by: MANIFEST_MANAGED_BY, version: packageVersion(), installed_at: nowIso(), skills: {} };
@@ -209,7 +262,7 @@ export function installSkills(skillsDir, {
209
262
  nextManifest.updated_at = nowIso();
210
263
  nextManifest.skills ||= {};
211
264
  const conflicts = force && !allowUnmanagedOverwrite
212
- ? unmanagedSkillConflicts(selected, sourceRoot, skillsDir, currentManifest, { adoptKnownSkills })
265
+ ? unmanagedSkillConflicts([...selected, ...shared], sourceRoot, skillsDir, currentManifest, { adoptKnownSkills })
213
266
  : [];
214
267
  if (conflicts.length) return conflicts;
215
268
  const legacyResults = removeLegacySkillDirectories(skillsDir, selected, currentManifest, nextManifest, {
@@ -224,6 +277,18 @@ export function installSkills(skillsDir, {
224
277
  manifest: currentManifest,
225
278
  nextManifest,
226
279
  }));
280
+ // Copy shared companion resources (e.g. `_shared/` doctrine) alongside skills so
281
+ // installed `../_shared/...` references never dangle. These are not skills and are
282
+ // excluded from skill enumeration/status, but reuse managed-copy semantics.
283
+ const sharedResults = shared.length
284
+ ? shared.map((name) => copyManagedSkill(path.join(sourceRoot, name), path.join(skillsDir, name), name, {
285
+ force,
286
+ allowUnmanagedOverwrite,
287
+ adoptKnownSkills,
288
+ manifest: currentManifest,
289
+ nextManifest,
290
+ }))
291
+ : [];
227
292
  writeSkillsManifest(skillsDir, nextManifest);
228
- return [...legacyResults, ...deletedResults, ...results];
293
+ return [...legacyResults, ...deletedResults, ...results, ...sharedResults];
229
294
  }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { installSkills } from './install-actions/skills.mjs';
6
+ import { writeManagedGlobalBridge } from './install-actions/bridge-writer.mjs';
7
+ import { BRIDGE_END, BRIDGE_START } from './shared.mjs';
8
+
9
+ const AGENT_TARGETS = {
10
+ claude: path.join(os.homedir(), '.claude', 'CLAUDE.md'),
11
+ codex: path.join(os.homedir(), '.codex', 'AGENTS.md'),
12
+ opencode: path.join(os.homedir(), '.config', 'opencode', 'AGENTS.md'),
13
+ };
14
+
15
+ function isGlobalNpmLifecycle() {
16
+ return process.env.npm_config_global === 'true' || process.env.npm_config_location === 'global';
17
+ }
18
+
19
+ function hasManagedBridge(file) {
20
+ if (!fs.existsSync(file)) return false;
21
+ const text = fs.readFileSync(file, 'utf8');
22
+ return text.includes(BRIDGE_START) && text.includes(BRIDGE_END);
23
+ }
24
+
25
+ function main() {
26
+ if (!isGlobalNpmLifecycle()) return;
27
+ const skillsDir = path.join(os.homedir(), '.jhste', 'skills');
28
+ const manifestExists = fs.existsSync(path.join(skillsDir, '.jhste-skills-manifest.json'));
29
+ const bridgeTargets = Object.entries(AGENT_TARGETS)
30
+ .filter(([, file]) => hasManagedBridge(file))
31
+ .map(([agent, file]) => ({ agent, file }));
32
+
33
+ // Do not create a new global setup from an npm lifecycle hook. Only refresh a
34
+ // global setup the user already opted into with `jhste-skills global`.
35
+ if (!manifestExists && bridgeTargets.length === 0) return;
36
+
37
+ const skillResults = installSkills(skillsDir, {
38
+ force: true,
39
+ adoptKnownSkills: true,
40
+ allowUnmanagedOverwrite: false,
41
+ skillSet: 'all',
42
+ });
43
+ const blocked = skillResults.filter((item) => ['skipped-unmanaged-different', 'invalid-manifest'].includes(item.status));
44
+ if (blocked.length) {
45
+ console.warn(`jhste-skills postinstall: global skill refresh skipped (${blocked.length} blocked); run jhste-skills global --force after review.`);
46
+ return;
47
+ }
48
+
49
+ for (const { file } of bridgeTargets) writeManagedGlobalBridge(file);
50
+ console.log(`jhste-skills postinstall: refreshed global skills${bridgeTargets.length ? ` and ${bridgeTargets.length} global bridge(s)` : ''}.`);
51
+ }
52
+
53
+ try {
54
+ main();
55
+ } catch (error) {
56
+ console.warn(`jhste-skills postinstall: global refresh skipped: ${error instanceof Error ? error.message : String(error)}`);
57
+ }
@@ -25,6 +25,22 @@ export function listDirectories(dir) {
25
25
  .sort();
26
26
  }
27
27
 
28
+ // Directories under skills/ whose name starts with `_` are shared companion
29
+ // resources (doctrine/templates), not skills. They ship in the package and are
30
+ // copied alongside skills so cross-skill `../_shared/...` references resolve,
31
+ // but are excluded from skill enumeration, selection, and missing-skill checks.
32
+ export function isSharedResourceName(name) {
33
+ return name.startsWith('_');
34
+ }
35
+
36
+ export function listSkillDirectories(dir) {
37
+ return listDirectories(dir).filter((name) => !isSharedResourceName(name));
38
+ }
39
+
40
+ export function listSharedResourceNames(dir) {
41
+ return listDirectories(dir).filter((name) => isSharedResourceName(name));
42
+ }
43
+
28
44
  export function directoryDigest(dir) {
29
45
  const hash = crypto.createHash('sha256');
30
46
  if (!fs.existsSync(dir)) return null;
@@ -26,6 +26,27 @@ Do not enter an unbounded fix/review loop; stop after at most two fix + re-revie
26
26
 
27
27
  export const MANAGED_BRIDGE_BLOCK = `${BRIDGE_START}\n${BRIDGE_BLOCK}\n${BRIDGE_END}`;
28
28
 
29
+ // Agent-neutral variant written to a global instruction file (e.g. Claude
30
+ // ~/.claude/CLAUDE.md, Codex ~/.codex/AGENTS.md, OpenCode
31
+ // ~/.config/opencode/AGENTS.md). Shares the same markers so it is
32
+ // marker-managed (idempotent update/removal), but omits repo-only wording and the
33
+ // repo profile reference, and frames guard as advisory (no per-repo hooks).
34
+ export const GLOBAL_BRIDGE_BLOCK = `## Agent skills (jhste, global)
35
+ jhste skills are installed globally as shared engineering guidance for this agent.
36
+ Repo-local AGENTS.md, CLAUDE.md, and docs remain authoritative when present.
37
+ File, repo, command, issue, PR, or other external side effects are allowed when the user directly requested that workflow or repo-local standing approval covers it.
38
+ Ask for destructive, irreversible, ambiguous, production, secret, cost-bearing, broad existing-item, or out-of-scope changes.
39
+ For reversible in-scope choices, make a reasonable assumption, proceed, and report it in the final summary.
40
+ Before non-trivial code changes, use the \`jhste-engineering-groundwork\` skill to check scope, boundaries, failure paths, final behavior predicates, and assumptions.
41
+ For changed code, name the one main responsibility of each changed class, module, and function, and reject adjacent responsibilities unless they are on the changed execution path and prevent a concrete failure.
42
+ Use SOLID-informed coding discipline as a clean-code review lens for concrete failure modes, not a compliance claim or automatic abstraction trigger; guard findings are review candidates, not proof.
43
+ After code changes, run \`jhste-skills guard --scope changed --format text --fail-on warning\` when available; treat guard output as advisory review evidence, not proof.
44
+ Before declaring non-trivial code work complete, use the \`jhste-red-team-review\` skill.
45
+ Skip red-team review for docs-only, comment-only, formatting-only, or trivial rename-only changes.
46
+ Do not enter an unbounded fix/review loop; stop after at most two fix + re-review cycles and report remaining risks.`;
47
+
48
+ export const MANAGED_GLOBAL_BRIDGE_BLOCK = `${BRIDGE_START}\n${GLOBAL_BRIDGE_BLOCK}\n${BRIDGE_END}`;
49
+
29
50
  export const DEFAULT_PROFILE = `version: 1
30
51
  mode: advisory
31
52
  installed_at: "<installed_at>"