@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.
- package/CHANGELOG.md +41 -1
- package/README.md +232 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +54 -45
- package/dist/cli.js.map +1 -1
- package/dist/color.d.ts +40 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +55 -0
- package/dist/color.js.map +1 -0
- package/dist/design/bootstrap.d.ts +7 -1
- package/dist/design/bootstrap.d.ts.map +1 -1
- package/dist/design/bootstrap.js +48 -0
- package/dist/design/bootstrap.js.map +1 -1
- package/dist/library-install.d.ts.map +1 -1
- package/dist/library-install.js +8 -3
- package/dist/library-install.js.map +1 -1
- package/dist/scripts/check-opencode-config.d.ts.map +1 -1
- package/dist/scripts/check-opencode-config.js +28 -1
- package/dist/scripts/check-opencode-config.js.map +1 -1
- package/dist/scripts/check-pack.d.ts.map +1 -1
- package/dist/scripts/check-pack.js +1 -0
- package/dist/scripts/check-pack.js.map +1 -1
- package/dist/scripts/publish-npm.d.ts +3 -0
- package/dist/scripts/publish-npm.d.ts.map +1 -0
- package/dist/scripts/publish-npm.js +67 -0
- package/dist/scripts/publish-npm.js.map +1 -0
- package/dist/scripts/release-check.d.ts.map +1 -1
- package/dist/scripts/release-check.js +7 -3
- package/dist/scripts/release-check.js.map +1 -1
- package/docs/RECONCILE.md +505 -0
- package/junamis-skills.yml +34 -6
- package/library/HANDOFF.md +3 -3
- package/library/RESUME.md +1 -0
- package/library/SAFETY.md +2 -1
- package/library/agents/context-keeper.md +9 -1
- package/library/agents/frontend.md +9 -1
- package/library/agents/maintainer.md +9 -1
- package/library/agents/nestjs.md +9 -1
- package/library/agents/nextjs.md +9 -1
- package/library/agents/operator.md +11 -2
- package/library/agents/orchestrator.md +121 -41
- package/library/agents/researcher.md +9 -1
- package/library/agents/reviewer.md +9 -1
- package/library/skills/release-prep/SKILL.md +9 -1
- package/library/skills/route/SKILL.md +2 -0
- package/opencode.json +366 -1
- 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
|
-
## [
|
|
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
|
-
|
|
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":"
|
|
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(
|
|
167
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
189
|
+
console.log(`${c.cyan("config:")} wrote ${c.gray(result.path)}`);
|
|
184
190
|
else if (result.status === "unchanged")
|
|
185
|
-
console.log(
|
|
191
|
+
console.log(`${c.cyan("config:")} unchanged (${c.gray(result.path)})`);
|
|
186
192
|
else if (result.status === "kept")
|
|
187
|
-
console.log(
|
|
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(
|
|
195
|
+
console.log(`${c.cyan("config:")} would write ${c.gray(result.path)}`);
|
|
190
196
|
else if (result.status === "would-replace")
|
|
191
|
-
console.log(
|
|
197
|
+
console.log(`${c.cyan("config:")} would replace ${c.gray(result.path)} (--force)`);
|
|
192
198
|
else
|
|
193
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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("
|
|
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(
|
|
399
|
-
console.log(
|
|
400
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(`\
|
|
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;
|