@mhmdreza-rafiei/agentry 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 comprehensive-skills contributors
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,95 @@
1
+ # agentry
2
+
3
+ A raw installer for AI/agent tooling. `agentry` fetches **agents, skills, rules, and scripts** from any Git repo (or local path) and installs the ones you pick into your project — or globally — under `.cursor/`. The CLI ships no assets of its own; you point it at a source.
4
+
5
+ ## Install / run
6
+
7
+ ```bash
8
+ # Run without installing (recommended)
9
+ npx agentry@latest --help
10
+
11
+ # Or install the CLI globally
12
+ npm install -g agentry@latest
13
+
14
+ # Update / uninstall the CLI
15
+ agentry update
16
+ agentry --uninstall
17
+ ```
18
+
19
+ > Publishing to npm is pending. Until then, run from a clone: `node bin/agentry.js …` or `npx . …`.
20
+
21
+ ## Usage
22
+
23
+ ```
24
+ agentry add <type> <source> [category/name | category] [options]
25
+ agentry add profile <name> [source] [options]
26
+ agentry remove <type> [category/name | category] [options]
27
+ agentry list <source> [type]
28
+ agentry update
29
+ ```
30
+
31
+ `<source>` is a GitHub shorthand `author/repo`, a full git URL, or a local path.
32
+
33
+ ```bash
34
+ agentry add skills Prat011/awesome-llm-skills # list all skills and pick
35
+ agentry add skills Prat011/awesome-llm-skills video-downloader # one skill (no category)
36
+ agentry add skills Prat011/awesome-llm-skills document-skills/docx # one skill (category/name)
37
+ agentry add agents author/repo frontend # a whole category
38
+ agentry add profile frontend author/repo # apply profile/frontend.json
39
+ agentry remove skills document-skills/docx # remove an installed asset
40
+ agentry list Prat011/awesome-llm-skills
41
+ ```
42
+
43
+ Flags: `-g, --global` (into `~/.cursor`), `--project` (current folder, default), `--dir <path>`, `-y, --yes` (no prompts; install everything matched), `-v, --version`, `-h, --help`, `--uninstall`.
44
+
45
+ When neither `--global` nor `--project` is given, an interactive terminal asks where to install; non‑interactive runs (agents/CI) default to the current project.
46
+
47
+ ## How sources are discovered
48
+
49
+ For each type, `agentry` finds assets in two ways and merges them:
50
+
51
+ 1. Under an explicit folder — `<type>/<name>/` or `<type>/<category>/<name>/`.
52
+ 2. At the repo root by marker file (like `npx skills add`) — a folder with `SKILL.md` is a skill, `AGENT.md` an agent, `RULE.md` a rule. A root folder whose children carry markers becomes a category (e.g. `document-skills/docx`).
53
+
54
+ Selectors after the source: `category/name` (one asset), `name` (one asset when uncategorized), `category` (whole category), or omit to list-and-pick.
55
+
56
+ Assets install to `<root>/.cursor/<type>/[category/]<name>/` and are tracked in `.cursor/agentry.lock.json` for idempotent installs and clean removal. `remove` works purely on installed files, so it needs no source.
57
+
58
+ ## Profiles
59
+
60
+ A profile is a plain JSON file at `profile/<name>.json` in your project. It lists selectors per type; `agentry add profile <name> <source>` fetches the source once and installs everything the profile lists.
61
+
62
+ ```json
63
+ {
64
+ "repo": "author/repo",
65
+ "agents": ["frontend"],
66
+ "skills": ["prompt/enhance-prompt"],
67
+ "rules": ["workflow/ask-dont-guess"]
68
+ }
69
+ ```
70
+
71
+ `repo` is optional — the command-line `<source>` wins when given. See [`profile/frontend.json`](profile/frontend.json) and [`profile/backend.json`](profile/backend.json).
72
+
73
+ ## Publish to npm (maintainers)
74
+
75
+ ```bash
76
+ # ensure package.json "name" is available (or scope it: @you/agentry)
77
+ npm login
78
+ npm publish --access public
79
+ # verify
80
+ npx agentry@latest --version
81
+ ```
82
+
83
+ ## Development / test
84
+
85
+ ```bash
86
+ npm test # node --test (discovery, source resolution, install/remove, profiles)
87
+ node bin/agentry.js … # run the CLI locally
88
+ node bin/agentry.js list ./examples # try it against the bundled sample source
89
+ ```
90
+
91
+ `examples/` is a sample source repo (skills/agents/rules/scripts) used by the demo — not part of the CLI. `src/` holds the logic: `registry` (discovery), `source` (fetch), `commands` (install/remove), `profile` (config).
92
+
93
+ ## License
94
+
95
+ MIT
package/bin/agentry.js ADDED
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const readline = require('node:readline');
5
+ const { execSync } = require('node:child_process');
6
+ const reg = require('../src/registry');
7
+ const src = require('../src/source');
8
+ const prof = require('../src/profile');
9
+ const cmd = require('../src/commands');
10
+ const pkg = require('../package.json');
11
+
12
+ function parseArgs(argv) {
13
+ const flags = {};
14
+ const positional = [];
15
+ for (let i = 0; i < argv.length; i++) {
16
+ const a = argv[i];
17
+ if (a === '-g' || a === '--global') flags.global = true;
18
+ else if (a === '--project') flags.project = true;
19
+ else if (a === '-y' || a === '--yes') flags.yes = true;
20
+ else if (a === '--dir') flags.dir = argv[++i];
21
+ else if (a === '-v' || a === '--version') flags.version = true;
22
+ else if (a === '-h' || a === '--help') flags.help = true;
23
+ else if (a === '--uninstall') flags.uninstall = true;
24
+ else positional.push(a);
25
+ }
26
+ return { flags, positional };
27
+ }
28
+
29
+ const HELP = `agentry — a raw installer for agents, skills, rules, and scripts from any repo.
30
+
31
+ Usage:
32
+ agentry add <type> <source> [category/name | category] [options]
33
+ agentry add profile <name> [source] [options]
34
+ agentry remove <type> [category/name | category] [options]
35
+ agentry list <source> [type]
36
+ agentry update
37
+
38
+ Sources:
39
+ author/repo GitHub shorthand e.g. Prat011/awesome-llm-skills
40
+ https://…/repo.git full git URL
41
+ ./path a local directory
42
+
43
+ Selectors (after the source):
44
+ category/name one asset e.g. video/downloader
45
+ name one asset (when the repo has no categories)
46
+ category a whole category
47
+ (omitted) list assets and pick interactively
48
+
49
+ Examples:
50
+ agentry add skills Prat011/awesome-llm-skills # pick from all skills
51
+ agentry add skills Prat011/awesome-llm-skills video/downloader # one skill (categorized)
52
+ agentry add skills Prat011/awesome-llm-skills video-downloader # one skill (no category)
53
+ agentry add agents author/repo frontend # a whole category
54
+ agentry add profile frontend author/repo # apply profile/frontend.json
55
+ agentry remove skills video/downloader # remove installed asset
56
+ agentry list author/repo
57
+
58
+ Options:
59
+ -g, --global Install into ~/.cursor (all projects)
60
+ --project Install into the current folder (default)
61
+ --dir <p> Install into a specific folder
62
+ -y, --yes Don't prompt; install everything matched
63
+ -v, --version Print version
64
+ -h, --help Show this help
65
+ --uninstall Uninstall the agentry CLI`;
66
+
67
+ function ask(question) {
68
+ return new Promise((resolve) => {
69
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
70
+ rl.question(question, (answer) => {
71
+ rl.close();
72
+ resolve(answer.trim());
73
+ });
74
+ });
75
+ }
76
+
77
+ function interactive(flags) {
78
+ return process.stdin.isTTY && !flags.yes;
79
+ }
80
+
81
+ async function resolveTarget(flags) {
82
+ if (flags.global) return { global: true };
83
+ if (flags.project || flags.dir) return { global: false, dir: flags.dir };
84
+ if (interactive(flags)) {
85
+ const ans = await ask('Install location — [P]roject (this folder) or [g]lobal (home)? ');
86
+ if (/^g/i.test(ans)) return { global: true };
87
+ }
88
+ return { global: false };
89
+ }
90
+
91
+ async function pick(assets) {
92
+ console.log('Available:');
93
+ assets.forEach((a, i) => {
94
+ const desc = (a.description || '').replace(/\s+/g, ' ').slice(0, 60);
95
+ console.log(` [${i + 1}] ${a.type}/${a.id}${desc ? ' — ' + desc : ''}`);
96
+ });
97
+ const ans = await ask('Install which? (numbers comma-separated, or "all"): ');
98
+ if (!ans || /^all$/i.test(ans)) return assets;
99
+ const idx = ans
100
+ .split(/[, ]+/)
101
+ .map((s) => parseInt(s, 10) - 1)
102
+ .filter((n) => n >= 0 && n < assets.length);
103
+ return idx.map((i) => assets[i]);
104
+ }
105
+
106
+ function locationLabel(opts) {
107
+ return opts.global ? 'globally (~/.cursor)' : `project (${cmd.targetRoot(opts)})`;
108
+ }
109
+
110
+ function dedupe(assets) {
111
+ const seen = new Set();
112
+ return assets.filter((a) => {
113
+ const key = `${a.type}/${a.id}`;
114
+ if (seen.has(key)) return false;
115
+ seen.add(key);
116
+ return true;
117
+ });
118
+ }
119
+
120
+ function installAll(assets, opts) {
121
+ if (!assets.length) {
122
+ console.log('Nothing matched — nothing installed.');
123
+ return;
124
+ }
125
+ console.log(`Installing ${assets.length} item(s) ${locationLabel(opts)}:`);
126
+ for (const a of assets) {
127
+ const dest = cmd.installOne(a, opts);
128
+ console.log(` + ${a.type}/${a.id} -> ${dest}`);
129
+ }
130
+ console.log('\nDone. Review assets before use; they run with full agent permissions.');
131
+ }
132
+
133
+ async function addType(type, source, selector, flags) {
134
+ if (!source) throw new Error(`Missing source. e.g. agentry add ${type} author/repo`);
135
+ const opts = await resolveTarget(flags);
136
+ const { root, cleanup } = src.resolveSource(source);
137
+ try {
138
+ let assets = type === 'all' ? reg.allAssets(root) : reg.select(root, type, selector);
139
+ if (!assets.length) throw new Error(`Nothing found for "${type}${selector ? ' ' + selector : ''}" in ${source}.`);
140
+ if (!selector && type !== 'all' && assets.length > 1 && interactive(flags)) assets = await pick(assets);
141
+ installAll(assets, opts);
142
+ } finally {
143
+ cleanup();
144
+ }
145
+ }
146
+
147
+ async function addProfile(name, source, flags) {
148
+ const { file, cfg } = prof.loadProfile(name);
149
+ const repo = source || cfg.repo;
150
+ if (!repo) throw new Error(`No source given and ${file} has no "repo". Try: agentry add profile ${name} author/repo`);
151
+ const opts = await resolveTarget(flags);
152
+ const { root, cleanup } = src.resolveSource(repo);
153
+ try {
154
+ const assets = [];
155
+ for (const type of reg.TYPES) {
156
+ for (const selector of cfg[type] || []) assets.push(...reg.select(root, type, selector));
157
+ }
158
+ installAll(dedupe(assets), opts);
159
+ } finally {
160
+ cleanup();
161
+ }
162
+ }
163
+
164
+ function listSource(source, type) {
165
+ if (!source) throw new Error('Missing source. e.g. agentry list author/repo');
166
+ const { root, cleanup } = src.resolveSource(source);
167
+ try {
168
+ for (const t of type ? [type] : reg.TYPES) {
169
+ console.log(`\n${t}:`);
170
+ const items = reg.listType(root, t);
171
+ if (!items.length) {
172
+ console.log(' (none)');
173
+ continue;
174
+ }
175
+ for (const it of items) {
176
+ const desc = (it.description || '').replace(/\s+/g, ' ').slice(0, 80);
177
+ console.log(` ${it.id}${desc ? ' — ' + desc : ''}`);
178
+ }
179
+ }
180
+ } finally {
181
+ cleanup();
182
+ }
183
+ }
184
+
185
+ async function remove(type, selector, flags) {
186
+ const opts = await resolveTarget(flags);
187
+ const scope = [type, selector].filter(Boolean).join('/');
188
+ if (type === 'all') {
189
+ let count = 0;
190
+ for (const t of reg.TYPES) count += cmd.removeSelection(t, undefined, opts).removedKeys.length;
191
+ console.log(`Removed ${count} item(s) from ${locationLabel(opts)}.`);
192
+ return;
193
+ }
194
+ const r = cmd.removeSelection(type, selector, opts);
195
+ if (!r.existed && r.removedKeys.length === 0) {
196
+ console.log(`Nothing installed at "${scope}" in ${locationLabel(opts)}.`);
197
+ return;
198
+ }
199
+ for (const k of r.removedKeys) console.log(`removed ${k}`);
200
+ console.log(`\nRemoved ${r.removedKeys.length || 1} item(s) from ${locationLabel(opts)}.`);
201
+ }
202
+
203
+ function runUpdate() {
204
+ console.log('Updating agentry CLI...');
205
+ try {
206
+ execSync('npm install -g agentry@latest', { stdio: 'inherit' });
207
+ } catch {
208
+ console.error('Update failed. Once published, run: npm install -g agentry@latest');
209
+ process.exit(1);
210
+ }
211
+ }
212
+
213
+ function runUninstall() {
214
+ console.log('Uninstalling agentry CLI...');
215
+ try {
216
+ execSync('npm uninstall -g agentry', { stdio: 'inherit' });
217
+ } catch {
218
+ console.error('Uninstall failed. Try: npm uninstall -g agentry');
219
+ process.exit(1);
220
+ }
221
+ }
222
+
223
+ async function main() {
224
+ const { flags, positional } = parseArgs(process.argv.slice(2));
225
+
226
+ if (flags.version) return console.log(pkg.version);
227
+ if (flags.uninstall) return runUninstall();
228
+ if (flags.help || positional.length === 0) return console.log(HELP);
229
+
230
+ const [action] = positional;
231
+
232
+ if (action === 'update') return runUpdate();
233
+ if (action === 'list') return listSource(positional[1], positional[2]);
234
+
235
+ if (action === 'add') {
236
+ const type = positional[1];
237
+ if (!type) throw new Error('Missing type. e.g. agentry add skills author/repo');
238
+ if (type === 'profile') return addProfile(positional[2], positional[3], flags);
239
+ if (type !== 'all' && !reg.TYPES.includes(type)) throw new Error(`Unknown type: ${type}`);
240
+ return addType(type, positional[2], positional[3], flags);
241
+ }
242
+
243
+ if (action === 'remove') {
244
+ const type = positional[1];
245
+ if (!type) throw new Error('Missing type. e.g. agentry remove skills video/downloader');
246
+ if (type !== 'all' && !reg.TYPES.includes(type)) throw new Error(`Unknown type: ${type}`);
247
+ return remove(type, positional[2], flags);
248
+ }
249
+
250
+ console.error(`Unknown action: ${action}\n\n${HELP}`);
251
+ process.exit(1);
252
+ }
253
+
254
+ main().catch((err) => {
255
+ console.error(err.message || err);
256
+ process.exit(1);
257
+ });
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: frontend-developer
3
+ description: >
4
+ Starter frontend developer agent — UI implementation, component structure,
5
+ accessibility, responsive layout, and visual polish. Edit for your project.
6
+ ---
7
+
8
+ # frontend-developer
9
+
10
+ Example agent shipped with `agentry`. Use it as a starting point and adapt it to your stack.
11
+
12
+ ## Focus
13
+
14
+ - Implement and refine UI components and layouts.
15
+ - Prioritize accessibility, responsive design, and visual polish.
16
+ - Reuse existing design patterns and tokens before introducing new ones.
17
+ - Keep changes small and verifiable; prefer the simplest approach that works.
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "frontend-developer",
3
+ "type": "agents",
4
+ "description": "Starter frontend developer agent persona."
5
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: ask-dont-guess
3
+ description: >
4
+ Ask before guessing requirements or product truth. Act without asking only
5
+ when verified context already answers it. Prefer short, concrete questions.
6
+ ---
7
+
8
+ # ask-dont-guess
9
+
10
+ Before writing code, changing architecture, or stating product facts, ask when anything is unclear, ambiguous, missing, or a "probably fine" default that would lock the project in.
11
+
12
+ - Use short, concrete questions; multiple-choice is fine.
13
+ - Do not invent missing specs.
14
+ - When verified project context already answers it, proceed — do not re-ask settled facts.
15
+ - When docs and code conflict, trust verified code and runtime evidence.
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "ask-dont-guess",
3
+ "type": "rules",
4
+ "description": "Ask before guessing requirements or product truth."
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "doctor",
3
+ "type": "scripts",
4
+ "description": "Print a simple environment readiness check."
5
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ # Starter script shipped with agentry. Prints a simple readiness check.
3
+ set -euo pipefail
4
+ echo "agentry: environment OK"
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: enhance-prompt
3
+ description: >
4
+ Use when the user asks to enhance, improve, rewrite, optimize, or clarify a prompt —
5
+ or when always-on prompt enhancement is enabled for the session.
6
+ Rewrites casual or vague chat into precise, portable agent instructions
7
+ without injecting workspace paths, stack, or session-local context.
8
+ Preserves user-stated contracts; ranks related skills; asks clarifying /
9
+ mode / better-decision questions when needed; ends with an after-finish summary instruction.
10
+ ---
11
+
12
+ # enhance-prompt
13
+
14
+ Rewrite the user’s message into a precise, portable agent prompt. **Enhance = clarify and structure — never strip user-stated details that affect accuracy.** Accuracy first; tokens second (same or better meaning, fewer tokens).
15
+
16
+ Do **not** invent requirements or paste workspace-local paths/stack. For shape and examples, open [references/TEMPLATE.md](references/TEMPLATE.md).
17
+
18
+ ## Procedure
19
+
20
+ Apply passes in order. Deliver one copy-ready enhanced prompt (or questions-first when blocked — see pass 0).
21
+
22
+ ### 0) Clarify, suggest, choose mode (ask before guessing)
23
+
24
+ Before (or instead of) emitting a full enhanced prompt, ask when it improves accuracy.
25
+
26
+ **Ask when:**
27
+
28
+ - Critical details are missing (fields, success criteria, scope, must-nots)
29
+ - A better / more optimal decision exists than what the user stated — propose it; don’t silently override
30
+ - Approach is ambiguous (how to do X vs Y given what they wrote)
31
+ - Depth / packing mode is unclear for a large task
32
+
33
+ **How to ask:**
34
+
35
+ - Prefer short multiple-choice or yes/no + one-line rationale
36
+ - Suggest **better options** (optimal defaults) labeled as suggestions — user can reject
37
+ - Ask **mode** when useful: `compact` | `standard` | `full` (see Depth dial)
38
+ - Ask approach forks only when they change the result (e.g. auth vs unauth OTP purpose)
39
+ - Cap: **MAX 3 questions** per turn (batch them). Don’t interrogate for niceties.
40
+ - If the user said “just enhance” / “don’t ask” / always-on with enough signal → enhance without questions
41
+ - Casual but clear enough → enhance; put residual ambiguity under **Open questions** for the implementer
42
+
43
+ **Question-first output** (when blocked on critical gaps):
44
+
45
+ 1. Brief “what I understood”
46
+ 2. Up to 3 questions (include mode / better-decision suggestions when relevant)
47
+ 3. Optional mini draft only if helpful — still no path/stack injection
48
+
49
+ After answers (or if not blocked), continue passes 1–8.
50
+
51
+ ### Depth dial
52
+
53
+ | Mode | When | Behavior |
54
+ |------|------|----------|
55
+ | `compact` | User asks, or rich contract already dense | Densest packing; full contracts kept; minimal Steps |
56
+ | `standard` | Default | Normal sections; Steps only if needed |
57
+ | `full` | Large / multi-system task, or user asks | More Steps + verify detail; still no path/stack dumps |
58
+
59
+ Same accuracy floor in all modes — only verbosity changes. If unset, use `standard` (or ask once when the task is large and mode would matter).
60
+
61
+ ### 1) Full-text enhance
62
+
63
+ - Fix grammar, wording, clarity; prefer precise verbs.
64
+ - Remove filler, noise symbols, decorative separators, emojis.
65
+ - Preserve user intent and constraints; never invent requirements, brand systems, stack, or scope the user did not ask for.
66
+ - **Do not rename** user field names (`phone` stays `phone`, not `phone-number`).
67
+
68
+ ### 2) Context policy (non-negotiable)
69
+
70
+ **Prompt context must be portable and session-safe.**
71
+
72
+ #### Always preserve (if present in the user input)
73
+
74
+ Contracts — compressing them away makes results worse:
75
+
76
+ - Endpoint lists, request/response bodies, field lists (e.g. `{ phone, password, code }`)
77
+ - Remove / keep / replace lists
78
+ - Security rules, do / don’t, acceptance criteria
79
+ - Explicit open questions or ambiguities the user left unresolved
80
+ - Any detail that changes implementation behavior
81
+
82
+ **Enhance** = reorganize and tighten. **Not** = delete or “summarize away” those facts.
83
+
84
+ **Rich input** → compress wording only; **copy contracts forward** (don’t rebuild from memory).
85
+ **Casual input** → add structure without inventing environment.
86
+
87
+ #### Never inject
88
+
89
+ - Absolute or relative project/workspace folder paths
90
+ - Repo roots, home paths, machine-specific paths
91
+ - Tech stack / framework / language lists the target workspace already provides via `AGENTS.md`, `context/`, rules, or open files
92
+ - Long dumps of docs already loadable in the *target* workspace
93
+ - Paths or facts from the *current* workspace when the prompt will be used elsewhere
94
+ - Invented field names, status codes, or API shapes the user did not state
95
+ - Silent “optimal” overrides the user didn’t accept (suggest via pass 0 instead)
96
+
97
+ Rules of thumb:
98
+
99
+ - Agent can read it from the target workspace → don’t paste paths/stack/docs.
100
+ - User stated a contract → keep it densely (tables / one-liners OK).
101
+ - Unsure if session-local → omit or `[use target workspace context]`.
102
+
103
+ ### 3) Structure / layout enhance
104
+
105
+ Use only sections with content; drop empty ones. One structure. **One home per fact** (Contract = shapes; Constraints = must-not; Steps = order only).
106
+
107
+ - **Title** — when helpful
108
+ - **Goal** — success looks like
109
+ - **Contract** (optional) — APIs / bodies / remove-keep; prefer compact packing
110
+ - **Constraints** — must / must-not
111
+ - **Steps** — only if order matters (omit in `compact` unless essential)
112
+ - **Related skills** — pass 5
113
+ - **Output** — deliverable format
114
+ - **Open questions** — unresolved; “resolve from workspace — don’t guess”
115
+ - **After you finish** — pass 6
116
+
117
+ After drafting: **Goal ↔ Contract ↔ Constraints ↔ Steps must agree**.
118
+
119
+ ### 4) Token optimization + dedup
120
+
121
+ - Same or better meaning, fewer tokens — **without dropping preserved contracts**.
122
+ - Strip banners, redundant headers, fluff verbs.
123
+ - Prefer short imperatives; pack field lists and endpoints as compact lines/tables.
124
+ - **Dedup:** remove the same constraint repeated across Goal / Constraints / Steps; delete Steps that only restate Contract.
125
+
126
+ ### 5) Related skills (precision)
127
+
128
+ Discover skills relevant to the prompt; attach **inside** the prompt.
129
+
130
+ 1. Search installed / project / agent skills (`npx skills list`, `AGENTS.md`, agent skills dirs). Prefer **target** environment.
131
+ 2. Rank by relevance to the goal; keep the **strongest** matches only (prefer ≤5).
132
+ 3. Each entry: name + one “use for …” tied to the goal. Optional: mark top 1–2 as “load first”.
133
+ 4. Skip weak / unrelated. Do not invent skill names.
134
+ 5. If none: omit section or `None found — proceed with workspace defaults.`
135
+
136
+ ### 6) After you finish (handoff summary instruction)
137
+
138
+ End with **After you finish**: tell the *implementing* agent, once work is done, write a result summary that:
139
+
140
+ - Anyone can understand (plain language)
141
+ - Still detailed — what changed, added/removed, how to verify, what’s still open
142
+ - Is **not** a prompt restatement or enhancer changelog
143
+
144
+ Always include unless the user asked to omit post-work summaries.
145
+
146
+ ### 7) Ambiguity policy
147
+
148
+ - Unclear but non-blocking → **Open questions** for the implementer; do not invent a default.
149
+ - Blocking → return to pass 0 (ask). Never silently pick auth rules, field names, or modes the user didn’t state or accept.
150
+
151
+ ### 8) Fidelity checklist + silent self-eval (before emit)
152
+
153
+ **Must pass before delivering the enhanced prompt:**
154
+
155
+ - [ ] Every user field / body / endpoint preserved?
156
+ - [ ] Every remove / keep preserved?
157
+ - [ ] Goal ↔ Contract ↔ Steps agree?
158
+ - [ ] No renamed fields; no invented stack / paths?
159
+ - [ ] No silent override of user intent (suggestions only via asks)?
160
+ - [ ] Deduped; depth mode respected?
161
+
162
+ If any fail → fix once, then emit.
163
+
164
+ Silently score 1–5: fidelity, portability, density. If fidelity &lt; 5 → revise once. Don’t print scores unless asked.
165
+
166
+ Optional chat-only enhancer changelog after the copy-ready block — omit if overloaded or user disabled it.
167
+
168
+ ## Always-on mode
169
+
170
+ If the user asks to enhance every chat / every prompt:
171
+
172
+ 1. Create or update `AGENTS.md` in the **target** project.
173
+ 2. Add a **Skills** section: when-to-use vs always-use.
174
+ 3. Register `enhance-prompt` as always-on, e.g.:
175
+
176
+ ```yaml
177
+ alwaysApply: true
178
+ ```
179
+
180
+ Always-on: rewrite the user message into a better agent prompt **before** acting — still obey context policy and fidelity rules. Prefer enhancing over asking unless blocked on critical gaps (then ≤3 tight questions).
181
+
182
+ ## Output
183
+
184
+ 1. Either questions-first (pass 0) **or** one copy-ready enhanced prompt (Related skills when found + **After you finish**).
185
+ 2. Optional brief chat-only enhancer changelog outside that block.
186
+ 3. Do not announce skill usage unless the user asks.
@@ -0,0 +1,182 @@
1
+ # enhance-prompt — output template
2
+
3
+ Enhance = clarify + structure. **Never strip user-stated contracts.** Never inject paths/stack. Ask (≤3) when blocked or when a better decision/mode would help — don’t silently override.
4
+
5
+ ## Correct output shape
6
+
7
+ ```markdown
8
+ # <short title>
9
+
10
+ ## Goal
11
+ <what success looks like>
12
+
13
+ ## Contract
14
+ <optional — compact endpoint/body/remove-keep lines the user stated>
15
+
16
+ ## Constraints
17
+ - <must / must-not — one home; don’t repeat Contract>
18
+
19
+ ## Steps
20
+ 1. <only if order matters>
21
+ 2. …
22
+
23
+ ## Related skills
24
+ - `/skill-name` *(load first)* — use for <why tied to goal>
25
+ - `/other` — use for <why>
26
+
27
+ ## Output
28
+ <deliverable format>
29
+
30
+ ## Open questions
31
+ - <resolve from workspace — don’t guess>
32
+
33
+ ## After you finish
34
+ When the work is done, write a summary anyone can understand, while remaining detailed:
35
+ - What changed (added/removed)
36
+ - How to verify
37
+ - Anything still open or risky
38
+ ```
39
+
40
+ Omit empty sections. Respect depth dial: `compact` | `standard` | `full`.
41
+
42
+ ---
43
+
44
+ ## Questions-first shape (pass 0)
45
+
46
+ When critical gaps or better decisions/modes matter:
47
+
48
+ ```markdown
49
+ ## Understood
50
+ <1–3 lines of intent>
51
+
52
+ ## Questions (max 3)
53
+ 1. **Mode:** run `compact` (densest), `standard` (default), or `full` (more steps/verify)? — suggestion: `standard` because <why>
54
+ 2. **Better decision:** you asked for X; Y is often safer/cleaner because <why>. Use **Y** or keep **X**?
55
+ 3. **Missing detail:** <field / success criteria / scope> — options A / B / C?
56
+
57
+ Reply with choices; then I’ll emit the full enhanced prompt.
58
+ ```
59
+
60
+ Do not invent stack/paths in questions. Don’t ask if the user said not to, or signal is already enough.
61
+
62
+ ---
63
+
64
+ ## Example A — casual UI (standard)
65
+
66
+ **In:** `Hey bro, the frontend look mid can you improve it?`
67
+
68
+ **Out (right):**
69
+
70
+ ```markdown
71
+ # Improve frontend UI quality
72
+
73
+ ## Goal
74
+ Raise frontend visual quality so the first viewport and overall UI feel polished and intentional.
75
+
76
+ ## Constraints
77
+ - Preserve existing behavior/content unless change is required for the visual fix
78
+ - Do not invent brand, stack, or design language
79
+ - Prefer target workspace design patterns when present
80
+ - First viewport: brand signal, one headline, short support, CTA group, one dominant visual
81
+
82
+ ## Related skills
83
+ - `/frontend-design` *(load first)* — layout and visual polish (if installed)
84
+
85
+ ## Output
86
+ UI improvements in the target workspace.
87
+
88
+ ## After you finish
89
+ Plain-language summary anyone can follow: what looked weak, what changed, how to check desktop and mobile.
90
+ ```
91
+
92
+ **Wrong:** invent React/Tailwind paths under `/Users/...`.
93
+
94
+ ---
95
+
96
+ ## Example B — ask mode + better decision (before enhance)
97
+
98
+ **In:** `refactor auth otp stuff, make it better`
99
+
100
+ **Questions-first (right):**
101
+
102
+ ```markdown
103
+ ## Understood
104
+ You want an auth/OTP refactor toward a cleaner flow, but the target shape isn’t specified.
105
+
106
+ ## Questions (max 3)
107
+ 1. **Mode:** `compact`, `standard`, or `full`? — suggestion: `full` (multi-endpoint refactor).
108
+ 2. **Better decision:** collapse to `otp/request` → action endpoints (login | register | password-change) with OTP verified **inside** actions (no public verify)? Or keep a public verify step?
109
+ 3. **Register body:** if OTP is inside register, should the body be `{ phone, password, code }` (recommended) or something else you already use?
110
+
111
+ Reply 1–3; then I’ll emit the enhanced prompt with contracts preserved.
112
+ ```
113
+
114
+ **Wrong:** silently invent full API + FastAPI stack + `G:\Projects\...` without asking.
115
+
116
+ ---
117
+
118
+ ## Example C — rich auth: preserve contracts (compact packing)
119
+
120
+ **In (excerpt):** OTP verify internal; `otp/request` → action; register `{ phone, password, code }`; password-change like login + `new_password`; remove public verify + change/reset.
121
+
122
+ **Wrong (diluted):**
123
+
124
+ ```markdown
125
+ ## Constraints
126
+ - Register: accept phone-number and password only
127
+ ```
128
+
129
+ **Right (`compact` / `standard`):**
130
+
131
+ ```markdown
132
+ # Auth: OTP internal + collapse password APIs
133
+
134
+ ## Goal
135
+ OTP verification is internal only. Public flow: otp/request → action (`login` | `register` | `password-change`). Remove public verify-OTP and separate change/reset password APIs.
136
+
137
+ ## Contract
138
+ - `POST /auth/otp/request` — `{ phone, purpose }`
139
+ - `POST /auth/login` — `{ phone, method, secret }`
140
+ - `POST /auth/register` — `{ phone, password, code }` ← OTP inside register
141
+ - `POST /auth/password-change` — `{ phone, method, secret, new_password }`
142
+ - Keep: refresh, logout
143
+ - Remove: public verify-OTP; password change; password reset; otp_proof cookie register flow if present
144
+
145
+ ## Constraints
146
+ - Verify-OTP stays internal — do not re-expose
147
+ - Register: no `method`, no `secret`
148
+ - `locale_pref`: prefer `localStorage`, else cookies
149
+ - No invented auth fields/status/error shapes; use target workspace
150
+ - Preserve existing security behavior unless user said otherwise
151
+ - No path/stack paste — load from target workspace
152
+
153
+ ## Related skills
154
+ - Rank ≤5 installed skills for auth / API / frontend / tests; mark top as load first
155
+
156
+ ## Output
157
+ Backend + frontend matching Contract.
158
+
159
+ ## Open questions
160
+ - Authenticated vs unauthenticated password-change: resolve OTP purpose / phone-vs-session from workspace — don’t guess
161
+
162
+ ## After you finish
163
+ Plain + detailed summary: before→after flow, endpoints removed vs added (with shapes), how to verify (tests + results), anything still open.
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Example D — path / stack leak anti-pattern
169
+
170
+ **Wrong:** `/home/ubuntu/...`, `/Users/dev/...`, “Stack: Next.js, Prisma…”, README dumps.
171
+
172
+ **Right:** portable Goal/Contract/Constraints; `[use target workspace context]` if a pointer is needed.
173
+
174
+ ---
175
+
176
+ ## Fidelity gate (before emit)
177
+
178
+ - Fields/endpoints/remove-keep all present?
179
+ - No renames (`phone` not `phone-number`)?
180
+ - Goal ↔ Contract ↔ Steps agree?
181
+ - No silent override (suggestions only via questions)?
182
+ - Deduped; mode respected?
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@mhmdreza-rafiei/agentry",
3
+ "version": "0.1.0",
4
+ "description": "One CLI to install agents, skills, rules, and scripts into any project or globally — portable AI/agent tooling.",
5
+ "bin": {
6
+ "agentry": "bin/agentry.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "src",
11
+ "profile",
12
+ "examples",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "agentry": "node bin/agentry.js",
18
+ "test": "node --test"
19
+ },
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "keywords": [
24
+ "ai",
25
+ "agents",
26
+ "skills",
27
+ "rules",
28
+ "cli",
29
+ "cursor",
30
+ "agentic",
31
+ "scaffold"
32
+ ],
33
+ "license": "MIT"
34
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "skills": ["prompt/enhance-prompt"],
3
+ "rules": ["workflow"],
4
+ "scripts": ["env/doctor"]
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "agents": ["frontend"],
3
+ "skills": ["prompt/enhance-prompt"],
4
+ "rules": ["workflow/ask-dont-guess"]
5
+ }
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ // Assets install under `<root>/.cursor/<type>/<category>/<name>`.
8
+ // ponytail: single target dir (.cursor) for now; per-agent targets (.claude, .codex)
9
+ // can be added later via a `target` field in each asset's agentry.json.
10
+ function targetRoot({ global: isGlobal, dir } = {}) {
11
+ const base = isGlobal ? os.homedir() : dir || process.cwd();
12
+ return path.join(base, '.cursor');
13
+ }
14
+
15
+ function lockPath(root) {
16
+ return path.join(root, 'agentry.lock.json');
17
+ }
18
+
19
+ function readLock(root) {
20
+ try {
21
+ return JSON.parse(fs.readFileSync(lockPath(root), 'utf8'));
22
+ } catch {
23
+ return { version: 1, items: {} };
24
+ }
25
+ }
26
+
27
+ function writeLock(root, lock) {
28
+ fs.mkdirSync(root, { recursive: true });
29
+ fs.writeFileSync(lockPath(root), JSON.stringify(lock, null, 2) + '\n');
30
+ }
31
+
32
+ function installOne(asset, opts) {
33
+ const root = targetRoot(opts);
34
+ const dest = path.join(root, asset.type, asset.id);
35
+ fs.rmSync(dest, { recursive: true, force: true }); // idempotent re-install
36
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
37
+ fs.cpSync(asset.dir, dest, { recursive: true });
38
+ const lock = readLock(root);
39
+ lock.items[`${asset.type}/${asset.id}`] = { installedAt: new Date().toISOString() };
40
+ writeLock(root, lock);
41
+ return dest;
42
+ }
43
+
44
+ // Removes a type, a category, or a single asset from the install target.
45
+ // selector: undefined -> whole type; "category" -> category; "category/name" -> one asset.
46
+ function removeSelection(type, selector, opts) {
47
+ const root = targetRoot(opts);
48
+ const rel = [type, selector].filter(Boolean).join('/');
49
+ const dest = path.join(root, rel);
50
+ const existed = fs.existsSync(dest);
51
+ fs.rmSync(dest, { recursive: true, force: true });
52
+ const lock = readLock(root);
53
+ const removedKeys = [];
54
+ for (const key of Object.keys(lock.items)) {
55
+ if (key === rel || key.startsWith(rel + '/')) {
56
+ delete lock.items[key];
57
+ removedKeys.push(key);
58
+ }
59
+ }
60
+ writeLock(root, lock);
61
+ return { existed, removedKeys };
62
+ }
63
+
64
+ module.exports = { targetRoot, lockPath, readLock, installOne, removeSelection };
package/src/profile.js ADDED
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ // Profiles are plain JSON under `<baseDir>/profile/<name>.json`, mapping each
7
+ // asset type to a list of selectors, e.g.:
8
+ // { "repo": "author/repo", "skills": ["prompt/enhance-prompt"], "rules": ["workflow"] }
9
+ // `repo` is optional; the command-line source (when given) takes precedence.
10
+ function loadProfile(name, baseDir) {
11
+ const file = path.join(baseDir || process.cwd(), 'profile', `${name}.json`);
12
+ let cfg;
13
+ try {
14
+ cfg = JSON.parse(fs.readFileSync(file, 'utf8'));
15
+ } catch {
16
+ throw new Error(`Profile not found or invalid JSON: ${file}`);
17
+ }
18
+ return { file, cfg };
19
+ }
20
+
21
+ module.exports = { loadProfile };
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ // Discovers assets inside a *source root* (a fetched repo or local path).
7
+ // Two discovery modes, unioned:
8
+ // 1) Under <type>/ : <type>/<name>/ or <type>/<category>/<name>/
9
+ // 2) At the repo root (like `npx skills add`), classified by a marker file:
10
+ // <name>/SKILL.md -> skills/<name>
11
+ // <group>/<name>/SKILL.md -> skills/<group>/<name>
12
+ // (AGENT.md -> agents, RULE.md -> rules). Scripts have no root marker.
13
+ const TYPES = ['agents', 'skills', 'rules', 'scripts'];
14
+ const MARKERS = { skills: 'SKILL.md', agents: 'AGENT.md', rules: 'RULE.md', scripts: null };
15
+
16
+ function readJson(file) {
17
+ try {
18
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ function entries(dir) {
25
+ try {
26
+ return fs.readdirSync(dir, { withFileTypes: true });
27
+ } catch {
28
+ return [];
29
+ }
30
+ }
31
+
32
+ function hasFile(dir) {
33
+ return entries(dir).some((e) => e.isFile());
34
+ }
35
+
36
+ function firstHeading(md) {
37
+ const m = md.match(/^#\s+(.+)$/m);
38
+ return m ? m[1].trim() : '';
39
+ }
40
+
41
+ function frontmatterDescription(md) {
42
+ const fm = md.match(/^---\r?\n([\s\S]*?)\r?\n---/);
43
+ if (!fm) return '';
44
+ const lines = fm[1].split(/\r?\n/);
45
+ for (let i = 0; i < lines.length; i++) {
46
+ const dm = lines[i].match(/^description:\s*(.*)$/);
47
+ if (!dm) continue;
48
+ let val = dm[1].trim();
49
+ if (val === '' || val === '>' || val === '|' || val === '>-' || val === '|-') {
50
+ const collected = [];
51
+ for (let j = i + 1; j < lines.length; j++) {
52
+ if (/^\s+\S/.test(lines[j])) collected.push(lines[j].trim());
53
+ else break;
54
+ }
55
+ val = collected.join(' ');
56
+ }
57
+ return val.trim();
58
+ }
59
+ return '';
60
+ }
61
+
62
+ function assetDescription(dir) {
63
+ const cfg = readJson(path.join(dir, 'agentry.json'));
64
+ if (cfg && cfg.description) return cfg.description;
65
+ for (const f of ['SKILL.md', 'AGENT.md', 'RULE.md', 'README.md']) {
66
+ const p = path.join(dir, f);
67
+ if (!fs.existsSync(p)) continue;
68
+ const md = fs.readFileSync(p, 'utf8');
69
+ const d = frontmatterDescription(md) || firstHeading(md);
70
+ if (d) return d;
71
+ }
72
+ return '';
73
+ }
74
+
75
+ function hasMarker(dir, marker) {
76
+ return !!marker && fs.existsSync(path.join(dir, marker));
77
+ }
78
+
79
+ function makeAsset(type, category, name, dir) {
80
+ const id = category ? `${category}/${name}` : name;
81
+ return { type, category, name, id, dir, description: assetDescription(dir) };
82
+ }
83
+
84
+ // Assets living under an explicit <type>/ folder (this repo's own convention).
85
+ function collectFromTypeDir(base, type, out) {
86
+ for (const child of entries(base)) {
87
+ if (!child.isDirectory()) continue;
88
+ const childDir = path.join(base, child.name);
89
+ if (hasFile(childDir)) {
90
+ out.push(makeAsset(type, null, child.name, childDir));
91
+ } else {
92
+ for (const item of entries(childDir)) {
93
+ if (!item.isDirectory()) continue;
94
+ out.push(makeAsset(type, child.name, item.name, path.join(childDir, item.name)));
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ // Assets at the repo root, identified by marker file (e.g. SKILL.md).
101
+ function collectFromRoot(root, type, marker, out) {
102
+ if (!marker) return;
103
+ for (const child of entries(root)) {
104
+ if (!child.isDirectory()) continue;
105
+ if (child.name.startsWith('.') || child.name === 'node_modules' || TYPES.includes(child.name)) continue;
106
+ const childDir = path.join(root, child.name);
107
+ if (hasMarker(childDir, marker)) {
108
+ out.push(makeAsset(type, null, child.name, childDir));
109
+ } else {
110
+ for (const item of entries(childDir)) {
111
+ if (item.isDirectory() && hasMarker(path.join(childDir, item.name), marker)) {
112
+ out.push(makeAsset(type, child.name, item.name, path.join(childDir, item.name)));
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ function listType(root, type) {
120
+ const out = [];
121
+ const base = path.join(root, type);
122
+ if (fs.existsSync(base)) collectFromTypeDir(base, type, out);
123
+ collectFromRoot(root, type, MARKERS[type], out);
124
+ const seen = new Set();
125
+ return out
126
+ .filter((a) => (seen.has(a.id) ? false : seen.add(a.id)))
127
+ .sort((a, b) => a.id.localeCompare(b.id));
128
+ }
129
+
130
+ function allAssets(root) {
131
+ return TYPES.flatMap((t) => listType(root, t));
132
+ }
133
+
134
+ // selector: undefined -> whole type; "category/name" -> one asset;
135
+ // "category" -> whole category; bare name -> fallback exact-name match.
136
+ function select(root, type, selector) {
137
+ const items = listType(root, type);
138
+ if (!selector) return items;
139
+ if (selector.includes('/')) return items.filter((a) => a.id === selector);
140
+ const byCategory = items.filter((a) => a.category === selector);
141
+ if (byCategory.length) return byCategory;
142
+ return items.filter((a) => a.name === selector);
143
+ }
144
+
145
+ module.exports = { TYPES, listType, allAssets, select };
package/src/source.js ADDED
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+ const { execFileSync } = require('node:child_process');
7
+
8
+ function isLocalPath(source) {
9
+ return (
10
+ source.startsWith('.') ||
11
+ source.startsWith('/') ||
12
+ source.startsWith('~') ||
13
+ fs.existsSync(source)
14
+ );
15
+ }
16
+
17
+ // Accepts a full git URL or the "author/repo" GitHub shorthand.
18
+ function toGitUrl(source) {
19
+ if (/^https?:\/\//.test(source) || source.startsWith('git@')) return source;
20
+ if (/^[\w.-]+\/[\w.-]+$/.test(source)) return `https://github.com/${source}.git`;
21
+ return null;
22
+ }
23
+
24
+ // Resolves a source (local path | author/repo | git URL) to a local root.
25
+ // Remote sources are shallow-cloned to a temp dir; call cleanup() when done.
26
+ function resolveSource(source) {
27
+ if (isLocalPath(source)) {
28
+ const root = path.resolve(source.replace(/^~/, os.homedir()));
29
+ if (!fs.existsSync(root)) throw new Error(`Path not found: ${source}`);
30
+ return { root, cleanup() {} };
31
+ }
32
+ const url = toGitUrl(source);
33
+ if (!url) throw new Error(`Cannot resolve source "${source}". Use a local path, author/repo, or git URL.`);
34
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'agentry-src-'));
35
+ try {
36
+ execFileSync('git', ['clone', '--depth', '1', '--quiet', url, tmp], {
37
+ stdio: ['ignore', 'ignore', 'pipe'],
38
+ });
39
+ } catch (e) {
40
+ fs.rmSync(tmp, { recursive: true, force: true });
41
+ const detail = e.stderr ? e.stderr.toString().trim() : e.message;
42
+ throw new Error(`git clone failed for ${url}\n${detail}`);
43
+ }
44
+ return {
45
+ root: tmp,
46
+ cleanup() {
47
+ fs.rmSync(tmp, { recursive: true, force: true });
48
+ },
49
+ };
50
+ }
51
+
52
+ module.exports = { isLocalPath, toGitUrl, resolveSource };