@mhmdreza-rafiei/agentry 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,182 +0,0 @@
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?
@@ -1,5 +0,0 @@
1
- {
2
- "skills": ["prompt/enhance-prompt"],
3
- "rules": ["workflow"],
4
- "scripts": ["env/doctor"]
5
- }
@@ -1,5 +0,0 @@
1
- {
2
- "agents": ["frontend"],
3
- "skills": ["prompt/enhance-prompt"],
4
- "rules": ["workflow/ask-dont-guess"]
5
- }
package/src/commands.js DELETED
@@ -1,64 +0,0 @@
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 DELETED
@@ -1,21 +0,0 @@
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 };
package/src/registry.js DELETED
@@ -1,145 +0,0 @@
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 DELETED
@@ -1,52 +0,0 @@
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 };