stackfit 0.0.0-stage → 0.1.1

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 ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.1
4
+
5
+ - Git exclude now lists only the files stackfit generates (plus `.agents/` and the two skill links) in one managed block. Earlier versions excluded the whole `.claude/` and `.cursor/` folders, which hid a user's own files there (for example `.claude/settings.json`) from git. The next sync replaces the old lines; your own lines in `.git/info/exclude` are kept.
6
+ - New: `skills/stackfit`, an agent skill that teaches Claude Code, Cursor, and Codex how to use stackfit (`npx skills add amin0075/stackfit`).
7
+
8
+ ## 0.1.0
9
+
10
+ - First release.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amin Keshavarzi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,186 @@
1
- # Temporary Holding Version
1
+ # stackfit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Your agent loads every skill in every repo. It needs a handful.**
4
+
5
+ stackfit keeps one catalog of agent skills, rules, commands, and subagents, and gives each repository only what fits its stack: Next.js skills where there is `next`, Stripe skills where there is `stripe`, nothing for design in a Go service. It renders them for **Claude Code, Cursor, and Codex**, across every project on your machine, in one command.
6
+
7
+ ![stackfit demo](https://raw.githubusercontent.com/amin0075/stackfit/main/media/demo.gif)
8
+
9
+ ```bash
10
+ npx stackfit catalog init --starter # a catalog in ~/.stackfit with curated packs
11
+ npx stackfit refresh --all # vendor the upstream skills (Vercel, shadcn, Stripe, ...)
12
+ npx stackfit report --root ~/code # what each repo would load, next to what your global skills cost today
13
+ npx stackfit sync --root ~/code # write it
14
+ ```
15
+
16
+ ### Or let your agent do it
17
+
18
+ stackfit ships an agent skill that knows every command, recipe, and error message:
19
+
20
+ ```bash
21
+ npx skills add amin0075/stackfit # or copy skills/stackfit into ~/.claude/skills
22
+ ```
23
+
24
+ Then ask your agent: *"Set up stackfit for my repos in ~/code and tell me honestly what it saves."* It measures your current global skills first, previews before writing, and never forces over your own files. The starter catalog includes the same skill.
25
+
26
+ ## Why
27
+
28
+ Every installed skill puts its name and description into every agent session, so the agent knows it exists. Install 40 skills globally and a Go service pays for your React animation guides on every turn. Copy skills into each repo by hand and they drift, go stale, and lose your edits on the next update.
29
+
30
+ stackfit treats agent context like a dependency:
31
+
32
+ - **Packs, detected per repo.** Skills belong to packs. A pack is selected when the repo's manifests mention a dependency or contain a file: `package.json`, `requirements.txt`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `composer.json`, at the root and in subfolders like `web/`, `apps/*`, `packages/*`.
33
+ - **One catalog, every repo.** Machine profiles list your project folders on each computer (Mac at home, Windows at work). `stackfit update --all` pulls the catalog and syncs them all.
34
+ - **Measured, honestly.** `stackfit report` shows what each repo loads every session (skill listings + always-on rules) next to what your global skill folders already cost everywhere; `stackfit audit` flags stale, unsourced, and oversized skills.
35
+ - **Upstream skills without losing your edits.** Vendor skills from GitHub with `refresh`; shorten a description, make a skill manual-only, or drop files with `render` overrides that survive every refresh.
36
+ - **Native for each agent.** Claude Code gets `.claude/skills`, `.claude/rules` (glob rules become `paths:`), manual-only commands, and subagents. Cursor gets `.cursor/skills`, `.mdc` rules, commands, and agents. Codex reads `.agents/skills`. One physical copy, symlinked (junctions on Windows, no admin needed).
37
+ - **Safe.** Generated files are tracked in a lock and only those exact files are excluded from Git locally (your own `.claude/settings.json` stays visible). stackfit never overwrites a file it did not create, and never touches files your repo owns.
38
+
39
+ ## Example
40
+
41
+ The starter catalog, four sample repos, and 31 skills installed globally the usual way:
42
+
43
+ ```text
44
+ $ stackfit report --root ~/code
45
+ project skills skill listings rules per session packs
46
+ ingest-worker 2 ~168 tok ~83 tok ~251 tok core
47
+ marketing-site 25 ~1.5k tok ~83 tok ~1.6k tok core, react, nextjs, ui, motion
48
+ payments-api 5 ~397 tok ~83 tok ~480 tok core, stripe
49
+ storefront 24 ~1.5k tok ~83 tok ~1.6k tok core, react, nextjs, e2e, ui, shadcn, stripe
50
+
51
+ With stackfit: ~4.0k tokens across 4 project(s). Every catalog skill in every repo would be ~8.7k.
52
+ Global skills add ~3.0k tokens to every session in every repo, with or without stackfit: ~/.claude/skills (31).
53
+ ```
54
+
55
+ Read it honestly: the saving comes from moving skills out of global folders and letting each repo take only its packs. The Go worker drops from ~3.0k to ~251 tokens; a Next.js app that needs most packs saves little. If you only had a handful of global skills, start from an empty catalog with just those (`stackfit catalog init`) rather than the starter, or the starter's extra packs can add context instead of removing it. `report` always shows both numbers, so you can tell.
56
+
57
+ ## How it works
58
+
59
+ ```text
60
+ ~/.stackfit/ your catalog (keep it in a private Git repo)
61
+ stackfit.json enabled skills + packs
62
+ skill-sources.json where each skill comes from, review dates, render overrides
63
+ skills/<name>/SKILL.md the skills
64
+ rules/ commands/ agents/ shared adapters
65
+ stackfit.local.json (gitignored) your project folders per machine
66
+ .env (gitignored) values for {{env:NAME}} placeholders
67
+
68
+ <project>/
69
+ .agents/skills/ the selected skills (one physical copy)
70
+ .claude/skills .cursor/skills symlinks to it
71
+ .claude/rules/*.md .cursor/rules/*.mdc
72
+ .claude/commands .cursor/commands
73
+ .claude/agents .cursor/agents
74
+ .stackfit.lock.json what stackfit owns here
75
+ docs/agent/config.json (optional, tracked) this repo's pack choices
76
+ ```
77
+
78
+ ### Packs
79
+
80
+ ```json
81
+ {
82
+ "schemaVersion": 3,
83
+ "skills": ["grilling", "next-server-security", "next-dev-loop", "shadcn"],
84
+ "packs": {
85
+ "core": { "always": true, "skills": ["grilling"] },
86
+ "nextjs": { "detect": { "dependencies": ["next"] }, "skills": ["next-server-security", "next-dev-loop"] },
87
+ "shadcn": { "detect": { "files": ["components.json"] }, "skills": ["shadcn"] },
88
+ "python": { "detect": { "dependencies": ["django", "fastapi"] }, "skills": [] }
89
+ }
90
+ }
91
+ ```
92
+
93
+ `always` packs go everywhere; `detect` packs are chosen automatically; packs with neither are opt-in. A repository adjusts its own selection in `docs/agent/config.json`:
94
+
95
+ ```json
96
+ { "schemaVersion": 1, "packs": ["motion"], "excludePacks": ["stripe"], "skills": ["ui-ux-pro-max"] }
97
+ ```
98
+
99
+ Or, without touching the repo, on its entry in `stackfit.local.json`.
100
+
101
+ ### Machines
102
+
103
+ ```json
104
+ {
105
+ "schemaVersion": 1,
106
+ "machines": [
107
+ { "id": "mac", "match": { "platform": "darwin" }, "roots": ["~/code"], "projects": ["storefront", { "name": "client-site", "scaffold": false, "packs": ["motion"] }] },
108
+ { "id": "work", "match": { "platform": "win32", "hostname": "WORK-PC" }, "roots": ["C:/dev"] }
109
+ ]
110
+ }
111
+ ```
112
+
113
+ Then `stackfit sync --all` on each machine. Without `projects`, every Git repo under `roots` is synced.
114
+
115
+ ### Upstream skills and overrides
116
+
117
+ ```json
118
+ "shadcn": {
119
+ "kind": "external",
120
+ "source": { "repository": "https://github.com/shadcn-ui/ui.git", "path": "skills/shadcn" },
121
+ "reviewed": "2026-10-26",
122
+ "render": {
123
+ "frontmatter": { "description": "Add, search, and style shadcn/ui components. Use with components.json." },
124
+ "exclude": ["evals/evals.json"]
125
+ }
126
+ }
127
+ ```
128
+
129
+ `stackfit refresh --skill shadcn` replaces the vendored copy and records the upstream commit in `skill-sources.lock.json`. Your `render` overrides apply at sync time, so they survive. Set `"disable-model-invocation": true` on long one-off workflows (migrations, adoptions) so they cost nothing until you call them.
130
+
131
+ ### Personal values
132
+
133
+ Commands, rules, and agents can contain `{{env:NAME}}`. stackfit fills them at sync time from your shell or the catalog's gitignored `.env`, so a shared catalog never holds handles, emails, or tokens. See `.env.example`.
134
+
135
+ ## Commands
136
+
137
+ | Command | What it does |
138
+ | --- | --- |
139
+ | `stackfit catalog init [dir] [--starter]` | Create a catalog (default `~/.stackfit`) |
140
+ | `stackfit report` | Per project: packs, skills, skill-listing and rule tokens; plus your global skill folders as a baseline (`--json`) |
141
+ | `stackfit status` | Dry run of sync; `--check` exits 1 on drift (CI) |
142
+ | `stackfit sync` | Render the selected skills, rules, commands, and agents |
143
+ | `stackfit update` | `git pull --ff-only` the catalog, then sync |
144
+ | `stackfit init` | Add tracked `AGENTS.md`, `CLAUDE.md`, and `docs/agent/` to projects; never overwrites |
145
+ | `stackfit refresh --skill <name> \| --all` | Vendor upstream skills into the catalog |
146
+ | `stackfit audit` | Check provenance, staleness (180 days), packs, description sizes |
147
+
148
+ Select projects with `--project <path>`, `--root <dir>[,<dir>]`, or `--all` (this machine's profile). The catalog is found from `--catalog`, `STACKFIT_CATALOG`, the nearest `stackfit.json` above the current folder, or `~/.stackfit`.
149
+
150
+ ## The starter catalog
151
+
152
+ `--starter` gives you packs built from maintained upstream skills, plus two first-party ones:
153
+
154
+ | Pack | Selected by | Skills |
155
+ | --- | --- | --- |
156
+ | core | always | grilling (Matt Pocock), improve (shadcn) |
157
+ | react | `react` | vercel-react-best-practices, vercel-composition-patterns, react-doctor |
158
+ | nextjs | `next` | next-server-security, next-dev-loop, next-bundle-optimizer, Cache Components and Partial Prefetching workflows (manual-only), vercel-react-view-transitions (manual-only) |
159
+ | e2e | `@playwright/test` | playwright-e2e-testing |
160
+ | ui | `react-dom`, `vue`, `svelte`, `tailwindcss`, ... | frontend-design (Anthropic), interface-design, baseline-ui, fixing-accessibility, make-interfaces-feel-better, web-design-guidelines |
161
+ | shadcn | `components.json` | shadcn |
162
+ | motion | `motion`, `framer-motion`, `gsap`, ... | Emil Kowalski's animation skills, fixing-motion-performance |
163
+ | stripe | `stripe`, `@stripe/*` | stripe-best-practices, upgrade-stripe, stripe-projects |
164
+ | design-research | opt-in | ui-ux-pro-max |
165
+
166
+ Skills are vendored from their authors' repositories under their own licences when you run `refresh`; stackfit does not redistribute them.
167
+
168
+ Next.js 16.2+ ships version-matched docs in `node_modules/next/dist/docs`, and `next dev` adds a managed block to `AGENTS.md` pointing agents at them. That is why the starter has no generic "Next.js best practices" skill: workflows belong in skills, framework reference belongs in the bundled docs.
169
+
170
+ ## FAQ
171
+
172
+ **Does it replace `npx skills` / skills.sh?** No. Those install skills. stackfit decides which repo gets which, keeps them in sync across many repos and agents, and measures the cost. Point `skill-sources.json` at any GitHub skill.
173
+
174
+ **What about my existing `.cursor/rules` or `.claude/skills`?** stackfit refuses to overwrite files it does not own. Move your own files into the catalog (shared) or `docs/agent/` (repo-specific), or pass `--force` once you have reviewed the collision.
175
+
176
+ **Does it commit anything to my projects?** No. Generated paths are added to `.git/info/exclude`. Only `stackfit init` creates tracked files (`AGENTS.md`, `CLAUDE.md`, `docs/agent/`), and only when you run it.
177
+
178
+ **Windows?** Yes. Skill folders are linked with directory junctions, which need no admin rights or developer mode.
179
+
180
+ ## Contributing
181
+
182
+ Issues and PRs welcome, especially new detection signals and agent targets. See [CONTRIBUTING.md](CONTRIBUTING.md). Run `npm test` (Node 22+, no dependencies).
183
+
184
+ ## License
185
+
186
+ MIT © Amin Keshavarzi
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from "node:fs";
4
+ import { dirname, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { loadRegistry, resolveCatalog } from "../src/catalog.mjs";
7
+ import { auditCatalog, printAudit } from "../src/audit.mjs";
8
+ import { selectedProjects } from "../src/projects.mjs";
9
+ import { refreshSkills } from "../src/refresh.mjs";
10
+ import { globalSkills, printReport, projectReport, skillCosts } from "../src/report.mjs";
11
+ import { initCatalog, initProject } from "../src/scaffold.mjs";
12
+ import { applyPlan, createPlan, git } from "../src/sync.mjs";
13
+
14
+ const VERSION = JSON.parse(readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../package.json"), "utf8")).version;
15
+
16
+ const HELP = `stackfit ${VERSION}: give each repo only the agent skills that fit its stack.
17
+
18
+ Usage: stackfit <command> [options]
19
+
20
+ Commands:
21
+ catalog init [dir] Create a catalog (default ~/.stackfit); --starter adds curated packs
22
+ report Per project: packs, skills, always-loaded tokens; plus global skills
23
+ status Dry run of sync; --check exits 1 when anything would change (CI)
24
+ sync Render the selected skills, rules, commands, and agents into projects
25
+ update git pull --ff-only the catalog, then sync
26
+ init Add tracked docs/agent scaffolding to projects; never overwrites
27
+ refresh Vendor upstream skills into the catalog (--skill <name> | --all)
28
+ audit Check the catalog: provenance, staleness, packs, context cost
29
+
30
+ Project selection:
31
+ --project <path> One repository
32
+ --all This machine's projects from stackfit.local.json
33
+ --root <dir>[,<dir>] Every Git repository directly under these folders
34
+
35
+ Options:
36
+ --catalog <dir> Catalog folder (else STACKFIT_CATALOG, the nearest stackfit.json, ~/.stackfit)
37
+ --dry-run Report without writing
38
+ --force Take ownership of colliding unmanaged files
39
+ --quiet Summaries only
40
+ --json Machine-readable output (report, audit)
41
+ --skip-pull update without git pull
42
+ --version, --help
43
+
44
+ Docs: https://github.com/amin0075/stackfit`;
45
+
46
+ function parseArgs(argv) {
47
+ const options = { positional: [] };
48
+ const flags = { "--all": "all", "--force": "force", "--dry-run": "dryRun", "--quiet": "quiet", "--skip-pull": "skipPull", "--check": "check", "--json": "json", "--starter": "starter", "--help": "help", "-h": "help", "--version": "version", "-v": "version" };
49
+ const values = { "--project": "project", "--root": "root", "--skill": "skill", "--catalog": "catalog" };
50
+ for (let index = 0; index < argv.length; index += 1) {
51
+ const arg = argv[index];
52
+ if (flags[arg]) options[flags[arg]] = true;
53
+ else if (values[arg]) {
54
+ if (argv[index + 1] === undefined) throw new Error(`${arg} needs a value`);
55
+ options[values[arg]] = argv[++index];
56
+ } else if (arg.startsWith("-")) throw new Error(`Unknown option: ${arg}`);
57
+ else options.positional.push(arg);
58
+ }
59
+ [options.command, ...options.args] = options.positional;
60
+ return options;
61
+ }
62
+
63
+ async function run(options) {
64
+ if (options.version) return console.log(VERSION);
65
+ if (options.help || !options.command) return console.log(HELP);
66
+ const { command } = options;
67
+
68
+ if (command === "catalog") {
69
+ if (options.args[0] !== "init") throw new Error("Usage: stackfit catalog init [dir] [--starter]");
70
+ return initCatalog(options.args[1], options);
71
+ }
72
+
73
+ const catalog = resolveCatalog(options.catalog);
74
+ if (command === "audit") {
75
+ const result = auditCatalog(catalog);
76
+ if (options.json) console.log(JSON.stringify(result, null, 2));
77
+ else printAudit(result);
78
+ if (result.errors.length) process.exitCode = 1;
79
+ return;
80
+ }
81
+ if (command === "refresh") return refreshSkills(catalog, options);
82
+ if (!["status", "sync", "update", "init", "report"].includes(command)) throw new Error(`Unknown command '${command}'. Run stackfit --help.`);
83
+
84
+ if (command === "update" && !options.skipPull) {
85
+ if (git(["status", "--porcelain"], catalog.root).stdout.trim()) throw new Error("Catalog has local changes; commit or stash them before update");
86
+ if (git(["pull", "--ff-only"], catalog.root, "inherit").status !== 0) throw new Error("Could not fast-forward the catalog");
87
+ }
88
+ const { projects, machine } = selectedProjects(catalog, options);
89
+ if (machine && !options.quiet && !options.json) console.log(`machine: ${machine.id} (${machine.platform}, ${machine.hostname})`);
90
+
91
+ if (command === "init") {
92
+ let total = 0;
93
+ for (const project of projects) total += await initProject(project.path, { ...options, scaffold: project.scaffold });
94
+ return console.log(`${options.dryRun ? "Would create" : "Created"} ${total} tracked docs file(s) across ${projects.length} project(s).`);
95
+ }
96
+
97
+ const registry = loadRegistry(catalog);
98
+ if (command === "report") {
99
+ const costs = skillCosts(catalog, registry);
100
+ const result = { projects: [], global: globalSkills() };
101
+ for (const project of projects) result.projects.push(await projectReport(catalog, registry, project, costs));
102
+ return options.json ? console.log(JSON.stringify(result, null, 2)) : printReport(result);
103
+ }
104
+
105
+ if (command === "status") options.dryRun = true;
106
+ let total = 0;
107
+ for (const project of projects) {
108
+ const plan = await createPlan(catalog, project.path, registry, { scaffold: project.scaffold, selection: project.selection });
109
+ const actions = await applyPlan(catalog, project.path, plan, options);
110
+ total += actions.length;
111
+ const suffix = project.scaffold ? "" : " [adapter-only]";
112
+ console.log(`${plan.projectName}: ${actions.length ? `${actions.length} change(s)` : "up to date"}${suffix}`);
113
+ if (!options.quiet) {
114
+ console.log(` packs: ${plan.packs.join(", ")}`);
115
+ for (const action of actions) console.log(` ${action.type.padEnd(6)} ${action.key}`);
116
+ }
117
+ }
118
+ console.log(`${options.dryRun ? "Would apply" : "Applied"} ${total} generated change(s) across ${projects.length} project(s).`);
119
+ if (options.check && total) process.exitCode = 1;
120
+ }
121
+
122
+ run(parseArgs(process.argv.slice(2))).catch((error) => {
123
+ console.error(`stackfit: ${error.message}`);
124
+ process.exitCode = 1;
125
+ });
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "stackfit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Give each repo only the AI agent skills that fit its stack. One catalog, every project, Claude Code + Cursor + Codex.",
5
+ "type": "module",
6
+ "bin": {
7
+ "stackfit": "./bin/stackfit.mjs"
8
+ },
9
+ "files": [
10
+ "CHANGELOG.md",
11
+ "LICENSE",
12
+ "README.md",
13
+ "bin",
14
+ "schemas",
15
+ "src",
16
+ "starter"
17
+ ],
18
+ "engines": {
19
+ "node": ">=22"
20
+ },
21
+ "scripts": {
22
+ "test": "node --test test/stackfit.test.mjs",
23
+ "check": "node --check bin/stackfit.mjs && node --test test/stackfit.test.mjs"
24
+ },
25
+ "keywords": [
26
+ "agent-skills",
27
+ "claude-code",
28
+ "cursor",
29
+ "codex",
30
+ "agents-md",
31
+ "skills",
32
+ "ai-agents",
33
+ "context",
34
+ "monorepo",
35
+ "nextjs"
36
+ ],
37
+ "author": "Amin Keshavarzi (https://github.com/amin0075)",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/amin0075/stackfit.git"
42
+ },
43
+ "homepage": "https://github.com/amin0075/stackfit#readme",
44
+ "bugs": "https://github.com/amin0075/stackfit/issues"
45
+ }
@@ -0,0 +1,71 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "stackfit project config (docs/agent/config.json)",
4
+ "type": "object",
5
+ "required": [
6
+ "schemaVersion"
7
+ ],
8
+ "properties": {
9
+ "schemaVersion": {
10
+ "const": 1
11
+ },
12
+ "packs": {
13
+ "$ref": "#/$defs/componentNames",
14
+ "description": "Packs to include in addition to auto-detected ones (e.g. \"motion\" for CSS-only animation work, \"design-research\")."
15
+ },
16
+ "excludePacks": {
17
+ "$ref": "#/$defs/componentNames",
18
+ "description": "Auto-detected packs to skip. `core` cannot be excluded."
19
+ },
20
+ "detect": {
21
+ "type": "boolean",
22
+ "default": true,
23
+ "description": "Set false to use only `core` plus the packs listed explicitly."
24
+ },
25
+ "skills": {
26
+ "$ref": "#/$defs/componentNames",
27
+ "description": "Individual catalog skills to add without taking their whole pack."
28
+ },
29
+ "disabledSharedSkills": {
30
+ "type": "array",
31
+ "items": {
32
+ "type": "string"
33
+ },
34
+ "uniqueItems": true,
35
+ "description": "Rare opt-out for a shared skill that is misleading in this repository. Applied last."
36
+ },
37
+ "overrides": {
38
+ "type": "object",
39
+ "description": "Explicit names of repo-owned components allowed to replace shared components.",
40
+ "properties": {
41
+ "skills": {
42
+ "$ref": "#/$defs/componentNames"
43
+ },
44
+ "agents": {
45
+ "$ref": "#/$defs/componentNames"
46
+ },
47
+ "commands": {
48
+ "$ref": "#/$defs/componentNames"
49
+ },
50
+ "rules": {
51
+ "$ref": "#/$defs/componentNames"
52
+ }
53
+ },
54
+ "additionalProperties": false
55
+ },
56
+ "$schema": {
57
+ "type": "string"
58
+ }
59
+ },
60
+ "$defs": {
61
+ "componentNames": {
62
+ "type": "array",
63
+ "items": {
64
+ "type": "string",
65
+ "minLength": 1
66
+ },
67
+ "uniqueItems": true
68
+ }
69
+ },
70
+ "additionalProperties": false
71
+ }
@@ -0,0 +1,97 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "stackfit skill provenance (skill-sources.json)",
4
+ "type": "object",
5
+ "required": [
6
+ "schemaVersion",
7
+ "skills"
8
+ ],
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string"
12
+ },
13
+ "$comment": {
14
+ "type": "string"
15
+ },
16
+ "schemaVersion": {
17
+ "const": 2
18
+ },
19
+ "skills": {
20
+ "type": "object",
21
+ "additionalProperties": {
22
+ "type": "object",
23
+ "required": [
24
+ "kind"
25
+ ],
26
+ "properties": {
27
+ "kind": {
28
+ "enum": [
29
+ "external",
30
+ "custom"
31
+ ],
32
+ "description": "external: vendored from upstream and refreshable. custom: owned in this catalog."
33
+ },
34
+ "source": {
35
+ "oneOf": [
36
+ {
37
+ "type": "null"
38
+ },
39
+ {
40
+ "type": "object",
41
+ "required": [
42
+ "repository",
43
+ "path"
44
+ ],
45
+ "properties": {
46
+ "repository": {
47
+ "type": "string"
48
+ },
49
+ "path": {
50
+ "type": "string"
51
+ }
52
+ },
53
+ "additionalProperties": false
54
+ }
55
+ ]
56
+ },
57
+ "reviewed": {
58
+ "type": "string",
59
+ "format": "date",
60
+ "description": "Last date a human checked the skill is current and worth its context. audit warns after 180 days."
61
+ },
62
+ "note": {
63
+ "type": "string"
64
+ },
65
+ "render": {
66
+ "type": "object",
67
+ "description": "Sync-time overrides that survive refreshes.",
68
+ "properties": {
69
+ "frontmatter": {
70
+ "type": "object",
71
+ "description": "SKILL.md frontmatter keys to replace (null removes a key).",
72
+ "additionalProperties": {
73
+ "type": [
74
+ "string",
75
+ "boolean",
76
+ "number",
77
+ "null"
78
+ ]
79
+ }
80
+ },
81
+ "exclude": {
82
+ "type": "array",
83
+ "items": {
84
+ "type": "string"
85
+ },
86
+ "description": "Skill-relative files not synced into projects."
87
+ }
88
+ },
89
+ "additionalProperties": false
90
+ }
91
+ },
92
+ "additionalProperties": false
93
+ }
94
+ }
95
+ },
96
+ "additionalProperties": false
97
+ }