@devenius/junamis-skills 1.0.0-placeholder.0 → 1.1.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 (47) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/README.md +232 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +54 -45
  5. package/dist/cli.js.map +1 -1
  6. package/dist/color.d.ts +40 -0
  7. package/dist/color.d.ts.map +1 -0
  8. package/dist/color.js +55 -0
  9. package/dist/color.js.map +1 -0
  10. package/dist/design/bootstrap.d.ts +7 -1
  11. package/dist/design/bootstrap.d.ts.map +1 -1
  12. package/dist/design/bootstrap.js +48 -0
  13. package/dist/design/bootstrap.js.map +1 -1
  14. package/dist/library-install.d.ts.map +1 -1
  15. package/dist/library-install.js +8 -3
  16. package/dist/library-install.js.map +1 -1
  17. package/dist/scripts/check-opencode-config.d.ts.map +1 -1
  18. package/dist/scripts/check-opencode-config.js +28 -1
  19. package/dist/scripts/check-opencode-config.js.map +1 -1
  20. package/dist/scripts/check-pack.d.ts.map +1 -1
  21. package/dist/scripts/check-pack.js +1 -0
  22. package/dist/scripts/check-pack.js.map +1 -1
  23. package/dist/scripts/publish-npm.d.ts +3 -0
  24. package/dist/scripts/publish-npm.d.ts.map +1 -0
  25. package/dist/scripts/publish-npm.js +67 -0
  26. package/dist/scripts/publish-npm.js.map +1 -0
  27. package/dist/scripts/release-check.d.ts.map +1 -1
  28. package/dist/scripts/release-check.js +7 -3
  29. package/dist/scripts/release-check.js.map +1 -1
  30. package/docs/RECONCILE.md +505 -0
  31. package/junamis-skills.yml +34 -6
  32. package/library/HANDOFF.md +3 -3
  33. package/library/RESUME.md +1 -0
  34. package/library/SAFETY.md +2 -1
  35. package/library/agents/context-keeper.md +9 -1
  36. package/library/agents/frontend.md +9 -1
  37. package/library/agents/maintainer.md +9 -1
  38. package/library/agents/nestjs.md +9 -1
  39. package/library/agents/nextjs.md +9 -1
  40. package/library/agents/operator.md +11 -2
  41. package/library/agents/orchestrator.md +121 -41
  42. package/library/agents/researcher.md +9 -1
  43. package/library/agents/reviewer.md +9 -1
  44. package/library/skills/release-prep/SKILL.md +9 -1
  45. package/library/skills/route/SKILL.md +2 -0
  46. package/opencode.json +366 -1
  47. package/package.json +4 -2
package/CHANGELOG.md CHANGED
@@ -2,7 +2,47 @@
2
2
 
3
3
  All notable changes to this project are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
4
 
5
- ## [1.0.0-placeholder.0] - 2026-11-10
5
+ ## [Unreleased]
6
+
7
+ ### Added
8
+
9
+ ### Changed
10
+
11
+ ### Fixed
12
+
13
+ ## [1.1.0] - 2026-11-13
14
+
15
+ ### Added
16
+ - `.github/workflows/publish.yml` is renamed to `Publish` and runs two jobs: `publish-github` mirrors to GitHub Packages with `GITHUB_TOKEN` and `packages: write`, and `publish-npm` stages to npmjs with OIDC (`id-token: write`), the `npm-publish` environment, and `npm stage publish`. The npm job sets no `NODE_AUTH_TOKEN`; a maintainer approves the staged version on npmjs.com. Top-level permissions are `contents: read` and each job adds only what it needs.
17
+ - `pnpm publish:npm` (`scripts/publish-npm.mjs` over `src/scripts/publish-npm.ts`) is a read-only publish checklist. It validates the package name, `publishConfig.access`, and the absence of `publishConfig.registry`, then prints the exact `npm stage publish` command. It never runs a registry mutation.
18
+ - `src/color.ts` is a dependency-free ANSI helper (`paint`, `createColors`, `stripAnsi`, `colorsEnabled`). The `init` and `status` CLI output and the release gate use it: cyan headers, green success, yellow warnings, red errors, and gray details. Color disables itself for a non-TTY stream, `NO_COLOR`, or `TERM=dumb`, so captured output stays plain.
19
+ - Every orchestrator prompt starts with `git branch --show-current`. On `main` or a detached HEAD it runs `git fetch origin` and `git switch -c task/<task-slug> origin/main` (or `git switch -c task/<task-slug> main` when there is no remote) and continues on that branch. On a task branch it continues; it never works on `main`. The orchestrator commits on `task/<task-slug>` in the project checkout, where the state root (`<project>/.orchestrator/`) and the design registry (`<project>/docs/design/`) live.
20
+ - The orchestrator plans the delegation in `<state-root>/delegation.md` with one row per task: owner, depends on, worktree, branch, allowed files, and done proof. Every file has exactly one owner, and dependency order is explicit.
21
+ - Each delegated task runs in `<project>/.worktrees/<task-slug>-<subtask>` on `task/<task-slug>-<subtask>`, cut from the task branch. The brief names the absolute path, the branch, and the allowed files. The sub-agent edits only its allocated files, asks the orchestrator for any file outside the allocation, never creates a pull request, and may commit only its allocated files on its assigned sub-branch.
22
+ - When a sub-agent finishes, the orchestrator verifies the diff is a subset of the allowed files, runs the task checks, merges with `git merge --no-ff`, removes the worktree with `git worktree remove`, and deletes the merged sub-branch with `git branch -d`. On conflict it runs `git merge --abort`, stops, and asks the user.
23
+ - One pull request per approved batch, and orchestrator prompt version 2.0.0. After every task is merged and the full gate passes, the orchestrator asks the user to approve, pushes `task/<task-slug>`, opens the pull request with `gh pr create`, reports the URL, and stops. The user reviews and merges; the agent's authority still ends at `gh pr create`.
24
+ - Worktree limit is 8. Before creating one the orchestrator counts `git worktree list` entries under `<project-root>/.worktrees/` and prunes only a worktree that is merged into the task branch and clean, with `git worktree remove` (never `rm -rf`); if none qualifies it asks the user.
25
+ - `opencode.json` allows external directories under `/tmp/**`, `/var/tmp/**`, `.worktrees/**`, and `**/.worktrees/**`, and allows the worktree shell workflow (`git status`, `git diff`, `git fetch`, `git branch --show-current`, `git switch -c task/*`, `git checkout -b task/*`, `git merge --no-ff task/*`, `git merge task/*`, `git merge --abort`, `git worktree add|list|remove`, `git add`, `git commit`, `git push origin task/*`, `pnpm install|test|build|typecheck|lint`, `pnpm run check:*`, `node --test`, `npx tsc`, the `npx --package` fallbacks for `junamis-orchestrator`, `junamis-design-registry`, and `junamis-reconcile`, and the GitHub pull-request commands `gh pr create|view|list|checks|edit` plus `gh run list|view|watch`).
26
+ - The design registry bootstrap now always writes the project-wide map at `docs/design/other/project.md` (`surface_id: other/project`, kind `other`, `code_paths: []`, draft, owner `library/document`). It names the project, lists every discovered target as workspace, kind, surface_id, and code_paths, and is included in the bootstrap result and the regenerated `INDEX.md`. An existing map is never rewritten.
27
+ - `## Environment setup` in the orchestrator prompt. Before `boot`, `route`, or any dispatch the orchestrator resolves the project root as the user's cwd, checks the four installed library files, ensures the state CLI, checks the four required design registry files (`_schema.md`, `INDEX.md`, `_registry.json`, `other/project.md`), and bootstraps a missing registry only after asking once. A declined bootstrap continues with `design_context: none` and records the missing registry.
28
+ - `docs/RECONCILE.md` is installed as a shared doc next to `RESUME.md` and is published in the package file set.
29
+ - The orchestrator's authority now ends at `gh pr create` (prompt version 1.5.0). After pushing `task/<task-slug>` it opens the pull request, reports the URL, and stops. It never merges, never uses `--admin`, never pushes `main`, never force-pushes, never tags, and never rebases or resets `main`; every action that touches `main` belongs to the user, who reviews and merges.
30
+ - Approvals are single-use, stated in the orchestrator prompt, `AGENTS.md`, and `CONTRIBUTING.md`: one command in one prompt exactly once, ended when that command finishes or fails, never reused for a retry, a second command, a later step, another task, another pull request, or another session, and asked again every time.
31
+ - `.github/pull_request_template.md` checklist items record that the pull request was created by the agent and not merged by it, that `main` was not pushed, and that every approval used in the task was single-use.
32
+ - `test/orchestrator-policy.test.js` locks the PR-only authority and the single-use approval wording across the orchestrator prompt, `AGENTS.md`, `CONTRIBUTING.md`, and the pull request template.
33
+
34
+ ### Changed
35
+ - Hard-deny policies now also deny `shell:gh pr merge` (bare), `shell:git push origin main *`, `shell:git push origin HEAD:main`, and `shell:git push --force-with-lease *`. `git push origin task/*`, `git push -u origin task/*`, and `gh pr create *` stay allowed, and `HARD_DENY_POLICIES` in the config check mirrors the new denies.
36
+ - Hard-deny policies now also deny `shell:git switch main`, `shell:git checkout main`, `shell:git branch -d main`, and `shell:git branch -D main`, so the orchestrator cannot leave a task branch for `main`. `HARD_DENY_POLICIES` in the config check mirrors them, and `git switch -c task/*` and `git checkout -b task/*` stay allowed.
37
+ - Hard-deny policies replace the blanket `shell:git push *` with targeted denies: `shell:git push origin main`, `shell:git push --force *`, `shell:git push -f *`, `shell:git commit --no-verify *`, and `shell:gh pr merge *`. Pushing a `task/*` branch and opening or checking a pull request are allowed; every other hard deny is unchanged.
38
+ - The orchestrator never skips a state or design step because a bin is missing. It prefers `junamis-orchestrator` and `junamis-design-registry` on `PATH` and falls back to `npx --yes --package github:devenius/junamis-skills#main ...`; the package `prepare` script builds `dist/` for that fallback.
39
+ - The orchestrator must call `start` before every dispatch, `end` after it, and close every session with `checkpoint` plus `finish` or `lease release`. A task with no `start`/`end` is not a completed run.
40
+ - `RESUME.md` reference implementation documents the `npx --package` fallback for the state CLI; the normative contract is unchanged.
41
+
42
+ ### Fixed
43
+ - Git installs (`npx github:...`) now build `dist/` through the `prepare` script, so the bin can load the CLI.
44
+
45
+ ## [1.0.0] - 2026-11-10
6
46
 
7
47
  ### Added
8
48
  - `library/RESUME.md` as the normative persistence and resume contract. It is installed as a shared doc next to `HANDOFF.md`, `SAFETY.md`, `EXTERNAL.md`, and `SECURITY.md`.
package/README.md CHANGED
@@ -1 +1,232 @@
1
- "Placeholder release — not usable; install 1.1.0 or later."
1
+ # junamis-skills
2
+
3
+ An orchestrated set of Agent Skills and agents for AI coding tools. The package ships:
4
+
5
+ - **The library** (`library/`): the orchestrator, eight specialist agents, and the skills they use.
6
+ - **The manifest** (`skills-lock.json`): external skill sources, immutable commits, per-skill tree SHAs, and licenses.
7
+ - **A scan record** (`SECURITY.md`): per-skill kind, source, license, scan status, and last scan month.
8
+ - **An installer** (`junamis-skills`): one `init` command installs the library and every external skill, and writes a setup guide into your project.
9
+
10
+ ## Quick start
11
+
12
+ Run these in the project that will use the skills. Use `--dry-run` first to see what will change. Requires Node.js 26+; external installs also require Git.
13
+
14
+ The package name is `@devenius/junamis-skills`. The command-line binary it installs is `junamis-skills`. `npx @devenius/junamis-skills ...` runs the binary without a separate install.
15
+
16
+ ### Option A: Install from npmjs (primary)
17
+
18
+ ```bash
19
+ # 1. Preview the whole install, then apply it.
20
+ npx @devenius/junamis-skills@latest init --dry-run
21
+ npx @devenius/junamis-skills@latest init
22
+
23
+ # Or install the CLI globally and use the junamis-skills binary.
24
+ npm install -g @devenius/junamis-skills@latest
25
+
26
+ # 2. Check state.
27
+ npx @devenius/junamis-skills@latest status
28
+ ```
29
+
30
+ ### npmjs status
31
+
32
+ npmjs is the primary public registry. **Until `1.1.0` is published and approved on npmjs.com, `latest` still points at the `0.0.0-stage` placeholder**, so the commands above resolve to an empty placeholder, not a working installer. The first usable npm release is **`1.1.0`**.
33
+
34
+ - **Until `1.1.0` is live on npmjs**, use the GitHub direct install in Option B. It is the safe path today.
35
+ - **After `1.1.0` is live on npmjs**, the npmjs commands in Option A are primary and `latest` is `1.1.0`.
36
+
37
+ The placeholder was staged from a separate release branch; it is not a banner on `main`.
38
+
39
+ ### Option B: Run directly from GitHub (no setup, safe today)
40
+
41
+ ```bash
42
+ # 1. Preview the whole install, then apply it.
43
+ npx github:devenius/junamis-skills init --dry-run
44
+ npx github:devenius/junamis-skills init
45
+
46
+ # For Claude Code instead of OpenCode:
47
+ npx github:devenius/junamis-skills init --agent claude
48
+
49
+ # Install into the project instead of globally:
50
+ npx github:devenius/junamis-skills init --scope project
51
+
52
+ # 2. Check state.
53
+ npx github:devenius/junamis-skills status
54
+ ```
55
+
56
+ `init` writes the setup guide, writes `opencode.json` when it is missing (OpenCode only), installs the library, and installs every pinned external skill. Use `--force` to overwrite a guide, config, or library file you edited.
57
+
58
+ ### Option C: Install from GitHub Packages (mirror)
59
+
60
+ GitHub Packages is the mirror of npmjs. Configure `~/.npmrc` once (see [Installing from GitHub Packages](#installing-from-github-packages)), then use the normal npx form:
61
+
62
+ ```bash
63
+ npx @devenius/junamis-skills init --dry-run
64
+ npx @devenius/junamis-skills init
65
+ npx @devenius/junamis-skills status
66
+ ```
67
+
68
+ Then open the project in your agent and send your first request to `orchestrator`.
69
+
70
+ ## Commands
71
+
72
+ The CLI has two working commands and `help`.
73
+
74
+ | Command | What it does |
75
+ |---|---|
76
+ | `init [--agent opencode\|claude] [--scope global\|project] [--dir <path>] [--force] [--dry-run]` | In order: writes `junamis-skills-guide.md` and its marker; writes/refreshes `opencode.json` for OpenCode (only when missing, or with `--force`); installs the library; installs every pinned external skill non-interactively. Exits 1 only when an external install fails. |
77
+ | `status [--dir <path>] [--scope global\|project\|all]` | Reports the guide, marker, per-target internal/external skill counts, canonical agents and link state, legacy leftovers (informational), library manifest validity, `opencode.json` validity, external skill coverage, and the `SECURITY.md` scan record. Exits 1 only when a detected target's manifest or config is invalid, or project JSON is malformed. Default scope: `global` (matching `init`). |
78
+ | `help` | Prints usage. |
79
+
80
+ ## Supported agents
81
+
82
+ | Agent (`--agent`) | External skills directory | Internal (library) skills directory | Agent definitions (canonical → link) | External install support |
83
+ |---|---|---|---|---|
84
+ | `opencode` | `~/.agents/skills` | `~/.config/junamis/skills/skills` | `~/.config/junamis/skills/agents` → `~/.config/opencode/agents` | `skillsio` |
85
+ | `claude` | `~/.claude/skills` | `~/.claude/skills` | `~/.config/junamis/skills/agents` → `~/.claude/agents` | `skillsio` |
86
+ | `cursor` | `~/.cursor/skills` | `~/.cursor/skills` | `~/.config/junamis/skills/agents` → `~/.cursor/agents` | Library only |
87
+ | `windsurf` | `~/.codeium/windsurf/skills` | `~/.codeium/windsurf/skills` | `~/.config/junamis/skills/agents` → `~/.config/devin/agents` | Library only |
88
+ | `cline` | `~/.cline/skills` | `~/.cline/skills` | `~/.config/junamis/skills/agents` → `~/.cline/agents` | Library only |
89
+
90
+ Agent definitions are written once to the canonical directory and symlinked into the target's agents directory. If a link cannot be created (for example Windows without developer mode), the file is copied instead and a warning is printed.
91
+
92
+ For OpenCode, the shipped `opencode.json` registers both skill roots, so internal and external skills resolve side by side. Other targets keep a single skills directory shared by both kinds.
93
+
94
+ The shipped `opencode.json` uses OpenCode V2 keys. It enables the built-in file tools and local skills, keeps shell and network fetches behind `ask`, denies secret paths and `external_directory`, hard-denies destructive shell verbs, and ships no MCP servers or third-party plugins. Add MCP servers or plugins only after a reviewed decision.
95
+
96
+ Library installation supports all five targets. Scanned external installation (`init`) currently accepts only OpenCode and Claude Code.
97
+ The Windsurf target keeps project skills in the legacy `.windsurf/skills` directory and installs subagents using Devin's current `.devin/agents` format and locations.
98
+
99
+ Other agents are added on request, one target file each under `src/targets/`.
100
+
101
+ ## Where things go
102
+
103
+ - External skills: `<external skills directory>/<name>/SKILL.md`
104
+ - Internal (library) skills: `<internal skills directory>/<name>/SKILL.md`
105
+ - Canonical agents: `~/.config/junamis/skills/agents/<name>.md`
106
+ - Agent links: `<target agents directory>/<name>.md` → the canonical file
107
+ - Shared rules the agents read: `~/.config/junamis/skills/` (`HANDOFF.md`, `SAFETY.md`, `EXTERNAL.md`, `SECURITY.md`, `RESUME.md`, `RECONCILE.md`)
108
+ - Installed manifest for the state engine: `~/.config/junamis/skills/skills-lock.json`
109
+ - Install record: `~/.config/junamis/skills/installed/library-<agent>.json`
110
+ - Project scope: `<project>/.config/junamis/skills/{skills,agents,*.md,installed}` mirrors the internal side. External skills stay at the target's project skills directory, and project agent links stay at its project agents directory.
111
+ - Session state and the guide marker stay in `.junamis/` (`handoff.md`, `junamis-skills.json`).
112
+
113
+ For OpenCode the split is: internal skills in `~/.config/junamis/skills/skills`, canonical agents in `~/.config/junamis/skills/agents`, shared docs in `~/.config/junamis/skills/*.md`, the install manifest in `~/.config/junamis/skills/installed/library-opencode.json`, links in `~/.config/opencode/agents`, and external skills in `~/.agents/skills`. Other targets keep one skills directory for both kinds.
114
+
115
+ `init` writes only the nested layout. There is no automatic migration: older flat files under `~/.config/junamis/` and flat skill folders directly under `~/.config/junamis/skills/` are reported by `status` as legacy leftovers and are never deleted. Move or remove them yourself once the new install is verified.
116
+
117
+ ## Persistence and resume
118
+
119
+ The orchestrator is resumable. Every session starts by rehydrating execution state from disk, before `route` or any skill dispatch:
120
+
121
+ ```bash
122
+ junamis-orchestrator boot --project <cwd> --session <new id>
123
+ ```
124
+
125
+ The `junamis-orchestrator` bin also exposes `plan set`, `plan show`, `start`, `end`, `resolve`, `status`, `checkpoint`, `lease acquire|heartbeat|release|status`, and `finish`.
126
+
127
+ - **State root.** The default is `<project>/.orchestrator/` (gitignored). It holds `journal.ndjson` (the source of truth), `state.json`, `plan.json`, `tasks/<task_id>/`, `checkpoints/`, `locks/`, `resume-report.md`, and the generated `STATE.md` view. `ORCH_STATE_ROOT` overrides the location. The root must be persistent and host-local, never inside the operating system temp directory.
128
+ - **Report first.** `boot` writes `resume-report.md`. Read it in full. Its status is `NEW_RUN`, `RESUMING`, `COMPLETE`, `BLOCKED_DRIFT`, or `HALT_CORRUPT`. Exit codes are `0` success, `1` usage or I/O, `2` lease held or lost, `3` drift, `4` corrupt state, and `5` invalid transition or handoff. On a non-zero exit, stop and report the halt line and report path; dispatch nothing.
129
+ - **Single writer.** A lease under `locks/` prevents two sessions from writing one state root. A stale lease is taken over; a live lease halts the second session.
130
+ - **Drift gate.** `skills-lock.json` and the library override map are fingerprinted at plan time. A manifest or `library/` change makes boot report `BLOCKED_DRIFT` and refuse to dispatch.
131
+ - **Idempotent or escalate.** Every library skill declares `idempotent: true | false` in its frontmatter. An interrupted `true` step is retried; an interrupted `false` step becomes `needs_review` and waits for a user decision. An external skill records the value in its adapter and defaults to `false` when upstream does not declare one.
132
+ - **Contract.** `RESUME.md` is installed with `HANDOFF.md`, `SAFETY.md`, `EXTERNAL.md`, `SECURITY.md`, and `RECONCILE.md` at the config root and is the normative persistence contract. `RECONCILE.md` is the work-queue contract that decides what runs next.
133
+
134
+ The state engine lives under `src/orchestrator/`, uses only Node 26 built-in modules, and adds no runtime dependency.
135
+
136
+ ## External skills (manifest)
137
+
138
+ These are installed from `skills-lock.json` into the agent's global skills directory.
139
+
140
+ | Skill | Source | License |
141
+ |---|---|---|
142
+ | `agent-browser` | [vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser) | Apache-2.0 |
143
+ | `agent-rules` | [netresearch/agent-rules-skill](https://github.com/netresearch/agent-rules-skill) | MIT OR CC-BY-SA-4.0 |
144
+ | `architect` | [JavaScript-Mastery-Pro/skills](https://github.com/JavaScript-Mastery-Pro/skills) | MIT |
145
+ | `check` | [JavaScript-Mastery-Pro/skills](https://github.com/JavaScript-Mastery-Pro/skills) | MIT |
146
+ | `debug` | [JavaScript-Mastery-Pro/skills](https://github.com/JavaScript-Mastery-Pro/skills) | MIT |
147
+ | `develop` | [JavaScript-Mastery-Pro/skills](https://github.com/JavaScript-Mastery-Pro/skills) | MIT |
148
+ | `doc-coauthoring` | [anthropics/skills](https://github.com/anthropics/skills) | Apache-2.0 |
149
+ | `document` | [JavaScript-Mastery-Pro/skills](https://github.com/JavaScript-Mastery-Pro/skills) | MIT |
150
+ | `driftlock` | [KairosSignal/driftlock-agent-docs](https://github.com/KairosSignal/driftlock-agent-docs) | MIT |
151
+ | `find-skills` | [vercel-labs/skills](https://github.com/vercel-labs/skills) | MIT |
152
+ | `frontend-design` | [anthropics/skills](https://github.com/anthropics/skills) | Apache-2.0 |
153
+ | `grill-me` | [mattpocock/skills](https://github.com/mattpocock/skills) | MIT |
154
+ | `grill-with-docs` | [mattpocock/skills](https://github.com/mattpocock/skills) | MIT |
155
+ | `mcp-builder` | [anthropics/skills](https://github.com/anthropics/skills) | Apache-2.0 |
156
+ | `nestjs-best-practices` | [Kadajett/agent-nestjs-skills](https://github.com/Kadajett/agent-nestjs-skills) | MIT |
157
+ | `scope` | [JavaScript-Mastery-Pro/skills](https://github.com/JavaScript-Mastery-Pro/skills) | MIT |
158
+ | `shadcn` | [shadcn-ui/ui](https://github.com/shadcn-ui/ui) | MIT |
159
+ | `shadcn-component-discovery` | [mattbx/shadcn-skills](https://github.com/mattbx/shadcn-skills) | MIT |
160
+ | `shadcn-component-review` | [mattbx/shadcn-skills](https://github.com/mattbx/shadcn-skills) | MIT |
161
+ | `skill-creator` | [anthropics/skills](https://github.com/anthropics/skills) | Apache-2.0 |
162
+ | `tdd` | [mattpocock/skills](https://github.com/mattpocock/skills) | MIT |
163
+ | `test` | [JavaScript-Mastery-Pro/skills](https://github.com/JavaScript-Mastery-Pro/skills) | MIT |
164
+ | `theme-factory` | [anthropics/skills](https://github.com/anthropics/skills) | Apache-2.0 |
165
+ | `webapp-testing` | [anthropics/skills](https://github.com/anthropics/skills) | Apache-2.0 |
166
+
167
+ Each lock entry pins an upstream commit and skill tree SHA. Maintainers should follow the [external skill review and pinning procedure](docs/external-skills-maintenance.md) when changing this manifest.
168
+
169
+ ## Security record (`SECURITY.md`)
170
+
171
+ [`SECURITY.md`](SECURITY.md) is the recorded vetting record for every skill the package installs. It lists each internal skill and each locked external skill with its source, license, scan status (`first-party`, `pass`, or `flagged`), and the month it was last reviewed. Install-time scanning is skipped by design (`--skip-scan`); `SECURITY.md` plus `library/SAFETY.md` is the vetting record. `Last external scan` records when the pinned `skillsio 1.1.3` scan (Trust Hub, Socket, Snyk) was last recorded. `scripts/check-security.mjs` fails CI when a skill is added and not recorded, or when that line is missing. `pnpm run check:pins` re-fetches every locked source and confirms its tree SHA.
172
+
173
+ ## Installing from GitHub Packages
174
+
175
+ This package is also published to GitHub Packages as `@devenius/junamis-skills`; it is the mirror of the npmjs release, not the primary registry.
176
+
177
+ 1. Create or edit `~/.npmrc` (or project `.npmrc`):
178
+
179
+ ```ini
180
+ @devenius:registry=https://npm.pkg.github.com
181
+ //npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN
182
+ ```
183
+
184
+ 2. Install or run:
185
+
186
+ ```bash
187
+ npm install -g @devenius/junamis-skills
188
+ # or run without installing:
189
+ npx github:devenius/junamis-skills init
190
+ ```
191
+
192
+ A GitHub personal access token with `read:packages` (and `write:packages` for publishing) is required.
193
+
194
+ ## Maintainers: publishing
195
+
196
+ npmjs (`@devenius/junamis-skills`) is the primary public registry and GitHub Packages is the mirror. The release mechanics, trusted publisher setup, staged approval, and deprecation policy live in [`docs/publishing/npm.md`](docs/publishing/npm.md).
197
+
198
+ **The user runs every publish and deprecate command.** The agent prepares the exact command and the release metadata and stops there. It never runs `npm publish`, `npm stage publish`, `npm deprecate`, `npm unpublish`, `npm dist-tag`, `npm owner`, or `npm access`; those are user actions gated by `library/SAFETY.md` and single-use approvals.
199
+
200
+ ## Safety
201
+
202
+ - The installer never runs a command through a shell. Every value is checked against a strict pattern before use.
203
+ - The scanner is pinned to `skillsio@1.1.3` with a verified registry, tarball URL, and SHA-512 integrity. Each locked commit, tree SHA, and checkout HEAD is verified before it runs.
204
+ - A skill folder must be a Git tree. An in-tree symlink such as `CLAUDE.md -> AGENTS.md` is allowed; an absolute or escaping symlink is rejected, as is a gitlink.
205
+ - `init` passes `-y` and `--skip-scan` so it can install every skill non-interactively. `-y` cannot bypass the scanner's high/critical prompt, so install-time scanning is skipped by design; the recorded scan is the gate. After each group it verifies that every expected `<name>/SKILL.md` exists and counts only verified installs.
206
+ - Flagged findings are vetted in `SECURITY.md` and gated in `library/SAFETY.md`.
207
+ - Destructive or external actions in the library need your explicit approval. See `library/SAFETY.md`.
208
+
209
+ ## Development
210
+
211
+ The runtime code is TypeScript under `src/`. The built package ships `dist/` and its declarations; `src/` is not part of the published package.
212
+
213
+ ```bash
214
+ pnpm install # dev dependencies only; there is no runtime dependency
215
+ pnpm build # tsc -> dist/ (JavaScript, declarations, source maps)
216
+ pnpm typecheck # source check, then the compiled consumer contract
217
+ pnpm test # build, typecheck, then the full Node test suite
218
+ ```
219
+
220
+ Consumers import the package root (`@devenius/junamis-skills`) or the `./types` subpath; both resolve to `dist/`. `dist/` is generated and gitignored, so contributors never commit it. See [`docs/typescript.md`](docs/typescript.md) for the build outputs, strict compiler settings, and the `unknown`-at-the-boundary rules.
221
+
222
+ `main` is protected and read-only. Work on a task branch in its own worktree, run the gate above, push the branch, and open a pull request; never push directly to `main` and never self-merge. See [`CONTRIBUTING.md`](CONTRIBUTING.md) and [`.github/pull_request_template.md`](.github/pull_request_template.md) for the checklist.
223
+
224
+ ## Reporting issues
225
+
226
+ If an external skill is missing or an install fails, open an issue at
227
+ https://github.com/devenius/junamis-skills/issues with these details:
228
+ version, Node version, agent, installer, failing skill, and the command output.
229
+
230
+ ## License
231
+
232
+ MIT. The external skills keep their own licenses, listed in `skills-lock.json`.
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAIA,OAAO,EAAyC,UAAU,EAAE,MAAM,YAAY,CAAC;AAE/E,OAAO,EAAE,eAAe,EAAoC,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAgB,MAAM,sBAAsB,CAAC;AAG7F,OAAO,KAAK,EACV,qBAAqB,EAGrB,YAAY,EAGZ,cAAc,EACd,cAAc,EACf,MAAM,kBAAkB,CAAC;AAkC1B,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAmGD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,aAAa,GAAG,eAAe,CAAC;AAE9F,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAID,wBAAsB,mBAAmB,CAAC,EACxC,GAAmB,EACnB,KAAgB,EAChB,KAAa,EACb,MAAc,EACd,WAA0B,GAC3B,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA6B5D;AAmDD,iEAAiE;AACjE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,UAAU,CAAC;IACxC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,mBAAmB,CAAC;IAC1D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,kBAAkB,CAAC;IACxD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,mBAAmB,CAAC;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,eAAe,CAAC;IAClD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,yDAAyD;AACzD,MAAM,WAAW,mBAAoB,SAAQ,qBAAqB;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAE,QAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAmFtF;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,cAAsB,EAC7B,UAAU,GAAE,MAAsB,GACjC,cAAc,EAAE,CAkBlB;AAuND,wBAAsB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC,CAkB3F"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@ import { existsSync, lstatSync, readlinkSync } from "node:fs";
2
2
  import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
5
+ import { createColors, colorsEnabled } from "./color.js";
5
6
  import { PACKAGE_ROOT, GUIDE_NAME, MARKER_PATH, writeGuide } from "./guide.js";
6
7
  import { getTarget, targets } from "./targets/index.js";
7
8
  import { installExternal, validateAgent, validateInstaller } from "./external.js";
@@ -161,63 +162,69 @@ export async function writeOpencodeConfig({ dir = process.cwd(), scope = "global
161
162
  await writeFile(targetPath, content, "utf8");
162
163
  return { status: "written", path: targetPath };
163
164
  }
165
+ function palette() {
166
+ return createColors({ enabled: colorsEnabled() });
167
+ }
164
168
  function reportGuide(result, projectDir) {
169
+ const c = palette();
165
170
  if (result.status === "created") {
166
- console.log(`guide: wrote ${result.path}`);
167
- console.log(`marker: ${join(projectDir, MARKER_PATH)}`);
171
+ console.log(`${c.cyan("guide:")} wrote ${c.gray(result.path)}`);
172
+ console.log(`${c.cyan("marker:")} ${c.gray(join(projectDir, MARKER_PATH))}`);
168
173
  }
169
174
  else if (result.status === "exists") {
170
- console.log(`guide: ${GUIDE_NAME} already written by junamis-skills ${result.version}; nothing to do.`);
171
- console.log("guide: use --force to rewrite it.");
175
+ console.log(`${c.cyan("guide:")} ${GUIDE_NAME} already written by junamis-skills ${result.version}; nothing to do.`);
176
+ console.log(c.yellow("guide: use --force to rewrite it."));
172
177
  }
173
178
  else if (result.status === "kept-user-file") {
174
- console.log(`guide: ${result.path} exists and was not written by junamis-skills; left unchanged.`);
175
- console.log("guide: use --force to replace it.");
179
+ console.log(`${c.cyan("guide:")} ${c.gray(result.path)} exists and was not written by junamis-skills; left unchanged.`);
180
+ console.log(c.yellow("guide: use --force to replace it."));
176
181
  }
177
182
  else {
178
- console.log(`guide: ${String(result.status)} (${result.path})`);
183
+ console.log(`${c.cyan("guide:")} ${String(result.status)} (${c.gray(result.path)})`);
179
184
  }
180
185
  }
181
186
  function reportConfig(result) {
187
+ const c = palette();
182
188
  if (result.status === "written")
183
- console.log(`config: wrote ${result.path}`);
189
+ console.log(`${c.cyan("config:")} wrote ${c.gray(result.path)}`);
184
190
  else if (result.status === "unchanged")
185
- console.log(`config: unchanged (${result.path})`);
191
+ console.log(`${c.cyan("config:")} unchanged (${c.gray(result.path)})`);
186
192
  else if (result.status === "kept")
187
- console.log(`config: left unchanged; use --force (${result.path})`);
193
+ console.log(`${c.yellow("config:")} left unchanged; use --force (${c.gray(result.path)})`);
188
194
  else if (result.status === "would-write")
189
- console.log(`config: would write ${result.path}`);
195
+ console.log(`${c.cyan("config:")} would write ${c.gray(result.path)}`);
190
196
  else if (result.status === "would-replace")
191
- console.log(`config: would replace ${result.path} (--force)`);
197
+ console.log(`${c.cyan("config:")} would replace ${c.gray(result.path)} (--force)`);
192
198
  else
193
- console.log(`config: ${String(result.status)} (${result.path})`);
199
+ console.log(`${c.cyan("config:")} ${String(result.status)} (${c.gray(result.path)})`);
194
200
  }
195
201
  function reportLibrary(args, scope, result, version) {
202
+ const c = palette();
196
203
  const counts = {};
197
204
  for (const a of [...result.actions, ...(result.linkActions ?? [])]) {
198
205
  counts[a.action] = (counts[a.action] ?? 0) + 1;
199
206
  }
200
207
  const summary = Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(", ") || "nothing";
201
- console.log(`library for ${args.agent} (junamis-skills ${version}, ${scope}): ${summary}`);
208
+ console.log(`${c.cyan("library")} for ${args.agent} (junamis-skills ${version}, ${scope}): ${c.gray(summary)}`);
202
209
  if (args.dryRun) {
203
210
  for (const a of result.actions)
204
- console.log(` ${a.action.padEnd(24)} ${a.path}`);
211
+ console.log(` ${a.action.padEnd(24)} ${c.gray(a.path)}`);
205
212
  for (const a of result.linkActions ?? [])
206
- console.log(` ${a.action.padEnd(24)} ${a.path} -> ${a.target}`);
213
+ console.log(` ${a.action.padEnd(24)} ${c.gray(a.path)} -> ${c.gray(String(a.target))}`);
207
214
  }
208
215
  for (const p of result.stale)
209
- console.log(` ${args.dryRun ? "would remove" : "removed"} stale ${p}`);
216
+ console.log(` ${args.dryRun ? "would remove" : "removed"} stale ${c.gray(p)}`);
210
217
  for (const warning of result.warnings ?? [])
211
- console.warn(` warning: ${warning}`);
218
+ console.warn(c.yellow(` warning: ${warning}`));
212
219
  if (!result.ok) {
213
- console.error(`\n${result.conflicts.length} library file(s) were edited by you and are not managed by junamis-skills:`);
220
+ console.error(c.red(`\n${result.conflicts.length} library file(s) were edited by you and are not managed by junamis-skills:`));
214
221
  for (const p of result.conflicts)
215
- console.error(` ${p}`);
216
- console.error("Review them. Run again with --force to overwrite, or move your copy elsewhere.");
222
+ console.error(c.red(` ${p}`));
223
+ console.error(c.yellow("Review them. Run again with --force to overwrite, or move your copy elsewhere."));
217
224
  return 1;
218
225
  }
219
226
  if (args.dryRun)
220
- console.log("library: dry run, nothing was written.");
227
+ console.log(c.green("library: dry run, nothing was written."));
221
228
  return 0;
222
229
  }
223
230
  /**
@@ -239,19 +246,20 @@ export async function runInit(args, deps = {}) {
239
246
  throw new Error(`unknown agent "${agent}"; expected opencode or claude`);
240
247
  }
241
248
  const scope = resolvedScope(args.scope, "global");
249
+ const c = palette();
242
250
  let exitCode = 0;
243
251
  // 1. Guide.
244
252
  if (args.dryRun) {
245
253
  const guidePath = join(args.dir, GUIDE_NAME);
246
254
  const exists = existsSync(guidePath);
247
- console.log(`guide: ${exists && !args.force ? "present, would not rewrite without --force" : `would write ${guidePath}`}`);
255
+ console.log(`${c.cyan("guide:")} ${exists && !args.force ? "present, would not rewrite without --force" : `would write ${c.gray(guidePath)}`}`);
248
256
  }
249
257
  else {
250
258
  reportGuide(await d.writeGuide({ projectDir: args.dir, force: args.force }), args.dir);
251
259
  }
252
260
  // 2. opencode.json for OpenCode only.
253
261
  if (agent !== "opencode") {
254
- console.log("config: skipped (opencode.json is OpenCode only)");
262
+ console.log(`${c.cyan("config:")} skipped (opencode.json is OpenCode only)`);
255
263
  }
256
264
  else {
257
265
  reportConfig(await d.writeOpencodeConfig({
@@ -284,7 +292,7 @@ export async function runInit(args, deps = {}) {
284
292
  if (args.dryRun) {
285
293
  const names = await lockSkillNames();
286
294
  external = { ...external, planned: names.length };
287
- console.log(`external: ${names.length} pinned skills would be installed with skillsio`);
295
+ console.log(`${c.cyan("external:")} ${names.length} pinned skills would be installed with skillsio`);
288
296
  }
289
297
  else {
290
298
  validateInstaller("skillsio");
@@ -294,17 +302,17 @@ export async function runInit(args, deps = {}) {
294
302
  agent,
295
303
  installer: "skillsio",
296
304
  });
297
- console.log(`external: ${external.installed} installed, ${external.skipped} skipped, ${external.failed} failed`);
305
+ console.log(`${c.cyan("external:")} ${external.installed} installed, ${external.skipped} skipped, ${external.failed} failed`);
298
306
  if (external.failed > 0)
299
307
  exitCode = 1;
300
308
  }
301
309
  // 5. Summary.
302
- console.log("\nsummary:");
303
- console.log(` guide: ${args.dryRun ? "planned" : "done"}`);
304
- console.log(` config: ${agent === "opencode" ? (args.dryRun ? "planned" : "done") : "skipped"}`);
305
- console.log(` library: ${args.dryRun ? "planned" : "done"} (${scope})`);
306
- console.log(` external: ${args.dryRun ? `planned (${String(external.planned)})` : `${external.installed} installed, ${external.skipped} skipped, ${external.failed} failed`}`);
307
- console.log(` exit: ${exitCode === 0 ? "ok" : "needs attention"}`);
310
+ console.log(`\n${c.cyan("summary:")}`);
311
+ console.log(` ${c.gray("guide:")} ${args.dryRun ? c.yellow("planned") : c.green("done")}`);
312
+ console.log(` ${c.gray("config:")} ${agent === "opencode" ? (args.dryRun ? c.yellow("planned") : c.green("done")) : "skipped"}`);
313
+ console.log(` ${c.gray("library:")} ${args.dryRun ? c.yellow("planned") : c.green("done")} (${scope})`);
314
+ console.log(` ${c.gray("external:")} ${args.dryRun ? c.yellow(`planned (${String(external.planned)})`) : `${external.installed} installed, ${external.skipped} skipped, ${external.failed} failed`}`);
315
+ console.log(` ${c.gray("exit:")} ${exitCode === 0 ? c.green("ok") : c.yellow("needs attention")}`);
308
316
  return { exitCode, scope, external };
309
317
  }
310
318
  export function statusLocations(agentId, scope = "all", projectDir = process.cwd()) {
@@ -379,6 +387,7 @@ async function legacyLeftovers(scope, projectDir) {
379
387
  return found;
380
388
  }
381
389
  async function status(args) {
390
+ const c = palette();
382
391
  const scope = args.scope ?? "global";
383
392
  if (!isOneOf(scope, ALL_SCOPES)) {
384
393
  throw new Error(`unknown scope "${scope}"; expected global, project, or all`);
@@ -386,7 +395,7 @@ async function status(args) {
386
395
  const scopes = scope === "all" ? ["global", "project"] : [scope];
387
396
  const failures = [];
388
397
  const check = (label, pass, detail = "") => {
389
- console.log(`${pass ? "✔" : "✖"} ${label}${detail ? `: ${detail}` : ""}`);
398
+ console.log(`${pass ? c.green("✔") : c.red("✖")} ${label}${detail ? `: ${c.gray(detail)}` : ""}`);
390
399
  if (!pass)
391
400
  failures.push(label);
392
401
  return pass;
@@ -395,9 +404,9 @@ async function status(args) {
395
404
  // only a present but malformed marker is a real problem.
396
405
  const guidePath = join(args.dir, GUIDE_NAME);
397
406
  const guideExists = existsSync(guidePath);
398
- console.log(`project: ${args.dir}`);
399
- console.log(`package: ${PACKAGE_ROOT}`);
400
- console.log(`guide: ${guideExists ? "present" : "missing"} (${GUIDE_NAME})`);
407
+ console.log(`${c.cyan("project:")} ${c.gray(args.dir)}`);
408
+ console.log(`${c.cyan("package:")} ${c.gray(PACKAGE_ROOT)}`);
409
+ console.log(`${c.cyan("guide:")} ${guideExists ? c.green("present") : "missing"} (${GUIDE_NAME})`);
401
410
  const markerPath = join(args.dir, MARKER_PATH);
402
411
  let marker = null;
403
412
  let markerProblem = null;
@@ -418,18 +427,18 @@ async function status(args) {
418
427
  }
419
428
  }
420
429
  }
421
- console.log(`marker: ${marker ? `written ${marker.writtenAt} by ${marker.version}` : "none"}`);
430
+ console.log(`${c.cyan("marker:")} ${marker ? `written ${marker.writtenAt} by ${marker.version}` : "none"}`);
422
431
  if (markerProblem) {
423
- console.error(`✖ invalid marker ${markerPath}: ${markerProblem}`);
432
+ console.error(c.red(`✖ invalid marker ${markerPath}: ${markerProblem}`));
424
433
  failures.push("marker");
425
434
  }
426
435
  // Per-target counts and library manifests. Missing directories and manifests
427
436
  // are informational: a target can be detected without this package being
428
437
  // installed for it. A present manifest is always validated.
429
- console.log("agents:");
438
+ console.log(c.cyan("agents:"));
430
439
  for (const t of targets) {
431
440
  const detected = t.detect();
432
- console.log(` ${t.label.padEnd(12)} ${detected ? "detected" : "not found"}`);
441
+ console.log(` ${t.label.padEnd(12)} ${detected ? c.green("detected") : c.gray("not found")}`);
433
442
  for (const location of statusLocations(t.id, scope, args.dir)) {
434
443
  const libraryCount = await countSkills(location.librarySkillsDir);
435
444
  const externalCount = await countSkills(location.skillsDir);
@@ -453,13 +462,13 @@ async function status(args) {
453
462
  }
454
463
  const parsed = await readJsonObject(location.manifestPath);
455
464
  if (!parsed.ok) {
456
- console.error(`✖ library manifest invalid (${location.manifestPath}): ${parsed.error}`);
465
+ console.error(c.red(`✖ library manifest invalid (${location.manifestPath}): ${parsed.error}`));
457
466
  failures.push("library manifest");
458
467
  continue;
459
468
  }
460
469
  const problem = describeLibraryManifest(parsed.value);
461
470
  if (problem) {
462
- console.error(`✖ library manifest invalid (${location.manifestPath}): ${problem}`);
471
+ console.error(c.red(`✖ library manifest invalid (${location.manifestPath}): ${problem}`));
463
472
  failures.push("library manifest");
464
473
  continue;
465
474
  }
@@ -516,7 +525,7 @@ async function status(args) {
516
525
  const missing = lockNames.filter((name) => !existsSync(join(externalDir, name)));
517
526
  const coverage = `${lockNames.length - missing.length}/${lockNames.length}`;
518
527
  const coverageDetail = missing.length === 0 ? `${coverage} in ${externalDir}` : `${coverage}; missing ${missing.join(", ")}`;
519
- console.log(`external coverage: ${coverageDetail}`);
528
+ console.log(`${c.cyan("external coverage:")} ${missing.length === 0 ? c.green(coverageDetail) : c.yellow(coverageDetail)}`);
520
529
  // SECURITY.md is the recorded scan vetted at install time. Report its status
521
530
  // without failing; the scan record is part of the package, not the install.
522
531
  const securityPath = join(PACKAGE_ROOT, "SECURITY.md");
@@ -525,9 +534,9 @@ async function status(args) {
525
534
  const text = await readFile(securityPath, "utf8");
526
535
  securityMonth = text.match(/^Last external scan:\s*(\d{4}-\d{2})/m)?.[1] ?? null;
527
536
  }
528
- console.log(`SECURITY.md scan record: ${securityMonth ? `${securityPath} (last external scan ${securityMonth})` : `${securityPath} is missing or has no Last external scan line`}`);
537
+ console.log(`${c.cyan("SECURITY.md scan record:")} ${securityMonth ? `${c.gray(securityPath)} (last external scan ${securityMonth})` : c.yellow(`${securityPath} is missing or has no Last external scan line`)}`);
529
538
  const passed = failures.length === 0;
530
- console.log(`\nstatus: ${passed ? "ok" : `${failures.length} required check(s) failed`}`);
539
+ console.log(`\n${c.cyan("status:")} ${passed ? c.green("ok") : c.yellow(`${failures.length} required check(s) failed`)}`);
531
540
  if (!passed)
532
541
  process.exitCode = 1;
533
542
  return passed ? 0 : 1;