activate-agentmd 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -0
- package/bin/agentmd.js +186 -0
- package/package.json +55 -0
- package/src/commands/agents.js +44 -0
- package/src/commands/analytics.js +128 -0
- package/src/commands/auth.js +144 -0
- package/src/commands/ci.js +296 -0
- package/src/commands/extract.js +254 -0
- package/src/commands/info.js +105 -0
- package/src/commands/init.js +166 -0
- package/src/commands/install.js +216 -0
- package/src/commands/link.js +206 -0
- package/src/commands/list.js +99 -0
- package/src/commands/outdated.js +92 -0
- package/src/commands/remove.js +72 -0
- package/src/commands/review.js +293 -0
- package/src/commands/search.js +110 -0
- package/src/commands/sync.js +245 -0
- package/src/commands/telemetry.js +82 -0
- package/src/commands/test.js +226 -0
- package/src/commands/update.js +115 -0
- package/src/commands/validate.js +125 -0
- package/src/config/license-public-keys.json +17 -0
- package/src/detect.json +452 -0
- package/src/lib/agents.js +301 -0
- package/src/lib/analytics.js +137 -0
- package/src/lib/coordinates.js +103 -0
- package/src/lib/credentials.js +115 -0
- package/src/lib/detect.js +352 -0
- package/src/lib/diff.js +142 -0
- package/src/lib/enterprise.js +140 -0
- package/src/lib/fetcher.js +174 -0
- package/src/lib/invocation.js +47 -0
- package/src/lib/license.js +234 -0
- package/src/lib/manifest.js +191 -0
- package/src/lib/patterns.js +135 -0
- package/src/lib/pro.js +149 -0
- package/src/lib/ranking.js +287 -0
- package/src/lib/registry.js +207 -0
- package/src/lib/star.js +105 -0
- package/src/lib/status.js +75 -0
- package/src/lib/telemetry.js +169 -0
- package/src/lib/versions.js +190 -0
- package/src/registry.json +33907 -0
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# activate-agentmd
|
|
2
|
+
|
|
3
|
+
> The package manager for AI coding-agent engineering standards.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx activate-agentmd init
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/activate-agentmd)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
Scans your project, shows what it found, and installs the standards that match.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
agentmd scanning project…
|
|
16
|
+
|
|
17
|
+
✓ Docker Dockerfile
|
|
18
|
+
✓ TypeScript tsconfig.json
|
|
19
|
+
✓ Prisma prisma/
|
|
20
|
+
✓ Next.js package.json (next)
|
|
21
|
+
✓ PostgreSQL package.json (pg)
|
|
22
|
+
|
|
23
|
+
Recommended packages for claude:
|
|
24
|
+
|
|
25
|
+
Backend nextjs
|
|
26
|
+
Database prisma, orm, migration, schema-design, postgres, indexes
|
|
27
|
+
Frontend typescript, nextjs, server-components, routing, react
|
|
28
|
+
Security authentication, authorization, oauth, owasp
|
|
29
|
+
Testing unit, test-strategy, e2e, accessibility
|
|
30
|
+
|
|
31
|
+
31 to install
|
|
32
|
+
|
|
33
|
+
Install these? [Y/n]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then wire them into whichever agent you use:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx activate-agentmd link
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
That writes the installed package list into `CLAUDE.md`, `AGENTS.md`,
|
|
43
|
+
`.cursor/rules/agentmd.mdc`, `GEMINI.md` or
|
|
44
|
+
`.github/copilot-instructions.md` — inside a delimited block, so your own
|
|
45
|
+
content is never touched.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Commands
|
|
50
|
+
|
|
51
|
+
| Command | What it does |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `init` | Scan the project, install what matches |
|
|
54
|
+
| `link` | Wire installed packages into your agent config files |
|
|
55
|
+
| `search <query>` | Search names, categories, descriptions |
|
|
56
|
+
| `list [model/category]` | Browse the registry |
|
|
57
|
+
| `list --installed` | What this project has |
|
|
58
|
+
| `info <package>` | Details for one package |
|
|
59
|
+
| `install <package>` | Install a package, or a whole category |
|
|
60
|
+
| `remove <package>` | Uninstall |
|
|
61
|
+
| `outdated` | What's changed upstream, and what you've edited |
|
|
62
|
+
| `update` | Pull current versions |
|
|
63
|
+
| `validate` | Check registry + manifest integrity |
|
|
64
|
+
| `agents` | List supported agent config targets |
|
|
65
|
+
|
|
66
|
+
Package paths are `<model>/<category>/<package>`. A trailing slash installs the
|
|
67
|
+
whole category:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
agentmd install claude/Security/owasp # one
|
|
71
|
+
agentmd install claude/Testing/ # all 11
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Full reference:
|
|
75
|
+
[docs/cli.md](https://github.com/Aaditya1273/Agent.md/blob/main/docs/cli.md)
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Updates respect your edits
|
|
80
|
+
|
|
81
|
+
`agentmd update` never overwrites a package you've edited. It reports the
|
|
82
|
+
conflict and leaves it alone until you pass `--force`.
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
$ agentmd outdated
|
|
86
|
+
|
|
87
|
+
↑ claude/Testing/unit update available
|
|
88
|
+
✎ claude/Security/owasp edited locally
|
|
89
|
+
|
|
90
|
+
1 outdated, 1 modified, 24 current
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The distinction comes from a SHA-256 checksum recorded at install time.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Where things go
|
|
98
|
+
|
|
99
|
+
| Path | What |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| `.agentmd/presets/` | Package content |
|
|
102
|
+
| `.agentmd/manifest.json` | What's installed, with version, checksum and source |
|
|
103
|
+
|
|
104
|
+
Commit both — that's how a team shares one set of standards, and how changes
|
|
105
|
+
to your agent's instructions show up in code review.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Registry
|
|
110
|
+
|
|
111
|
+
304 canonical standards, tuned for 11 model families (3,344 total):
|
|
112
|
+
`claude`, `deepseek`, `gemini`, `glm`, `grok`, `kimi`, `minimax`, `mistral`, `open-ai`, `qwen` and `sarvam-ai`.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
agentmd list claude # all categories
|
|
116
|
+
agentmd list claude/Security # packages, with descriptions
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Security
|
|
122
|
+
|
|
123
|
+
This CLI distributes content your agent reads as instructions, so:
|
|
124
|
+
|
|
125
|
+
- Package content is **never executed** — it's markdown written to disk.
|
|
126
|
+
- HTTPS only; a redirect to plaintext HTTP is refused.
|
|
127
|
+
- Writes are resolved against `.agentmd/presets/` and refuse to escape it.
|
|
128
|
+
- Fetches are bounded by timeout, redirect count and response size.
|
|
129
|
+
- No accounts, no telemetry, one runtime dependency (`picocolors`).
|
|
130
|
+
|
|
131
|
+
Packages are not signed — see
|
|
132
|
+
[SECURITY.md](https://github.com/Aaditya1273/Agent.md/blob/main/SECURITY.md)
|
|
133
|
+
for the full threat model, including what is *not* guaranteed.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Environment
|
|
138
|
+
|
|
139
|
+
| Variable | Meaning |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| `AGENTMD_REGISTRY_BASE` | Override the registry base URL (mirrors, testing) |
|
|
142
|
+
| `AGENTMD_TIMEOUT_MS` | Per-request timeout, default 30000 |
|
|
143
|
+
| `AGENTMD_DEBUG` | Print a stack trace on error |
|
|
144
|
+
|
|
145
|
+
Requires Node 18+.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Contributing
|
|
150
|
+
|
|
151
|
+
Adding a package is a directory and one markdown file — see
|
|
152
|
+
[CONTRIBUTING.md](https://github.com/Aaditya1273/Agent.md/blob/main/CONTRIBUTING.md).
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
cd packages/cli && npm ci && npm test
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
MIT licensed.
|
package/bin/agentmd.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* agentmd — the package manager for AI coding-agent engineering standards.
|
|
5
|
+
*
|
|
6
|
+
* Packages are markdown. `install` fetches them into .agentmd/presets/,
|
|
7
|
+
* `link` wires them into whichever agent config conventions your project
|
|
8
|
+
* uses, and `update`/`outdated` keep them current.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
const pc = require("picocolors");
|
|
14
|
+
const pkg = require("../package.json");
|
|
15
|
+
const { cmd } = require("../src/lib/invocation");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* How the user invoked us. `npx activate-agentmd` does not put `agentmd` on PATH,
|
|
19
|
+
* so every example below has to match the form they actually typed — otherwise
|
|
20
|
+
* the first thing they copy out of --help is `command not found`.
|
|
21
|
+
*/
|
|
22
|
+
const A = cmd();
|
|
23
|
+
|
|
24
|
+
const HELP = `
|
|
25
|
+
${pc.bold("agentmd")} ${pc.dim("v" + pkg.version)} — engineering standards for AI coding agents
|
|
26
|
+
|
|
27
|
+
${pc.bold("USAGE")}
|
|
28
|
+
${A} <command> [options]
|
|
29
|
+
|
|
30
|
+
${pc.bold("GETTING STARTED")}
|
|
31
|
+
${pc.cyan("init")} Scan this project and install what matches
|
|
32
|
+
${pc.cyan("link")} Wire installed packages into your agent config files
|
|
33
|
+
${pc.cyan("extract")} ${pc.yellow("PRO")} Derive this repo's unwritten conventions into a local standard
|
|
34
|
+
${pc.cyan("review")} ${pc.white("[--base ref]")} Check a diff against the installed standards (--fast: offline patterns, --local: Ollama)
|
|
35
|
+
${pc.cyan("test")} Benchmark whether the model actually obeys the installed standards
|
|
36
|
+
|
|
37
|
+
${pc.bold("FINDING PACKAGES")}
|
|
38
|
+
${pc.cyan("search")} ${pc.white("<query>")} Search names, categories and descriptions
|
|
39
|
+
${pc.cyan("list")} ${pc.white("[model[/category]]")} Browse the registry
|
|
40
|
+
${pc.cyan("list")} ${pc.white("--installed")} Show what this project has
|
|
41
|
+
${pc.cyan("info")} ${pc.white("<package>")} Details for one package
|
|
42
|
+
|
|
43
|
+
${pc.bold("MANAGING PACKAGES")}
|
|
44
|
+
${pc.cyan("install")} ${pc.white("<package>…")} Install packages, or a whole category
|
|
45
|
+
${pc.cyan("remove")} ${pc.white("<package>")} Uninstall a package
|
|
46
|
+
${pc.cyan("outdated")} Show packages that have changed upstream
|
|
47
|
+
${pc.cyan("update")} Pull the current version of each package
|
|
48
|
+
${pc.cyan("validate")} Check registry and manifest integrity
|
|
49
|
+
${pc.cyan("agents")} List supported agent config targets
|
|
50
|
+
${pc.cyan("ci")} Write or check the CI workflow (${pc.yellow("--cloud")}: hosted, private preview)
|
|
51
|
+
|
|
52
|
+
${pc.bold("ACCOUNT")}
|
|
53
|
+
${pc.cyan("login")} ${pc.white("[--key <key>]")} Sign in with an Agent.md Pro license key
|
|
54
|
+
${pc.cyan("logout")} Forget the stored key
|
|
55
|
+
${pc.cyan("whoami")} Show the current plan
|
|
56
|
+
${pc.cyan("sync")} ${pc.white("--private")} ${pc.yellow("PRO")} Push or pull your company's own standards
|
|
57
|
+
${pc.cyan("analytics")} ${pc.yellow("PRO")} What your standards actually caught
|
|
58
|
+
${pc.cyan("telemetry")} Anonymous usage counters (off by default)
|
|
59
|
+
|
|
60
|
+
${pc.bold("PACKAGE PATHS")}
|
|
61
|
+
${pc.white("<model>/<category>/<package>")} e.g. ${pc.white("claude/Security/owasp")}
|
|
62
|
+
${pc.white("<model>/<category>/")} installs the whole category
|
|
63
|
+
|
|
64
|
+
${pc.bold("OPTIONS")}
|
|
65
|
+
--force Overwrite existing files / local edits
|
|
66
|
+
--dry Show what would happen, change nothing
|
|
67
|
+
--yes, -y Skip the confirmation prompt (init)
|
|
68
|
+
--model=NAME Choose a model (default: claude)
|
|
69
|
+
--agent=NAME Choose link targets, comma-separated, or "all"
|
|
70
|
+
--help, -h Show this help
|
|
71
|
+
--version, -v Show the version
|
|
72
|
+
|
|
73
|
+
${pc.bold("FREE AND PRO")}
|
|
74
|
+
${pc.dim("Everything local is free, forever: init, link, install, update, list, search,")}
|
|
75
|
+
${pc.dim("review --fast and review --local. No account, no key, no telemetry.")}
|
|
76
|
+
${pc.dim("Pro adds extract, private sync, analytics and cloud CI — " + (process.env.AGENTMD_PRO_URL || "https://agent-dot-md.vercel.app/pro"))}
|
|
77
|
+
${pc.dim("In CI, set AGENTMD_PRO_KEY instead of logging in.")}
|
|
78
|
+
|
|
79
|
+
${pc.bold("EXAMPLES")}
|
|
80
|
+
${pc.dim("# The one-minute path")}
|
|
81
|
+
${A} init
|
|
82
|
+
|
|
83
|
+
${pc.dim("# Find and install a specific standard")}
|
|
84
|
+
${A} search authentication
|
|
85
|
+
${A} install claude/Security/owasp
|
|
86
|
+
${A} link
|
|
87
|
+
|
|
88
|
+
${pc.dim("# Install every Testing standard, then keep it current")}
|
|
89
|
+
${A} install claude/Testing/
|
|
90
|
+
${A} outdated
|
|
91
|
+
|
|
92
|
+
${pc.dim(" Files are written to .agentmd/presets/, tracked in .agentmd/manifest.json")}
|
|
93
|
+
${pc.dim(" Docs: https://github.com/Aaditya1273/Agent.md")}
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
/** Loaded lazily so `--help` and `--version` never parse the 348 KB index. */
|
|
97
|
+
const { requirePro } = require("../src/lib/pro");
|
|
98
|
+
|
|
99
|
+
const COMMANDS = {
|
|
100
|
+
init: () => require("../src/commands/init").init,
|
|
101
|
+
link: () => require("../src/commands/link").link,
|
|
102
|
+
install: () => require("../src/commands/install").install,
|
|
103
|
+
remove: () => require("../src/commands/remove").remove,
|
|
104
|
+
list: () => require("../src/commands/list").list,
|
|
105
|
+
search: () => require("../src/commands/search").search,
|
|
106
|
+
info: () => require("../src/commands/info").info,
|
|
107
|
+
outdated: () => require("../src/commands/outdated").outdated,
|
|
108
|
+
update: () => require("../src/commands/update").update,
|
|
109
|
+
validate: () => require("../src/commands/validate").validate,
|
|
110
|
+
agents: () => require("../src/commands/agents").agents,
|
|
111
|
+
review: () => require("../src/commands/review").review,
|
|
112
|
+
test: () => require("../src/commands/test").test,
|
|
113
|
+
ci: () => require("../src/commands/ci").ci,
|
|
114
|
+
|
|
115
|
+
// Account.
|
|
116
|
+
login: () => require("../src/commands/auth").login,
|
|
117
|
+
logout: () => require("../src/commands/auth").logout,
|
|
118
|
+
whoami: () => require("../src/commands/auth").whoami,
|
|
119
|
+
telemetry: () => require("../src/commands/telemetry").telemetry,
|
|
120
|
+
|
|
121
|
+
// Pro. The gate lives here, in one table, rather than inside each command —
|
|
122
|
+
// so what is paid for can be read at a glance and a command cannot
|
|
123
|
+
// accidentally half-gate itself. `ci --cloud` gates inside ci.js instead,
|
|
124
|
+
// because the command itself is free and only that one flag is not.
|
|
125
|
+
extract: () => requirePro("extract", require("../src/commands/extract").extract),
|
|
126
|
+
sync: () => requirePro("sync:private", require("../src/commands/sync").sync),
|
|
127
|
+
analytics: () => requirePro("analytics", require("../src/commands/analytics").analytics),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** Short forms, plus the names other package managers train people to type. */
|
|
131
|
+
const ALIASES = {
|
|
132
|
+
i: "install",
|
|
133
|
+
add: "install",
|
|
134
|
+
rm: "remove",
|
|
135
|
+
uninstall: "remove",
|
|
136
|
+
ls: "list",
|
|
137
|
+
s: "search",
|
|
138
|
+
find: "search",
|
|
139
|
+
show: "info",
|
|
140
|
+
up: "update",
|
|
141
|
+
upgrade: "update",
|
|
142
|
+
check: "outdated",
|
|
143
|
+
lint: "validate",
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
async function main() {
|
|
147
|
+
const argv = process.argv.slice(2);
|
|
148
|
+
|
|
149
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
150
|
+
console.log(HELP);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (argv.includes("--version") || argv.includes("-v")) {
|
|
154
|
+
console.log(pkg.version);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const name = ALIASES[argv[0]] || argv[0];
|
|
159
|
+
const load = Object.prototype.hasOwnProperty.call(COMMANDS, name) ? COMMANDS[name] : null;
|
|
160
|
+
|
|
161
|
+
if (!load) {
|
|
162
|
+
console.error(pc.red(`Unknown command: "${argv[0]}"`));
|
|
163
|
+
console.error("");
|
|
164
|
+
console.error(pc.dim(" Available: " + Object.keys(COMMANDS).join(", ")));
|
|
165
|
+
console.error(pc.dim(` Run '${A} --help' for usage.`));
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Telemetry is off unless the user opted in; `timed` is a no-op until then.
|
|
170
|
+
const { timed } = require("../src/lib/telemetry");
|
|
171
|
+
await timed(name, () => load()(argv.slice(1)));
|
|
172
|
+
|
|
173
|
+
// One ask, in one place, after the command succeeded. Throwing above skips
|
|
174
|
+
// it — a star request stapled to a stack trace is the wrong read of the
|
|
175
|
+
// room. star.js decides whether this particular moment earned one.
|
|
176
|
+
require("../src/lib/star").maybeAsk(name);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
main().catch((err) => {
|
|
180
|
+
console.error("");
|
|
181
|
+
console.error(pc.red("Error: ") + err.message);
|
|
182
|
+
if (process.env.AGENTMD_DEBUG) console.error(pc.dim(err.stack));
|
|
183
|
+
else console.error(pc.dim(" Re-run with AGENTMD_DEBUG=1 for a stack trace."));
|
|
184
|
+
console.error("");
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "activate-agentmd",
|
|
3
|
+
"version": "2.4.0",
|
|
4
|
+
"description": "The package manager for AI coding-agent engineering standards. Install, link and update rules across Claude Code, Cursor, Codex, Gemini and Copilot.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"agent",
|
|
8
|
+
"agents-md",
|
|
9
|
+
"claude",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"cursor",
|
|
12
|
+
"cursor-rules",
|
|
13
|
+
"codex",
|
|
14
|
+
"copilot",
|
|
15
|
+
"gemini",
|
|
16
|
+
"llm",
|
|
17
|
+
"prompts",
|
|
18
|
+
"presets",
|
|
19
|
+
"engineering-standards",
|
|
20
|
+
"ai-coding"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://github.com/Aaditya1273/Agent.md#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/Aaditya1273/Agent.md/issues"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/Aaditya1273/Agent.md.git"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"bin": {
|
|
32
|
+
"agentmd": "bin/agentmd.js"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin/",
|
|
36
|
+
"src/",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"test": "node --test test/*.test.js",
|
|
42
|
+
"smoke": "node bin/agentmd.js --help >/dev/null && node bin/agentmd.js validate",
|
|
43
|
+
"start": "node bin/agentmd.js",
|
|
44
|
+
"prepack": "node scripts/check-publish-safety.js",
|
|
45
|
+
"safety": "node scripts/check-publish-safety.js",
|
|
46
|
+
"test:pro": "node scripts/test-pro.js"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.0.0"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@anthropic-ai/sdk": "^0.125.0",
|
|
53
|
+
"picocolors": "1.1.1"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agents.js
|
|
3
|
+
*
|
|
4
|
+
* Handles: agentmd agents
|
|
5
|
+
*
|
|
6
|
+
* Lists every agent config convention `agentmd link` can write, and whether
|
|
7
|
+
* this project already has it. Deliberately short: it lists what is
|
|
8
|
+
* implemented, not what is planned.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const pc = require("picocolors");
|
|
16
|
+
const { TARGETS } = require("../lib/agents");
|
|
17
|
+
const { cmd } = require("../lib/invocation");
|
|
18
|
+
|
|
19
|
+
/** How the user invoked us — "npx activate-agentmd" or "agentmd". */
|
|
20
|
+
const A = cmd();
|
|
21
|
+
|
|
22
|
+
function agents() {
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
|
|
25
|
+
console.log(pc.bold("\nAgent config targets\n"));
|
|
26
|
+
console.log(pc.dim(" Packages are plain markdown, so any agent can read them. These are the"));
|
|
27
|
+
console.log(pc.dim(` config conventions '${A} link' writes automatically.\n`));
|
|
28
|
+
|
|
29
|
+
const width = Math.max(...Object.keys(TARGETS).map((k) => k.length));
|
|
30
|
+
for (const [name, target] of Object.entries(TARGETS)) {
|
|
31
|
+
const present = fs.existsSync(path.join(cwd, target.file));
|
|
32
|
+
const mark = present ? pc.green("✓") : pc.dim("·");
|
|
33
|
+
console.log(
|
|
34
|
+
` ${mark} ${pc.cyan(name.padEnd(width))} ${pc.white(target.file.padEnd(34))} ${pc.dim(target.label)}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log("");
|
|
39
|
+
console.log(pc.dim(" ✓ = already present in this project"));
|
|
40
|
+
console.log(pc.dim(` Write them: ${A} link --agent=claude,cursor`));
|
|
41
|
+
console.log(pc.dim(` Write all: ${A} link --agent=all\n`));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { agents };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* analytics.js
|
|
3
|
+
*
|
|
4
|
+
* Handles: agentmd analytics [--since 30d] [--json] (Pro)
|
|
5
|
+
*
|
|
6
|
+
* Reads the local ledger `review` writes and answers the question that decides
|
|
7
|
+
* whether any of this was worth it: which standards actually caught something,
|
|
8
|
+
* and which have never fired.
|
|
9
|
+
*
|
|
10
|
+
* The second half matters more than the first. A rule nobody ever violates is
|
|
11
|
+
* costing tokens on every prompt and buying nothing, and this is the only
|
|
12
|
+
* place that says so with a number.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
"use strict";
|
|
16
|
+
|
|
17
|
+
const pc = require("picocolors");
|
|
18
|
+
const { summarise, read } = require("../lib/analytics");
|
|
19
|
+
const { readManifest } = require("../lib/manifest");
|
|
20
|
+
const { cmd } = require("../lib/invocation");
|
|
21
|
+
|
|
22
|
+
const A = cmd();
|
|
23
|
+
|
|
24
|
+
/** `30d`, `12w`, `6m`, or `all`. */
|
|
25
|
+
function parseSince(value) {
|
|
26
|
+
if (!value || value === "all") return null;
|
|
27
|
+
const m = /^(\d+)\s*([dwm])$/i.exec(value.trim());
|
|
28
|
+
if (!m) return undefined; // signals "unparseable" so the caller can complain
|
|
29
|
+
const days = Number(m[1]) * ({ d: 1, w: 7, m: 30 })[m[2].toLowerCase()];
|
|
30
|
+
return new Date(Date.now() - days * 86400000);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const flagValue = (args, name) => {
|
|
34
|
+
const inline = (args || []).find((a) => a.startsWith(`--${name}=`));
|
|
35
|
+
if (inline) return inline.slice(name.length + 3);
|
|
36
|
+
const i = (args || []).indexOf(`--${name}`);
|
|
37
|
+
return i !== -1 && args[i + 1] && !args[i + 1].startsWith("--") ? args[i + 1] : null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** A fixed-width bar for a value against the largest in its column. */
|
|
41
|
+
const bar = (n, max, width = 18) =>
|
|
42
|
+
pc.dim("▁".repeat(0)) + "█".repeat(Math.max(1, Math.round((n / Math.max(1, max)) * width)));
|
|
43
|
+
|
|
44
|
+
function analytics(args) {
|
|
45
|
+
args = args || [];
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
const sinceRaw = flagValue(args, "since") || "all";
|
|
48
|
+
const since = parseSince(sinceRaw);
|
|
49
|
+
|
|
50
|
+
if (since === undefined) {
|
|
51
|
+
console.error(pc.red(`\n Could not read --since "${sinceRaw}". Use 30d, 12w, 6m or all.\n`));
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const ledger = read(cwd);
|
|
57
|
+
if (ledger.runs.length === 0) {
|
|
58
|
+
console.log(`\n ${pc.dim("No review history in this project yet.")}`);
|
|
59
|
+
console.log(pc.dim(` Run '${A} review --fast' — it records every run, free and offline.\n`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const s = summarise(cwd, since);
|
|
64
|
+
|
|
65
|
+
if (args.includes("--json")) {
|
|
66
|
+
console.log(JSON.stringify(s, null, 2));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const window = since ? `since ${since.toISOString().slice(0, 10)}` : "all time";
|
|
71
|
+
console.log(pc.bold(`\nWhat your standards caught ${pc.dim("— " + window)}\n`));
|
|
72
|
+
|
|
73
|
+
if (s.runs === 0) {
|
|
74
|
+
console.log(pc.dim(` No reviews in that window. ${ledger.runs.length} run(s) recorded overall.\n`));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log(` ${pc.white(String(s.total).padStart(5))} ${pc.dim("findings across")} ${pc.white(String(s.runs))} ${pc.dim("review" + (s.runs === 1 ? "" : "s"))}`);
|
|
79
|
+
console.log(` ${pc.dim(" " + s.patterns + " caught offline by pattern rules — no model, no tokens")}`);
|
|
80
|
+
console.log("");
|
|
81
|
+
console.log(
|
|
82
|
+
` ${pc.red(String(s.bySeverity.high || 0) + " high")} ` +
|
|
83
|
+
`${pc.yellow(String(s.bySeverity.medium || 0) + " medium")} ` +
|
|
84
|
+
`${pc.dim(String(s.bySeverity.low || 0) + " low")}`
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (s.byStandard.length) {
|
|
88
|
+
const max = s.byStandard[0][1];
|
|
89
|
+
console.log(pc.bold("\n Per standard\n"));
|
|
90
|
+
for (const [standard, n] of s.byStandard.slice(0, 12)) {
|
|
91
|
+
console.log(` ${pc.white(String(n).padStart(4))} ${pc.dim(bar(n, max))} ${standard}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (s.byRule.length > 1) {
|
|
96
|
+
console.log(pc.bold("\n Most-broken rules\n"));
|
|
97
|
+
for (const [rule, n] of s.byRule.slice(0, 5)) {
|
|
98
|
+
console.log(` ${pc.white(String(n).padStart(4))} ${rule}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The expensive half: what you are paying context for and getting nothing from.
|
|
103
|
+
try {
|
|
104
|
+
const installed = readManifest(cwd).presets.map((p) => `${p.category}/${p.preset}`);
|
|
105
|
+
const idle = installed.filter((id) => !s.idle.seen.has(id));
|
|
106
|
+
if (idle.length) {
|
|
107
|
+
console.log(pc.bold(`\n Never fired ${pc.dim(`(${idle.length} of ${installed.length} installed)`)}\n`));
|
|
108
|
+
for (const id of idle.slice(0, 10)) console.log(` ${pc.dim("·")} ${pc.dim(id)}`);
|
|
109
|
+
if (idle.length > 10) console.log(pc.dim(` … and ${idle.length - 10} more`));
|
|
110
|
+
console.log(pc.dim(`\n These cost context on every prompt. Drop one with '${A} remove <id>' if it is not`));
|
|
111
|
+
console.log(pc.dim(" guarding something you simply have not broken yet."));
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
// no manifest here — the per-standard table above still stands on its own
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (s.byWeek.length > 1) {
|
|
118
|
+
const max = Math.max(...s.byWeek.map(([, n]) => n));
|
|
119
|
+
console.log(pc.bold("\n By week\n"));
|
|
120
|
+
for (const [week, n] of s.byWeek.slice(-8)) {
|
|
121
|
+
console.log(` ${pc.dim(week)} ${pc.white(String(n).padStart(4))} ${pc.dim(bar(n, max, 24))}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(pc.dim(`\n Local only — this never leaves your machine. ${A} analytics --json for the raw numbers.\n`));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { analytics, parseSince };
|