skillmaxxing 0.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/.claude-plugin/marketplace.json +11 -0
- package/.claude-plugin/plugin.json +9 -0
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/dist/agents/claude.js +12 -0
- package/dist/agents/codex.js +12 -0
- package/dist/agents/cursor.js +12 -0
- package/dist/agents/hermes.js +12 -0
- package/dist/agents/opencode.js +12 -0
- package/dist/agents/registry.js +22 -0
- package/dist/agents/types.js +1 -0
- package/dist/cli.js +291 -0
- package/dist/commands/discover.js +76 -0
- package/dist/commands/doctor.js +84 -0
- package/dist/commands/init.js +47 -0
- package/dist/commands/install.js +74 -0
- package/dist/commands/list.js +74 -0
- package/dist/commands/optimize.js +152 -0
- package/dist/commands/plugin.js +232 -0
- package/dist/commands/remove.js +48 -0
- package/dist/commands/skillify.js +74 -0
- package/dist/commands/update.js +52 -0
- package/dist/commands/workspace.js +117 -0
- package/dist/create/match.js +23 -0
- package/dist/create/reflect.js +49 -0
- package/dist/create/skillify.js +117 -0
- package/dist/discover/collect.js +40 -0
- package/dist/discover/github.js +27 -0
- package/dist/discover/index.js +39 -0
- package/dist/discover/local.js +55 -0
- package/dist/discover/rank.js +63 -0
- package/dist/discover/types.js +1 -0
- package/dist/eval/runner.js +81 -0
- package/dist/eval/schema.js +78 -0
- package/dist/eval/scorers.js +19 -0
- package/dist/lock/global.js +53 -0
- package/dist/lock/project.js +67 -0
- package/dist/optimize/budget.js +22 -0
- package/dist/optimize/buffer.js +33 -0
- package/dist/optimize/diff.js +89 -0
- package/dist/optimize/loop.js +49 -0
- package/dist/plugin/guidance.js +30 -0
- package/dist/plugin/reflect.js +63 -0
- package/dist/plugin/sessions.js +58 -0
- package/dist/source/parser.js +63 -0
- package/dist/source/resolver.js +120 -0
- package/dist/state/store.js +120 -0
- package/dist/state/trust.js +31 -0
- package/dist/types.js +1 -0
- package/dist/util/collision.js +46 -0
- package/dist/util/exec.js +78 -0
- package/dist/util/frontmatter.js +72 -0
- package/dist/util/fs.js +77 -0
- package/dist/util/git.js +35 -0
- package/dist/util/log.js +33 -0
- package/dist/util/sanitize.js +36 -0
- package/dist/util/similarity.js +27 -0
- package/dist/util/versions.js +104 -0
- package/dist/workspace/channels.js +14 -0
- package/dist/workspace/collab.js +103 -0
- package/dist/workspace/registry.js +113 -0
- package/hooks/hooks.json +26 -0
- package/index/index.json +5 -0
- package/package.json +53 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skillmaxxing",
|
|
3
|
+
"owner": { "name": "Benny Jiang", "url": "https://github.com/Bennyoooo" },
|
|
4
|
+
"plugins": [
|
|
5
|
+
{
|
|
6
|
+
"name": "skillmaxxing",
|
|
7
|
+
"source": "./",
|
|
8
|
+
"description": "Self-evolving skills for your coding agent — auto-create and auto-improve skills as you work, no trigger needed."
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skillmaxxing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Self-evolving skills for your coding agent. Auto-creates and auto-improves skills as you work — no trigger needed.",
|
|
5
|
+
"author": { "name": "Benny Jiang" },
|
|
6
|
+
"homepage": "https://github.com/Bennyoooo/skillmaxxing",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": ["skills", "self-improving", "agent", "hooks", "claude-code"]
|
|
9
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benny Jiang
|
|
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
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/hero.svg" alt="SKILLMAXXING" width="760">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">SkillMaxxing</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<b>Self-evolving skills for your coding agent.</b><br>
|
|
9
|
+
Your agent auto-creates and auto-improves its own skills as it works — no command, no trigger, no babysitting.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="#-install-in-one-line"><img alt="install" src="https://img.shields.io/badge/install-one%20line-ff4d4d"></a>
|
|
14
|
+
<img alt="node" src="https://img.shields.io/badge/node-%E2%89%A520-2ec27e">
|
|
15
|
+
<img alt="deps" src="https://img.shields.io/badge/runtime%20deps-1-22b8cf">
|
|
16
|
+
<img alt="license" src="https://img.shields.io/badge/license-MIT-9b5de5">
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Why
|
|
22
|
+
|
|
23
|
+
Every coding agent starts every task from zero. It solves the same gnarly migration, re-derives the same release flow, re-learns the same repo quirk — and forgets it the moment the session ends.
|
|
24
|
+
|
|
25
|
+
**Skill Maxing makes the forgetting stop.** It hooks into your agent so that, after real work, the agent reflects on what it just did and crystallizes the reusable parts into a **skill** — or improves a skill it already has. Over days, your agent gets measurably better at *your* codebase. That's a self-evolving agent, and it takes one line to turn on.
|
|
26
|
+
|
|
27
|
+
Inspired by the [Hermes Agent](https://github.com/NousResearch/hermes-agent) self-improvement loop, adapted to run on the hooks that Claude Code, Codex, and other agents already expose.
|
|
28
|
+
|
|
29
|
+
## ⚡ Install in one line
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx skillmaxxing@latest plugin install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
That's it. Restart your agent session and Skill Maxing is live — **you never have to invoke anything.**
|
|
36
|
+
|
|
37
|
+
<details>
|
|
38
|
+
<summary>Claude Code, the native way</summary>
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
/plugin marketplace add Bennyoooo/skillmaxxing
|
|
42
|
+
/plugin install skillmaxxing
|
|
43
|
+
```
|
|
44
|
+
</details>
|
|
45
|
+
|
|
46
|
+
<details>
|
|
47
|
+
<summary>Codex / other agents</summary>
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx skillmaxxing@latest plugin install --agent codex
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Codex has no programmatic stop hook, so self-evolution runs **in-session** via standing guidance written to `AGENTS.md`. Claude Code gets the full background loop below.
|
|
54
|
+
</details>
|
|
55
|
+
|
|
56
|
+
Turn it off any time:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx skillmaxxing plugin uninstall # check state with: plugin status
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 🧠 How it works
|
|
63
|
+
|
|
64
|
+
Skill Maxing installs three hooks. You do nothing — the loop runs itself.
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
68
|
+
│ SessionStart → inject standing guidance │
|
|
69
|
+
│ "crystallize reusable work; fix stale skills"│
|
|
70
|
+
│ │
|
|
71
|
+
│ PostToolUse → count substantive tool calls this session │
|
|
72
|
+
│ │
|
|
73
|
+
│ Stop (task done) → enough work? fork a background reflector: │
|
|
74
|
+
│ review the transcript and, if warranted, │
|
|
75
|
+
│ create ONE new skill or improve an existing │
|
|
76
|
+
│ one — autonomously, trusted:false │
|
|
77
|
+
└──────────────────────────────────────────────────────────────┘
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This mirrors Hermes' three layers:
|
|
81
|
+
|
|
82
|
+
| Hermes | Skill Maxing |
|
|
83
|
+
|--------|--------------|
|
|
84
|
+
| Always-on system-prompt nudge | **SessionStart** hook injects skill-creation guidance |
|
|
85
|
+
| Background review every N iterations | **PostToolUse** counter + **Stop** hook fork a headless reflector after a threshold of real work |
|
|
86
|
+
| Provenance-gated curation | New/changed skills are recorded **`trusted: false`** until you approve them |
|
|
87
|
+
|
|
88
|
+
Two key Hermes ideas carry straight over: the reflector **prefers updating an existing skill over creating a near-duplicate**, and it is **conservative** — most sessions produce no skill at all.
|
|
89
|
+
|
|
90
|
+
### Two modes
|
|
91
|
+
|
|
92
|
+
| Mode | What happens on a substantial session | Pick it when |
|
|
93
|
+
|------|----------------------------------------|--------------|
|
|
94
|
+
| `auto` *(default)* | A **background** agent (`claude -p`, restricted to skill tools) reflects and writes/updates a skill while you keep working | You want truly hands-off self-evolution |
|
|
95
|
+
| `nudge` | The agent is reminded to crystallize the workflow itself, in-session | You want zero extra processes / full visibility |
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx skillmaxxing plugin install --mode nudge --threshold 12
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The background reflector is **recursion-guarded** (it can never trigger itself) and **detached** (it never blocks your session). Every skill it writes is `trusted: false` and never auto-executes until you grant trust.
|
|
102
|
+
|
|
103
|
+
## 🔧 The two superpowers
|
|
104
|
+
|
|
105
|
+
Everything above is built on two CLI primitives the reflector (or you) can call directly. The CLI is **model-agnostic** — it does the deterministic work; your agent supplies the reasoning.
|
|
106
|
+
|
|
107
|
+
**Create** — turn a workflow into a tested skill:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
skillmaxxing skillify --draft draft.json # stage → smoke-test → review → --commit
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Improve** — make an existing skill measurably better, safely:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
skillmaxxing optimize <score|apply|gate|promote|revert>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`optimize` is an **eval-gated** loop (rollout → reflect → bounded edit → validate): a candidate is promoted only on a strict score win with no regression, every version is retained, and any change is reversible.
|
|
120
|
+
|
|
121
|
+
## 🛟 Trust & safety
|
|
122
|
+
|
|
123
|
+
- **Untrusted by default.** Auto-created and improved skills are `trusted: false`; the sandbox refuses to run their code without your explicit `--allow-exec`.
|
|
124
|
+
- **Reversible.** Promotions are atomic and every prior version is retained — revert any time.
|
|
125
|
+
- **No surprise execution.** The background reflector writes skills; it does not run untrusted code or touch your project source.
|
|
126
|
+
|
|
127
|
+
## 🗺️ Roadmap
|
|
128
|
+
|
|
129
|
+
| Capability | Status |
|
|
130
|
+
|------------|--------|
|
|
131
|
+
| Auto-create skills (hook-driven) | ✅ v1 |
|
|
132
|
+
| Auto-improve skills (eval-gated) | ✅ v1 |
|
|
133
|
+
| Cross-agent install (Claude Code, Codex) | ✅ v1 |
|
|
134
|
+
| Discover skills from public sources | 🧰 CLI ready — landing in the plugin next |
|
|
135
|
+
| Team workspace: share + collaboratively optimize | 🧰 CLI ready — landing in the plugin next |
|
|
136
|
+
|
|
137
|
+
Discovery and team sharing already exist as CLI commands (`skillmaxxing discover`, `skillmaxxing workspace`); they're intentionally held out of the v1 plugin surface to keep the install dead-simple.
|
|
138
|
+
|
|
139
|
+
## 🛠️ Develop
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm install
|
|
143
|
+
npm run build # tsc -> dist/
|
|
144
|
+
npm test # node:test, ~90 tests
|
|
145
|
+
node scripts/gen-hero.mjs # regenerate the hero
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Zero runtime dependencies beyond `yaml`. ESM, Node ≥ 20.
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import { fileExists } from '../util/fs.js';
|
|
4
|
+
const claude = {
|
|
5
|
+
name: 'claude',
|
|
6
|
+
displayName: 'Claude Code',
|
|
7
|
+
cliCommand: 'claude',
|
|
8
|
+
globalSkillsDir: path.join(os.homedir(), '.claude', 'skills'),
|
|
9
|
+
projectSkillsDir: '.claude/skills',
|
|
10
|
+
detectInstalled: async () => fileExists(path.join(os.homedir(), '.claude')),
|
|
11
|
+
};
|
|
12
|
+
export default claude;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import { fileExists } from '../util/fs.js';
|
|
4
|
+
const codex = {
|
|
5
|
+
name: 'codex',
|
|
6
|
+
displayName: 'OpenAI Codex CLI',
|
|
7
|
+
cliCommand: 'codex',
|
|
8
|
+
globalSkillsDir: path.join(os.homedir(), '.codex', 'skills'),
|
|
9
|
+
projectSkillsDir: '.agents/skills',
|
|
10
|
+
detectInstalled: async () => fileExists(path.join(os.homedir(), '.codex')),
|
|
11
|
+
};
|
|
12
|
+
export default codex;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import { fileExists } from '../util/fs.js';
|
|
4
|
+
const cursor = {
|
|
5
|
+
name: 'cursor',
|
|
6
|
+
displayName: 'Cursor',
|
|
7
|
+
cliCommand: 'cursor',
|
|
8
|
+
globalSkillsDir: path.join(os.homedir(), '.cursor', 'skills'),
|
|
9
|
+
projectSkillsDir: '.cursor/skills',
|
|
10
|
+
detectInstalled: async () => fileExists(path.join(os.homedir(), '.cursor')),
|
|
11
|
+
};
|
|
12
|
+
export default cursor;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import { fileExists } from '../util/fs.js';
|
|
4
|
+
const hermes = {
|
|
5
|
+
name: 'hermes',
|
|
6
|
+
displayName: 'Hermes Agent',
|
|
7
|
+
cliCommand: 'hermes',
|
|
8
|
+
globalSkillsDir: path.join(os.homedir(), '.hermes', 'skills'),
|
|
9
|
+
projectSkillsDir: '.hermes/skills',
|
|
10
|
+
detectInstalled: async () => fileExists(path.join(os.homedir(), '.hermes')),
|
|
11
|
+
};
|
|
12
|
+
export default hermes;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import { fileExists } from '../util/fs.js';
|
|
4
|
+
const opencode = {
|
|
5
|
+
name: 'opencode',
|
|
6
|
+
displayName: 'OpenCode',
|
|
7
|
+
cliCommand: 'opencode',
|
|
8
|
+
globalSkillsDir: path.join(os.homedir(), '.config', 'opencode', 'skills'),
|
|
9
|
+
projectSkillsDir: '.opencode/skills',
|
|
10
|
+
detectInstalled: async () => fileExists(path.join(os.homedir(), '.config', 'opencode')),
|
|
11
|
+
};
|
|
12
|
+
export default opencode;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import claude from './claude.js';
|
|
2
|
+
import codex from './codex.js';
|
|
3
|
+
import cursor from './cursor.js';
|
|
4
|
+
import opencode from './opencode.js';
|
|
5
|
+
import hermes from './hermes.js';
|
|
6
|
+
export const ALL_AGENTS = [claude, codex, cursor, opencode, hermes];
|
|
7
|
+
const AGENT_MAP = new Map(ALL_AGENTS.map(a => [a.name, a]));
|
|
8
|
+
export function getAgent(name) {
|
|
9
|
+
return AGENT_MAP.get(name);
|
|
10
|
+
}
|
|
11
|
+
export function getAgentOrThrow(name) {
|
|
12
|
+
const agent = AGENT_MAP.get(name);
|
|
13
|
+
if (!agent) {
|
|
14
|
+
throw new Error(`Unknown agent '${name}'. Available: ${ALL_AGENTS.map(a => a.name).join(', ')}`);
|
|
15
|
+
}
|
|
16
|
+
return agent;
|
|
17
|
+
}
|
|
18
|
+
export async function detectInstalledAgents() {
|
|
19
|
+
const results = await Promise.all(ALL_AGENTS.map(async (a) => ({ agent: a, installed: await a.detectInstalled() })));
|
|
20
|
+
return results.filter(r => r.installed).map(r => r.agent);
|
|
21
|
+
}
|
|
22
|
+
export const ALL_AGENT_NAMES = ALL_AGENTS.map(a => a.name);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { install } from './commands/install.js';
|
|
3
|
+
import { discover } from './commands/discover.js';
|
|
4
|
+
import { skillify } from './commands/skillify.js';
|
|
5
|
+
import { optimize } from './commands/optimize.js';
|
|
6
|
+
import { workspace } from './commands/workspace.js';
|
|
7
|
+
import { plugin } from './commands/plugin.js';
|
|
8
|
+
import { list } from './commands/list.js';
|
|
9
|
+
import { remove } from './commands/remove.js';
|
|
10
|
+
import { update } from './commands/update.js';
|
|
11
|
+
import { init } from './commands/init.js';
|
|
12
|
+
import { doctor } from './commands/doctor.js';
|
|
13
|
+
import * as log from './util/log.js';
|
|
14
|
+
const VERSION = '0.1.0';
|
|
15
|
+
function parseFlags(argv) {
|
|
16
|
+
const positional = [];
|
|
17
|
+
const flags = {};
|
|
18
|
+
for (let i = 0; i < argv.length; i++) {
|
|
19
|
+
const arg = argv[i];
|
|
20
|
+
if (arg === '--') {
|
|
21
|
+
positional.push(...argv.slice(i + 1));
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
if (arg.startsWith('--')) {
|
|
25
|
+
const eq = arg.indexOf('=');
|
|
26
|
+
if (eq !== -1) {
|
|
27
|
+
flags[arg.substring(2, eq)] = arg.substring(eq + 1);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const next = argv[i + 1];
|
|
31
|
+
if (next && !next.startsWith('-')) {
|
|
32
|
+
flags[arg.substring(2)] = next;
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
flags[arg.substring(2)] = true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (arg.startsWith('-') && arg.length === 2) {
|
|
41
|
+
const shortMap = { g: 'global', a: 'agent', s: 'scope', y: 'yes', h: 'help' };
|
|
42
|
+
const long = shortMap[arg[1]] ?? arg[1];
|
|
43
|
+
const next = argv[i + 1];
|
|
44
|
+
if (next && !next.startsWith('-')) {
|
|
45
|
+
flags[long] = next;
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
flags[long] = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
positional.push(arg);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { positional, flags };
|
|
57
|
+
}
|
|
58
|
+
function printHelp() {
|
|
59
|
+
console.log(`skill-maxing v${VERSION}
|
|
60
|
+
|
|
61
|
+
A stack for installing, creating, improving, and governing AI agent skills.
|
|
62
|
+
|
|
63
|
+
Usage:
|
|
64
|
+
skill-maxing <command> [options]
|
|
65
|
+
|
|
66
|
+
Commands:
|
|
67
|
+
plugin <action> Self-evolving plugin: install|uninstall|status (hooks, no trigger)
|
|
68
|
+
install <source> Install skills from GitHub, URL, or local path
|
|
69
|
+
discover <query> Find skills by intent (curated index, repos, local)
|
|
70
|
+
skillify Create a skill from a draft (--draft/--commit/--list-drafts)
|
|
71
|
+
optimize <action> Eval-gated optimize: score|apply|gate|promote|revert
|
|
72
|
+
workspace <action> Team registry: publish|sync|list|pool|promote
|
|
73
|
+
list List installed skills
|
|
74
|
+
remove <names...> Remove installed skills
|
|
75
|
+
update [names...] Update installed skills to latest
|
|
76
|
+
init [name] Create a new skill template
|
|
77
|
+
doctor Check agent integrations and skill health
|
|
78
|
+
|
|
79
|
+
Options:
|
|
80
|
+
-g, --global Install/operate at global scope (default: project)
|
|
81
|
+
-a, --agent <name> Target specific agent (claude, codex, cursor, opencode, hermes)
|
|
82
|
+
--copy Force copy instead of symlink
|
|
83
|
+
--force Overwrite an existing unmanaged skill of the same name
|
|
84
|
+
--repo <owner/repo> Extra repo source(s) to scan during discover (comma-sep)
|
|
85
|
+
--limit <n> Max discover results (default 20)
|
|
86
|
+
--install <name> Install a named result (discover command)
|
|
87
|
+
--json Output as JSON (list/discover commands)
|
|
88
|
+
-y, --yes Skip confirmation prompts
|
|
89
|
+
-h, --help Show help
|
|
90
|
+
--version Show version
|
|
91
|
+
|
|
92
|
+
Examples:
|
|
93
|
+
skill-maxing install owner/repo Install skills from GitHub
|
|
94
|
+
skill-maxing install ./my-skills -g Install local skills globally
|
|
95
|
+
skill-maxing install owner/repo -a claude Install for Claude Code only
|
|
96
|
+
skill-maxing list List all installed skills
|
|
97
|
+
skill-maxing list -g List global skills only
|
|
98
|
+
skill-maxing remove my-skill Remove a skill
|
|
99
|
+
skill-maxing update Update all installed skills
|
|
100
|
+
skill-maxing init my-new-skill Create a skill template
|
|
101
|
+
skill-maxing doctor Check health
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
async function main() {
|
|
105
|
+
const { positional, flags } = parseFlags(process.argv.slice(2));
|
|
106
|
+
const command = positional[0];
|
|
107
|
+
if (flags.version === true) {
|
|
108
|
+
console.log(VERSION);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!command || flags.help) {
|
|
112
|
+
printHelp();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const scope = flags.global === true ? 'global' : 'project';
|
|
116
|
+
const agentFlag = typeof flags.agent === 'string' ? flags.agent : undefined;
|
|
117
|
+
const agents = agentFlag ? agentFlag.split(',') : undefined;
|
|
118
|
+
try {
|
|
119
|
+
switch (command) {
|
|
120
|
+
case 'install':
|
|
121
|
+
case 'add':
|
|
122
|
+
case 'i': {
|
|
123
|
+
const source = positional[1];
|
|
124
|
+
if (!source) {
|
|
125
|
+
log.error('Usage: skill-maxing install <source>');
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
await install({ source, agents, scope, copy: flags.copy === true, force: flags.force === true });
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case 'discover':
|
|
132
|
+
case 'search':
|
|
133
|
+
case 'find': {
|
|
134
|
+
const query = positional.slice(1).join(' ');
|
|
135
|
+
if (!query) {
|
|
136
|
+
log.error('Usage: skill-maxing discover "<what you want>" [--repo owner/repo] [--install <name>]');
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
const repos = typeof flags.repo === 'string' ? flags.repo.split(',') : undefined;
|
|
140
|
+
const limit = typeof flags.limit === 'string' ? parseInt(flags.limit, 10) : undefined;
|
|
141
|
+
await discover({
|
|
142
|
+
query,
|
|
143
|
+
repos,
|
|
144
|
+
json: flags.json === true,
|
|
145
|
+
limit: Number.isNaN(limit) ? undefined : limit,
|
|
146
|
+
install: typeof flags.install === 'string' ? flags.install : undefined,
|
|
147
|
+
scope,
|
|
148
|
+
agents,
|
|
149
|
+
copy: flags.copy === true,
|
|
150
|
+
force: flags.force === true,
|
|
151
|
+
});
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case 'skillify':
|
|
155
|
+
case 'create': {
|
|
156
|
+
await skillify({
|
|
157
|
+
draftPath: typeof flags.draft === 'string' ? flags.draft : undefined,
|
|
158
|
+
commit: typeof flags.commit === 'string' ? flags.commit : undefined,
|
|
159
|
+
listDrafts: flags['list-drafts'] === true,
|
|
160
|
+
discard: typeof flags.discard === 'string' ? flags.discard : undefined,
|
|
161
|
+
allowExec: flags['allow-exec'] === true,
|
|
162
|
+
forceNew: flags.new === true,
|
|
163
|
+
scope,
|
|
164
|
+
agents,
|
|
165
|
+
copy: flags.copy === true,
|
|
166
|
+
force: flags.force === true,
|
|
167
|
+
});
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 'optimize':
|
|
171
|
+
case 'opt': {
|
|
172
|
+
const action = positional[1];
|
|
173
|
+
if (!action) {
|
|
174
|
+
log.error('Usage: skill-maxing optimize <score|apply|gate|promote|revert> [options]');
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
const num = (k) => {
|
|
178
|
+
if (typeof flags[k] !== 'string')
|
|
179
|
+
return undefined;
|
|
180
|
+
const n = Number(flags[k]);
|
|
181
|
+
return Number.isNaN(n) ? undefined : n; // a non-numeric flag is not a silent 0/NaN
|
|
182
|
+
};
|
|
183
|
+
const str = (k) => typeof flags[k] === 'string' ? flags[k] : undefined;
|
|
184
|
+
await optimize({
|
|
185
|
+
action,
|
|
186
|
+
skillName: str('skill'),
|
|
187
|
+
skillDir: str('skill-dir'),
|
|
188
|
+
editsPath: str('edits'),
|
|
189
|
+
evalPath: str('eval'),
|
|
190
|
+
rolloutsPath: str('rollouts'),
|
|
191
|
+
liveDir: str('live'),
|
|
192
|
+
candidateDir: str('candidate'),
|
|
193
|
+
version: str('version'),
|
|
194
|
+
step: num('step'),
|
|
195
|
+
total: num('total'),
|
|
196
|
+
base: num('base'),
|
|
197
|
+
min: num('min'),
|
|
198
|
+
scheduler: str('scheduler'),
|
|
199
|
+
current: num('current'),
|
|
200
|
+
candidate: num('candidate'),
|
|
201
|
+
best: num('best'),
|
|
202
|
+
score: num('score'),
|
|
203
|
+
allowExec: flags['allow-exec'] === true,
|
|
204
|
+
json: flags.json === true,
|
|
205
|
+
});
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
case 'workspace':
|
|
209
|
+
case 'ws': {
|
|
210
|
+
const action = positional[1];
|
|
211
|
+
if (!action) {
|
|
212
|
+
log.error('Usage: skill-maxing workspace <publish|sync|list|pool|promote> --registry <dir> [options]');
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
const str = (k) => typeof flags[k] === 'string' ? flags[k] : undefined;
|
|
216
|
+
await workspace({
|
|
217
|
+
action,
|
|
218
|
+
registryDir: str('registry'),
|
|
219
|
+
skillDir: str('skill-dir'),
|
|
220
|
+
skillName: str('skill'),
|
|
221
|
+
channel: str('channel'),
|
|
222
|
+
by: str('by'),
|
|
223
|
+
approver: str('approver'),
|
|
224
|
+
approve: flags.approve === true,
|
|
225
|
+
score: typeof flags.score === 'string' && !Number.isNaN(Number(flags.score))
|
|
226
|
+
? Number(flags.score)
|
|
227
|
+
: undefined,
|
|
228
|
+
json: flags.json === true,
|
|
229
|
+
});
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case 'plugin': {
|
|
233
|
+
const action = positional[1];
|
|
234
|
+
if (!action) {
|
|
235
|
+
log.error('Usage: skill-maxing plugin <install|uninstall|status> [--agent claude|codex] [--mode auto|nudge] [--threshold N] [--project]');
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
const pAgent = agentFlag === 'codex' ? 'codex' : agentFlag === 'claude' ? 'claude' : undefined;
|
|
239
|
+
const pMode = flags.mode === 'nudge' ? 'nudge' : flags.mode === 'auto' ? 'auto' : undefined;
|
|
240
|
+
const pThreshold = typeof flags.threshold === 'string' && !Number.isNaN(Number(flags.threshold))
|
|
241
|
+
? Number(flags.threshold)
|
|
242
|
+
: undefined;
|
|
243
|
+
await plugin({
|
|
244
|
+
action,
|
|
245
|
+
agent: pAgent,
|
|
246
|
+
mode: pMode,
|
|
247
|
+
threshold: pThreshold,
|
|
248
|
+
project: flags.project === true,
|
|
249
|
+
});
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
case 'list':
|
|
253
|
+
case 'ls':
|
|
254
|
+
await list({ agent: agentFlag, scope: flags.global === true ? 'global' : undefined, json: flags.json === true });
|
|
255
|
+
break;
|
|
256
|
+
case 'remove':
|
|
257
|
+
case 'rm': {
|
|
258
|
+
const names = positional.slice(1);
|
|
259
|
+
if (names.length === 0) {
|
|
260
|
+
log.error('Usage: skill-maxing remove <skill-name> [skill-name...]');
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
await remove({ names, agent: agentFlag, scope: flags.global === true ? 'global' : undefined });
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
case 'update':
|
|
267
|
+
case 'upgrade': {
|
|
268
|
+
const names = positional.slice(1);
|
|
269
|
+
await update({ names: names.length > 0 ? names : undefined, scope: flags.global === true ? 'global' : undefined });
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
case 'init': {
|
|
273
|
+
const name = positional[1];
|
|
274
|
+
await init({ name });
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
case 'doctor':
|
|
278
|
+
await doctor();
|
|
279
|
+
break;
|
|
280
|
+
default:
|
|
281
|
+
log.error(`Unknown command: ${command}`);
|
|
282
|
+
printHelp();
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
main();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { collectSources } from '../discover/collect.js';
|
|
2
|
+
import { rankCandidates } from '../discover/rank.js';
|
|
3
|
+
import { install } from './install.js';
|
|
4
|
+
import * as log from '../util/log.js';
|
|
5
|
+
function truncate(s, n) {
|
|
6
|
+
return s.length > n ? s.slice(0, n - 1) + '…' : s;
|
|
7
|
+
}
|
|
8
|
+
export async function discover(args) {
|
|
9
|
+
const { candidates, errors } = await collectSources({
|
|
10
|
+
repos: args.repos,
|
|
11
|
+
projectDir: process.cwd(),
|
|
12
|
+
});
|
|
13
|
+
for (const e of errors)
|
|
14
|
+
log.warn(`Source '${e.source}' unavailable: ${e.message}`);
|
|
15
|
+
let ranked = rankCandidates(args.query, candidates);
|
|
16
|
+
if (args.query.trim())
|
|
17
|
+
ranked = ranked.filter((r) => r.score > 0);
|
|
18
|
+
const limited = ranked.slice(0, args.limit ?? 20);
|
|
19
|
+
if (limited.length === 0) {
|
|
20
|
+
log.warn(`No skills matched "${args.query}".`);
|
|
21
|
+
process.exitCode = 1; // explicit non-empty contract (review I2)
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Discover → install handoff.
|
|
25
|
+
if (args.install) {
|
|
26
|
+
const pick = limited.find((r) => r.name.toLowerCase() === args.install.toLowerCase());
|
|
27
|
+
if (!pick) {
|
|
28
|
+
log.error(`"${args.install}" is not in the results.`);
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!pick.source) {
|
|
33
|
+
if (pick.installed) {
|
|
34
|
+
log.info(`${pick.name} is already installed locally.`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
log.error(`${pick.name} has no installable source.`);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// NOTE: install re-resolves the source to HEAD. Exact pinned-commit install
|
|
42
|
+
// (using pick.commitSha) needs a fetch-by-SHA path the depth-1 clone can't
|
|
43
|
+
// do today — tracked as a known gap (review F2). We surface the resolved
|
|
44
|
+
// commit for provenance.
|
|
45
|
+
if (pick.commitSha) {
|
|
46
|
+
log.info(`${pick.name} discovered at ${pick.commitSha.slice(0, 10)}; installing from ${pick.source}.`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
log.info(`Installing ${pick.name} from ${pick.source}...`);
|
|
50
|
+
}
|
|
51
|
+
await install({
|
|
52
|
+
source: pick.source,
|
|
53
|
+
scope: args.scope,
|
|
54
|
+
agents: args.agents,
|
|
55
|
+
copy: args.copy,
|
|
56
|
+
force: args.force,
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (args.json) {
|
|
61
|
+
console.log(JSON.stringify(limited, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
log.heading(`Skills matching "${args.query}" (${limited.length})`);
|
|
65
|
+
log.table([
|
|
66
|
+
['', 'name', 'origin', 'source', 'description'],
|
|
67
|
+
...limited.map((r) => [
|
|
68
|
+
r.installed ? '✓' : ' ',
|
|
69
|
+
r.name,
|
|
70
|
+
r.origin,
|
|
71
|
+
r.source || '(local)',
|
|
72
|
+
truncate(r.description, 50),
|
|
73
|
+
]),
|
|
74
|
+
]);
|
|
75
|
+
log.info(`Install one with: skill-maxing discover "${args.query}" --install <name>`);
|
|
76
|
+
}
|