@michaeltroya/pouch 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 +21 -0
- package/README.md +237 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +284 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mt
|
|
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,237 @@
|
|
|
1
|
+
# Pouch
|
|
2
|
+
|
|
3
|
+
Create one agent skill and share it across Codex, Cursor, and Claude.
|
|
4
|
+
|
|
5
|
+
Pouch is a small CLI for keeping agent skills in one canonical place. Instead of copying the same `SKILL.md` into every agent's folder, Pouch creates the real skill once and links each agent to it.
|
|
6
|
+
|
|
7
|
+
```txt
|
|
8
|
+
.agents/skills/review-helper/SKILL.md
|
|
9
|
+
.codex/skills/review-helper -> ../../.agents/skills/review-helper
|
|
10
|
+
.cursor/skills/review-helper -> ../../.agents/skills/review-helper
|
|
11
|
+
.claude/skills/review-helper -> ../../.agents/skills/review-helper
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Why Pouch?
|
|
15
|
+
|
|
16
|
+
Agent skills are useful, but they tend to drift when each tool keeps its own copy.
|
|
17
|
+
|
|
18
|
+
Pouch gives you one source of truth:
|
|
19
|
+
|
|
20
|
+
- Create a skill once.
|
|
21
|
+
- Use it from multiple agents.
|
|
22
|
+
- Keep project-local skills inside the repo.
|
|
23
|
+
- Keep personal/global skills in your home directory.
|
|
24
|
+
- Avoid overwriting existing skills by accident.
|
|
25
|
+
|
|
26
|
+
Pouch is CLI-only. It does not expose a JavaScript API.
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
Requires Node.js 22.13 or newer.
|
|
31
|
+
|
|
32
|
+
Run without installing:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pnpm dlx @michaeltroya/pouch create
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
With npm:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npx @michaeltroya/pouch create
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
With Yarn:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
yarn dlx @michaeltroya/pouch create
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or install globally:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm install --global @michaeltroya/pouch
|
|
54
|
+
pouch create
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
For local development in this repo:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
pnpm install
|
|
61
|
+
pnpm build
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
During local development, run the CLI directly from TypeScript:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
pnpm dev -- create
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
After building, the package exposes a `pouch` binary:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
pouch create
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
Create a shared skill:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
pouch create
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Pouch will ask:
|
|
85
|
+
|
|
86
|
+
1. Skill name
|
|
87
|
+
2. Skill description
|
|
88
|
+
3. Where the canonical skill should live
|
|
89
|
+
4. Which agents should use it
|
|
90
|
+
|
|
91
|
+
You can choose a project-local skill:
|
|
92
|
+
|
|
93
|
+
```txt
|
|
94
|
+
./.agents/skills/<skill-name>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or a global skill:
|
|
98
|
+
|
|
99
|
+
```txt
|
|
100
|
+
~/.agents/skills/<skill-name>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Example output:
|
|
104
|
+
|
|
105
|
+
```txt
|
|
106
|
+
review-helper
|
|
107
|
+
Canonical: /path/to/project/.agents/skills/review-helper
|
|
108
|
+
Codex: /path/to/project/.codex/skills/review-helper (created)
|
|
109
|
+
Cursor: /path/to/project/.cursor/skills/review-helper (created)
|
|
110
|
+
Claude: /path/to/project/.claude/skills/review-helper (created)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## What Pouch Creates
|
|
114
|
+
|
|
115
|
+
Creating a project-local skill named `Review Helper` produces:
|
|
116
|
+
|
|
117
|
+
```txt
|
|
118
|
+
.agents/
|
|
119
|
+
skills/
|
|
120
|
+
review-helper/
|
|
121
|
+
SKILL.md
|
|
122
|
+
.codex/
|
|
123
|
+
skills/
|
|
124
|
+
review-helper -> ../../.agents/skills/review-helper
|
|
125
|
+
.cursor/
|
|
126
|
+
skills/
|
|
127
|
+
review-helper -> ../../.agents/skills/review-helper
|
|
128
|
+
.claude/
|
|
129
|
+
skills/
|
|
130
|
+
review-helper -> ../../.agents/skills/review-helper
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The generated `SKILL.md` starts like this:
|
|
134
|
+
|
|
135
|
+
```md
|
|
136
|
+
---
|
|
137
|
+
name: review-helper
|
|
138
|
+
description: 'Helps agents review code consistently.'
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
# Review Helper
|
|
142
|
+
|
|
143
|
+
Helps agents review code consistently.
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Supported Agents
|
|
147
|
+
|
|
148
|
+
| Agent | Project-local skill directory | Global skill directory |
|
|
149
|
+
| ------ | ----------------------------- | ---------------------- |
|
|
150
|
+
| Codex | `./.codex/skills` | `~/.codex/skills` |
|
|
151
|
+
| Cursor | `./.cursor/skills` | `~/.cursor/skills` |
|
|
152
|
+
| Claude | `./.claude/skills` | `~/.claude/skills` |
|
|
153
|
+
|
|
154
|
+
## Safety
|
|
155
|
+
|
|
156
|
+
Pouch is conservative with your files:
|
|
157
|
+
|
|
158
|
+
- It will not overwrite an existing canonical `SKILL.md`.
|
|
159
|
+
- It will not replace an existing non-symlink in an agent skill directory.
|
|
160
|
+
- It will not repoint an existing symlink that targets a different skill.
|
|
161
|
+
- If symlink creation fails partway through, it rolls back the skill and links it created during that run.
|
|
162
|
+
|
|
163
|
+
For project-local skills, Pouch creates relative symlinks so the links keep working if the project folder is moved.
|
|
164
|
+
|
|
165
|
+
On Windows, symlink creation may require Developer Mode or an elevated shell.
|
|
166
|
+
|
|
167
|
+
## Local Development
|
|
168
|
+
|
|
169
|
+
This repo uses pnpm.
|
|
170
|
+
|
|
171
|
+
Install dependencies:
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
pnpm install
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Run the CLI from TypeScript:
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
pnpm dev -- create
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Build the package:
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
pnpm build
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Run checks and formatting:
|
|
190
|
+
|
|
191
|
+
```sh
|
|
192
|
+
pnpm check
|
|
193
|
+
pnpm lint
|
|
194
|
+
pnpm format
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Tests are available through:
|
|
198
|
+
|
|
199
|
+
```sh
|
|
200
|
+
pnpm test
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Project Structure
|
|
204
|
+
|
|
205
|
+
```txt
|
|
206
|
+
src/
|
|
207
|
+
index.ts CLI entrypoint
|
|
208
|
+
commands/
|
|
209
|
+
create.ts Prompt flow for `pouch create`
|
|
210
|
+
lib/
|
|
211
|
+
agents.ts Supported agents and skill directory resolution
|
|
212
|
+
fs.ts Shared filesystem helpers
|
|
213
|
+
paths.ts Canonical skill paths and location schema
|
|
214
|
+
skill.ts Skill creation, symlink planning, and rollback
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Contributing
|
|
218
|
+
|
|
219
|
+
Issues and pull requests are welcome.
|
|
220
|
+
|
|
221
|
+
Before opening a PR, please run:
|
|
222
|
+
|
|
223
|
+
```sh
|
|
224
|
+
pnpm check
|
|
225
|
+
pnpm lint
|
|
226
|
+
pnpm format
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
If your change touches behavior, also run:
|
|
230
|
+
|
|
231
|
+
```sh
|
|
232
|
+
pnpm test
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { intro, isCancel, multiselect, note, outro, select, spinner, text } from "@clack/prompts";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import fs from "fs-extra";
|
|
8
|
+
//#region src/lib/paths.ts
|
|
9
|
+
const canonicalSkillLocationSchema = z.enum(["home", "project"]);
|
|
10
|
+
function getLocationRoot(location) {
|
|
11
|
+
if (location === "project") return process.cwd();
|
|
12
|
+
return os.homedir();
|
|
13
|
+
}
|
|
14
|
+
function getCanonicalSkillsDirectory(location) {
|
|
15
|
+
return path.join(getLocationRoot(location), ".agents", "skills");
|
|
16
|
+
}
|
|
17
|
+
function getCanonicalSkillPath(location, skillName) {
|
|
18
|
+
return path.join(getCanonicalSkillsDirectory(location), skillName);
|
|
19
|
+
}
|
|
20
|
+
function toComparablePath(value) {
|
|
21
|
+
const resolved = path.resolve(value);
|
|
22
|
+
if (resolved.startsWith("/private/var/")) return resolved.replace("/private/var/", "/var/");
|
|
23
|
+
return resolved;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/lib/agents.ts
|
|
27
|
+
const agentIdSchema = z.enum([
|
|
28
|
+
"codex",
|
|
29
|
+
"cursor",
|
|
30
|
+
"claude"
|
|
31
|
+
]);
|
|
32
|
+
const supportedAgents = [
|
|
33
|
+
{
|
|
34
|
+
id: "codex",
|
|
35
|
+
label: "Codex",
|
|
36
|
+
directoryName: ".codex"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "cursor",
|
|
40
|
+
label: "Cursor",
|
|
41
|
+
directoryName: ".cursor"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "claude",
|
|
45
|
+
label: "Claude",
|
|
46
|
+
directoryName: ".claude"
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
function getAgentsById(agentIds, location) {
|
|
50
|
+
const selected = new Set(agentIds);
|
|
51
|
+
return supportedAgents.filter((agent) => selected.has(agent.id)).map((agent) => ({
|
|
52
|
+
id: agent.id,
|
|
53
|
+
label: agent.label,
|
|
54
|
+
skillDirectory: getAgentSkillDirectory(agent.directoryName, location)
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
function getAgentSkillDirectory(directoryName, location) {
|
|
58
|
+
return path.join(getLocationRoot(location), directoryName, "skills");
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/lib/fs.ts
|
|
62
|
+
function isNodeError(error) {
|
|
63
|
+
return error instanceof Error && "code" in error;
|
|
64
|
+
}
|
|
65
|
+
async function lstatOrNull(filePath) {
|
|
66
|
+
return fs.lstat(filePath).catch((error) => {
|
|
67
|
+
if (isNodeError(error) && error.code === "ENOENT") return null;
|
|
68
|
+
throw error;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/lib/skill.ts
|
|
73
|
+
const skillInputSchema = z.object({
|
|
74
|
+
name: z.string().trim().min(1, "Skill name is required"),
|
|
75
|
+
description: z.string().trim().min(1, "Skill description is required")
|
|
76
|
+
});
|
|
77
|
+
function toSkillSlug(value) {
|
|
78
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
79
|
+
}
|
|
80
|
+
async function createSkill(input) {
|
|
81
|
+
const parsed = skillInputSchema.parse(input);
|
|
82
|
+
const skillName = toSkillSlug(parsed.name);
|
|
83
|
+
if (!skillName) throw new Error("Skill name must include at least one letter or number.");
|
|
84
|
+
const agents = dedupeAgents(input.agents);
|
|
85
|
+
const canonicalPath = getCanonicalSkillPath(input.location, skillName);
|
|
86
|
+
const skillFilePath = path.join(canonicalPath, "SKILL.md");
|
|
87
|
+
const canonicalExisted = await fs.pathExists(canonicalPath);
|
|
88
|
+
await ensureCanonicalSkillCanBeCreated(canonicalPath);
|
|
89
|
+
const linkPlans = await planSkillSymlinks(agents, canonicalPath, skillName);
|
|
90
|
+
await fs.ensureDir(canonicalPath);
|
|
91
|
+
await writeSkillFile(skillFilePath, parsed.name, parsed.description);
|
|
92
|
+
const links = [];
|
|
93
|
+
const createdLinkPaths = [];
|
|
94
|
+
try {
|
|
95
|
+
for (const linkPlan of linkPlans) {
|
|
96
|
+
const result = await applySkillSymlink(linkPlan, canonicalPath);
|
|
97
|
+
if (result.status === "created") createdLinkPaths.push(result.linkPath);
|
|
98
|
+
links.push(result);
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
await rollbackCreatedPaths({
|
|
102
|
+
canonicalExisted,
|
|
103
|
+
canonicalPath,
|
|
104
|
+
createdLinkPaths,
|
|
105
|
+
skillFilePath
|
|
106
|
+
});
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
skillName,
|
|
111
|
+
canonicalPath,
|
|
112
|
+
skillFilePath,
|
|
113
|
+
links
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function dedupeAgents(agents) {
|
|
117
|
+
const seen = /* @__PURE__ */ new Set();
|
|
118
|
+
return agents.filter((agent) => {
|
|
119
|
+
if (seen.has(agent.id)) return false;
|
|
120
|
+
seen.add(agent.id);
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async function writeSkillFile(skillFilePath, name, description) {
|
|
125
|
+
await fs.writeFile(skillFilePath, renderSkillMarkdown(name, description), { flag: "wx" }).catch((error) => {
|
|
126
|
+
if (isNodeError(error) && error.code === "EEXIST") throw new Error(`A canonical skill already exists at ${skillFilePath}`);
|
|
127
|
+
throw error;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async function ensureCanonicalSkillCanBeCreated(canonicalPath) {
|
|
131
|
+
await ensureDirectoryPathCanBeCreated(path.dirname(canonicalPath));
|
|
132
|
+
const existingCanonicalPath = await lstatOrNull(canonicalPath);
|
|
133
|
+
if (existingCanonicalPath && !existingCanonicalPath.isDirectory()) throw new Error(`Cannot create skill because ${canonicalPath} already exists.`);
|
|
134
|
+
if (existingCanonicalPath) {
|
|
135
|
+
const contents = await fs.readdir(canonicalPath);
|
|
136
|
+
if (contents.length > 0 && !contents.includes("SKILL.md")) throw new Error(`Cannot create skill because ${canonicalPath} already exists and is not empty.`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function renderSkillMarkdown(name, description) {
|
|
140
|
+
return `---
|
|
141
|
+
name: ${toSkillSlug(name)}
|
|
142
|
+
description: ${JSON.stringify(description.trim())}
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
# ${name.trim()}
|
|
146
|
+
|
|
147
|
+
${description.trim()}
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
async function planSkillSymlinks(agents, canonicalPath, skillName) {
|
|
151
|
+
return Promise.all(agents.map(async (agent) => {
|
|
152
|
+
const linkPath = path.join(agent.skillDirectory, skillName);
|
|
153
|
+
await ensureDirectoryPathCanBeCreated(path.dirname(linkPath));
|
|
154
|
+
const existing = await lstatOrNull(linkPath);
|
|
155
|
+
if (!existing) return {
|
|
156
|
+
agent,
|
|
157
|
+
linkPath,
|
|
158
|
+
action: "link"
|
|
159
|
+
};
|
|
160
|
+
if (!existing.isSymbolicLink()) throw new Error(`Cannot link skill because ${linkPath} already exists and is not a symlink.`);
|
|
161
|
+
const target = await fs.readlink(linkPath);
|
|
162
|
+
const resolvedTarget = path.resolve(path.dirname(linkPath), target);
|
|
163
|
+
if (toComparablePath(resolvedTarget) !== toComparablePath(canonicalPath)) throw new Error(`Cannot link skill because ${linkPath} points to ${resolvedTarget}.`);
|
|
164
|
+
return {
|
|
165
|
+
agent,
|
|
166
|
+
linkPath,
|
|
167
|
+
action: "skip"
|
|
168
|
+
};
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
async function ensureDirectoryPathCanBeCreated(directoryPath) {
|
|
172
|
+
const existing = await lstatOrNull(directoryPath);
|
|
173
|
+
if (existing) {
|
|
174
|
+
if (!existing.isDirectory()) throw new Error(`Cannot create directory because ${directoryPath} already exists and is not a directory.`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const parentPath = path.dirname(directoryPath);
|
|
178
|
+
if (parentPath === directoryPath) return;
|
|
179
|
+
await ensureDirectoryPathCanBeCreated(parentPath);
|
|
180
|
+
}
|
|
181
|
+
async function applySkillSymlink(plan, canonicalPath) {
|
|
182
|
+
if (plan.action === "skip") return {
|
|
183
|
+
agent: plan.agent,
|
|
184
|
+
linkPath: plan.linkPath,
|
|
185
|
+
status: "existing"
|
|
186
|
+
};
|
|
187
|
+
await fs.ensureDir(path.dirname(plan.linkPath));
|
|
188
|
+
const linkTarget = path.relative(path.dirname(plan.linkPath), canonicalPath);
|
|
189
|
+
await fs.symlink(linkTarget, plan.linkPath, "dir");
|
|
190
|
+
return {
|
|
191
|
+
agent: plan.agent,
|
|
192
|
+
linkPath: plan.linkPath,
|
|
193
|
+
status: "created"
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
async function rollbackCreatedPaths(options) {
|
|
197
|
+
await Promise.all(options.createdLinkPaths.map((linkPath) => fs.remove(linkPath)));
|
|
198
|
+
if (options.canonicalExisted) {
|
|
199
|
+
await fs.remove(options.skillFilePath);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
await fs.remove(options.canonicalPath);
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/commands/create.ts
|
|
206
|
+
function promptOrExit(value) {
|
|
207
|
+
if (isCancel(value)) {
|
|
208
|
+
outro("Cancelled");
|
|
209
|
+
process.exit(0);
|
|
210
|
+
}
|
|
211
|
+
return value;
|
|
212
|
+
}
|
|
213
|
+
function requireNonEmpty(message) {
|
|
214
|
+
return (value) => {
|
|
215
|
+
if (!(value ?? "").trim()) return message;
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function createCommand() {
|
|
219
|
+
const command = new Command("create");
|
|
220
|
+
command.description("Create a shared skill and link it into agent skill directories");
|
|
221
|
+
command.action(async () => {
|
|
222
|
+
intro("Create a shared agent skill");
|
|
223
|
+
const name = promptOrExit(await text({
|
|
224
|
+
message: "Skill name",
|
|
225
|
+
placeholder: "review-helper",
|
|
226
|
+
validate: requireNonEmpty("Enter a skill name.")
|
|
227
|
+
}));
|
|
228
|
+
const description = promptOrExit(await text({
|
|
229
|
+
message: "Skill description",
|
|
230
|
+
placeholder: "Helps agents review code consistently.",
|
|
231
|
+
validate: requireNonEmpty("Enter a skill description.")
|
|
232
|
+
}));
|
|
233
|
+
const location = promptOrExit(await select({
|
|
234
|
+
message: "Where should the canonical skill live?",
|
|
235
|
+
options: [{
|
|
236
|
+
value: "project",
|
|
237
|
+
label: "Current project",
|
|
238
|
+
hint: "./.agents/skills/<skill-name>"
|
|
239
|
+
}, {
|
|
240
|
+
value: "home",
|
|
241
|
+
label: "Home directory",
|
|
242
|
+
hint: "~/.agents/skills/<skill-name>"
|
|
243
|
+
}]
|
|
244
|
+
}));
|
|
245
|
+
const selectedAgentIds = promptOrExit(await multiselect({
|
|
246
|
+
message: "Which agents should use this skill?",
|
|
247
|
+
options: supportedAgents.map((agent) => ({
|
|
248
|
+
value: agent.id,
|
|
249
|
+
label: agent.label
|
|
250
|
+
})),
|
|
251
|
+
required: true
|
|
252
|
+
}));
|
|
253
|
+
const canonicalLocation = canonicalSkillLocationSchema.parse(location);
|
|
254
|
+
const agents = getAgentsById(agentIdSchema.array().parse(selectedAgentIds), canonicalLocation);
|
|
255
|
+
const activity = spinner();
|
|
256
|
+
activity.start("Creating canonical skill and symlinks");
|
|
257
|
+
try {
|
|
258
|
+
const result = await createSkill({
|
|
259
|
+
name,
|
|
260
|
+
description,
|
|
261
|
+
location: canonicalLocation,
|
|
262
|
+
agents
|
|
263
|
+
});
|
|
264
|
+
activity.stop("Skill created");
|
|
265
|
+
note([`Canonical: ${result.canonicalPath}`, ...result.links.map((link) => `${link.agent.label}: ${link.linkPath} (${link.status})`)].join("\n"), result.skillName);
|
|
266
|
+
outro("Done");
|
|
267
|
+
} catch (error) {
|
|
268
|
+
activity.stop("Could not create skill");
|
|
269
|
+
outro(error instanceof Error ? error.message : "Unknown error");
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return command;
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/index.ts
|
|
277
|
+
const program = new Command();
|
|
278
|
+
program.name("pouch").description("Create and link shared agent skills").version("0.1.0");
|
|
279
|
+
program.addCommand(createCommand());
|
|
280
|
+
await program.parseAsync();
|
|
281
|
+
//#endregion
|
|
282
|
+
export {};
|
|
283
|
+
|
|
284
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/paths.ts","../src/lib/agents.ts","../src/lib/fs.ts","../src/lib/skill.ts","../src/commands/create.ts","../src/index.ts"],"sourcesContent":["import os from 'node:os'\nimport path from 'node:path'\nimport { z } from 'zod'\n\nexport const canonicalSkillLocationSchema = z.enum(['home', 'project'])\n\nexport type CanonicalSkillLocation = z.infer<typeof canonicalSkillLocationSchema>\n\nexport function resolveHomePath(value: string): string {\n if (value === '~') {\n return os.homedir()\n }\n\n if (value.startsWith('~/')) {\n return path.join(os.homedir(), value.slice(2))\n }\n\n return value\n}\n\nexport function getLocationRoot(location: CanonicalSkillLocation): string {\n if (location === 'project') {\n return process.cwd()\n }\n\n return os.homedir()\n}\n\nexport function getCanonicalSkillsDirectory(location: CanonicalSkillLocation): string {\n return path.join(getLocationRoot(location), '.agents', 'skills')\n}\n\nexport function getCanonicalSkillPath(location: CanonicalSkillLocation, skillName: string): string {\n return path.join(getCanonicalSkillsDirectory(location), skillName)\n}\n\nexport function toComparablePath(value: string): string {\n const resolved = path.resolve(value)\n\n if (resolved.startsWith('/private/var/')) {\n return resolved.replace('/private/var/', '/var/')\n }\n\n return resolved\n}\n","import { z } from 'zod'\nimport path from 'node:path'\nimport { getLocationRoot } from '@/lib/paths.js'\nimport type { CanonicalSkillLocation } from '@/lib/paths.js'\n\nexport const agentIdSchema = z.enum(['codex', 'cursor', 'claude'])\n\nexport type AgentId = z.infer<typeof agentIdSchema>\n\nexport type AgentDefinition = {\n id: AgentId\n label: string\n skillDirectory: string\n}\n\ntype SupportedAgent = {\n id: AgentId\n label: string\n directoryName: string\n}\n\nexport const supportedAgents = [\n {\n id: 'codex',\n label: 'Codex',\n directoryName: '.codex',\n },\n {\n id: 'cursor',\n label: 'Cursor',\n directoryName: '.cursor',\n },\n {\n id: 'claude',\n label: 'Claude',\n directoryName: '.claude',\n },\n] satisfies SupportedAgent[]\n\nexport function getAgentsById(\n agentIds: AgentId[],\n location: CanonicalSkillLocation,\n): AgentDefinition[] {\n const selected = new Set(agentIds)\n return supportedAgents\n .filter((agent) => selected.has(agent.id))\n .map((agent) => ({\n id: agent.id,\n label: agent.label,\n skillDirectory: getAgentSkillDirectory(agent.directoryName, location),\n }))\n}\n\nfunction getAgentSkillDirectory(directoryName: string, location: CanonicalSkillLocation): string {\n return path.join(getLocationRoot(location), directoryName, 'skills')\n}\n","import fs from 'fs-extra'\n\nexport function isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && 'code' in error\n}\n\nexport async function lstatOrNull(filePath: string): Promise<fs.Stats | null> {\n return fs.lstat(filePath).catch((error: unknown) => {\n if (isNodeError(error) && error.code === 'ENOENT') {\n return null\n }\n\n throw error\n })\n}\n","import path from 'node:path'\nimport fs from 'fs-extra'\nimport { z } from 'zod'\nimport type { AgentDefinition } from '@/lib/agents.js'\nimport { isNodeError, lstatOrNull } from '@/lib/fs.js'\nimport {\n getCanonicalSkillPath,\n toComparablePath,\n type CanonicalSkillLocation,\n} from '@/lib/paths.js'\n\nconst skillInputSchema = z.object({\n name: z.string().trim().min(1, 'Skill name is required'),\n description: z.string().trim().min(1, 'Skill description is required'),\n})\n\nexport type CreateSkillInput = z.input<typeof skillInputSchema> & {\n agents: AgentDefinition[]\n location: CanonicalSkillLocation\n}\n\nexport type SkillLinkPlan =\n | { action: 'link'; agent: AgentDefinition; linkPath: string }\n | { action: 'skip'; agent: AgentDefinition; linkPath: string }\n\nexport type SkillLinkResult = {\n agent: AgentDefinition\n linkPath: string\n status: 'created' | 'existing'\n}\n\nexport type CreateSkillResult = {\n skillName: string\n canonicalPath: string\n skillFilePath: string\n links: SkillLinkResult[]\n}\n\nexport function toSkillSlug(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n\nexport async function createSkill(input: CreateSkillInput): Promise<CreateSkillResult> {\n const parsed = skillInputSchema.parse(input)\n const skillName = toSkillSlug(parsed.name)\n\n if (!skillName) {\n throw new Error('Skill name must include at least one letter or number.')\n }\n\n const agents = dedupeAgents(input.agents)\n const canonicalPath = getCanonicalSkillPath(input.location, skillName)\n const skillFilePath = path.join(canonicalPath, 'SKILL.md')\n const canonicalExisted = await fs.pathExists(canonicalPath)\n\n await ensureCanonicalSkillCanBeCreated(canonicalPath)\n const linkPlans = await planSkillSymlinks(agents, canonicalPath, skillName)\n\n await fs.ensureDir(canonicalPath)\n await writeSkillFile(skillFilePath, parsed.name, parsed.description)\n\n const links: SkillLinkResult[] = []\n const createdLinkPaths: string[] = []\n\n try {\n for (const linkPlan of linkPlans) {\n const result = await applySkillSymlink(linkPlan, canonicalPath)\n\n if (result.status === 'created') {\n createdLinkPaths.push(result.linkPath)\n }\n\n links.push(result)\n }\n } catch (error) {\n await rollbackCreatedPaths({\n canonicalExisted,\n canonicalPath,\n createdLinkPaths,\n skillFilePath,\n })\n\n throw error\n }\n\n return {\n skillName,\n canonicalPath,\n skillFilePath,\n links,\n }\n}\n\nfunction dedupeAgents(agents: AgentDefinition[]): AgentDefinition[] {\n const seen = new Set<AgentDefinition['id']>()\n\n return agents.filter((agent) => {\n if (seen.has(agent.id)) {\n return false\n }\n\n seen.add(agent.id)\n return true\n })\n}\n\nasync function writeSkillFile(\n skillFilePath: string,\n name: string,\n description: string,\n): Promise<void> {\n await fs\n .writeFile(skillFilePath, renderSkillMarkdown(name, description), {\n flag: 'wx',\n })\n .catch((error: unknown) => {\n if (isNodeError(error) && error.code === 'EEXIST') {\n throw new Error(`A canonical skill already exists at ${skillFilePath}`)\n }\n\n throw error\n })\n}\n\nasync function ensureCanonicalSkillCanBeCreated(canonicalPath: string): Promise<void> {\n await ensureDirectoryPathCanBeCreated(path.dirname(canonicalPath))\n\n const existingCanonicalPath = await lstatOrNull(canonicalPath)\n\n if (existingCanonicalPath && !existingCanonicalPath.isDirectory()) {\n throw new Error(`Cannot create skill because ${canonicalPath} already exists.`)\n }\n\n if (existingCanonicalPath) {\n const contents = await fs.readdir(canonicalPath)\n\n if (contents.length > 0 && !contents.includes('SKILL.md')) {\n throw new Error(\n `Cannot create skill because ${canonicalPath} already exists and is not empty.`,\n )\n }\n }\n}\n\nfunction renderSkillMarkdown(name: string, description: string): string {\n return `---\nname: ${toSkillSlug(name)}\ndescription: ${JSON.stringify(description.trim())}\n---\n\n# ${name.trim()}\n\n${description.trim()}\n`\n}\n\nasync function planSkillSymlinks(\n agents: AgentDefinition[],\n canonicalPath: string,\n skillName: string,\n): Promise<SkillLinkPlan[]> {\n return Promise.all(\n agents.map(async (agent) => {\n const linkPath = path.join(agent.skillDirectory, skillName)\n await ensureDirectoryPathCanBeCreated(path.dirname(linkPath))\n\n const existing = await lstatOrNull(linkPath)\n\n if (!existing) {\n return {\n agent,\n linkPath,\n action: 'link' as const,\n }\n }\n\n if (!existing.isSymbolicLink()) {\n throw new Error(\n `Cannot link skill because ${linkPath} already exists and is not a symlink.`,\n )\n }\n\n const target = await fs.readlink(linkPath)\n const resolvedTarget = path.resolve(path.dirname(linkPath), target)\n\n if (toComparablePath(resolvedTarget) !== toComparablePath(canonicalPath)) {\n throw new Error(`Cannot link skill because ${linkPath} points to ${resolvedTarget}.`)\n }\n\n return {\n agent,\n linkPath,\n action: 'skip' as const,\n }\n }),\n )\n}\n\nasync function ensureDirectoryPathCanBeCreated(directoryPath: string): Promise<void> {\n const existing = await lstatOrNull(directoryPath)\n\n if (existing) {\n if (!existing.isDirectory()) {\n throw new Error(\n `Cannot create directory because ${directoryPath} already exists and is not a directory.`,\n )\n }\n\n return\n }\n\n const parentPath = path.dirname(directoryPath)\n\n if (parentPath === directoryPath) {\n return\n }\n\n await ensureDirectoryPathCanBeCreated(parentPath)\n}\n\nasync function applySkillSymlink(\n plan: SkillLinkPlan,\n canonicalPath: string,\n): Promise<SkillLinkResult> {\n if (plan.action === 'skip') {\n return {\n agent: plan.agent,\n linkPath: plan.linkPath,\n status: 'existing',\n }\n }\n\n await fs.ensureDir(path.dirname(plan.linkPath))\n const linkTarget = path.relative(path.dirname(plan.linkPath), canonicalPath)\n await fs.symlink(linkTarget, plan.linkPath, 'dir')\n\n return {\n agent: plan.agent,\n linkPath: plan.linkPath,\n status: 'created',\n }\n}\n\nasync function rollbackCreatedPaths(options: {\n canonicalExisted: boolean\n canonicalPath: string\n createdLinkPaths: string[]\n skillFilePath: string\n}): Promise<void> {\n await Promise.all(options.createdLinkPaths.map((linkPath) => fs.remove(linkPath)))\n\n if (options.canonicalExisted) {\n await fs.remove(options.skillFilePath)\n return\n }\n\n await fs.remove(options.canonicalPath)\n}\n","import { intro, isCancel, multiselect, note, outro, select, spinner, text } from '@clack/prompts'\nimport { Command } from 'commander'\nimport { agentIdSchema, getAgentsById, supportedAgents } from '@/lib/agents.js'\nimport { createSkill } from '@/lib/skill.js'\nimport { canonicalSkillLocationSchema } from '@/lib/paths.js'\n\nfunction promptOrExit<T>(value: T | symbol): T {\n if (isCancel(value)) {\n outro('Cancelled')\n process.exit(0)\n }\n\n return value\n}\n\nfunction requireNonEmpty(message: string) {\n return (value?: string) => {\n if (!(value ?? '').trim()) {\n return message\n }\n return undefined\n }\n}\n\nexport function createCommand(): Command {\n const command = new Command('create')\n\n command.description('Create a shared skill and link it into agent skill directories')\n\n command.action(async () => {\n intro('Create a shared agent skill')\n\n const name = promptOrExit(\n await text({\n message: 'Skill name',\n placeholder: 'review-helper',\n validate: requireNonEmpty('Enter a skill name.'),\n }),\n )\n\n const description = promptOrExit(\n await text({\n message: 'Skill description',\n placeholder: 'Helps agents review code consistently.',\n validate: requireNonEmpty('Enter a skill description.'),\n }),\n )\n\n const location = promptOrExit(\n await select({\n message: 'Where should the canonical skill live?',\n options: [\n {\n value: 'project',\n label: 'Current project',\n hint: './.agents/skills/<skill-name>',\n },\n {\n value: 'home',\n label: 'Home directory',\n hint: '~/.agents/skills/<skill-name>',\n },\n ],\n }),\n )\n\n const selectedAgentIds = promptOrExit(\n await multiselect({\n message: 'Which agents should use this skill?',\n options: supportedAgents.map((agent) => ({\n value: agent.id,\n label: agent.label,\n })),\n required: true,\n }),\n )\n\n const canonicalLocation = canonicalSkillLocationSchema.parse(location)\n const parsedAgentIds = agentIdSchema.array().parse(selectedAgentIds)\n const agents = getAgentsById(parsedAgentIds, canonicalLocation)\n const activity = spinner()\n\n activity.start('Creating canonical skill and symlinks')\n\n try {\n const result = await createSkill({\n name,\n description,\n location: canonicalLocation,\n agents,\n })\n\n activity.stop('Skill created')\n\n note(\n [\n `Canonical: ${result.canonicalPath}`,\n ...result.links.map((link) => `${link.agent.label}: ${link.linkPath} (${link.status})`),\n ].join('\\n'),\n result.skillName,\n )\n\n outro('Done')\n } catch (error) {\n activity.stop('Could not create skill')\n outro(error instanceof Error ? error.message : 'Unknown error')\n process.exit(1)\n }\n })\n\n return command\n}\n","#!/usr/bin/env node\n\nimport { Command } from 'commander'\nimport { createCommand } from '@/commands/create.js'\n\nconst program = new Command()\n\nprogram.name('pouch').description('Create and link shared agent skills').version('0.1.0')\n\nprogram.addCommand(createCommand())\n\nawait program.parseAsync()\n"],"mappings":";;;;;;;;AAIA,MAAa,+BAA+B,EAAE,KAAK,CAAC,QAAQ,SAAS,CAAC;AAgBtE,SAAgB,gBAAgB,UAA0C;CACxE,IAAI,aAAa,WACf,OAAO,QAAQ,IAAI;CAGrB,OAAO,GAAG,QAAQ;AACpB;AAEA,SAAgB,4BAA4B,UAA0C;CACpF,OAAO,KAAK,KAAK,gBAAgB,QAAQ,GAAG,WAAW,QAAQ;AACjE;AAEA,SAAgB,sBAAsB,UAAkC,WAA2B;CACjG,OAAO,KAAK,KAAK,4BAA4B,QAAQ,GAAG,SAAS;AACnE;AAEA,SAAgB,iBAAiB,OAAuB;CACtD,MAAM,WAAW,KAAK,QAAQ,KAAK;CAEnC,IAAI,SAAS,WAAW,eAAe,GACrC,OAAO,SAAS,QAAQ,iBAAiB,OAAO;CAGlD,OAAO;AACT;;;ACvCA,MAAa,gBAAgB,EAAE,KAAK;CAAC;CAAS;CAAU;AAAQ,CAAC;AAgBjE,MAAa,kBAAkB;CAC7B;EACE,IAAI;EACJ,OAAO;EACP,eAAe;CACjB;CACA;EACE,IAAI;EACJ,OAAO;EACP,eAAe;CACjB;CACA;EACE,IAAI;EACJ,OAAO;EACP,eAAe;CACjB;AACF;AAEA,SAAgB,cACd,UACA,UACmB;CACnB,MAAM,WAAW,IAAI,IAAI,QAAQ;CACjC,OAAO,gBACJ,QAAQ,UAAU,SAAS,IAAI,MAAM,EAAE,CAAC,EACxC,KAAK,WAAW;EACf,IAAI,MAAM;EACV,OAAO,MAAM;EACb,gBAAgB,uBAAuB,MAAM,eAAe,QAAQ;CACtE,EAAE;AACN;AAEA,SAAS,uBAAuB,eAAuB,UAA0C;CAC/F,OAAO,KAAK,KAAK,gBAAgB,QAAQ,GAAG,eAAe,QAAQ;AACrE;;;ACrDA,SAAgB,YAAY,OAAgD;CAC1E,OAAO,iBAAiB,SAAS,UAAU;AAC7C;AAEA,eAAsB,YAAY,UAA4C;CAC5E,OAAO,GAAG,MAAM,QAAQ,EAAE,OAAO,UAAmB;EAClD,IAAI,YAAY,KAAK,KAAK,MAAM,SAAS,UACvC,OAAO;EAGT,MAAM;CACR,CAAC;AACH;;;ACHA,MAAM,mBAAmB,EAAE,OAAO;CAChC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,wBAAwB;CACvD,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,+BAA+B;AACvE,CAAC;AAwBD,SAAgB,YAAY,OAAuB;CACjD,OAAO,MACJ,KAAK,EACL,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE;AAC3B;AAEA,eAAsB,YAAY,OAAqD;CACrF,MAAM,SAAS,iBAAiB,MAAM,KAAK;CAC3C,MAAM,YAAY,YAAY,OAAO,IAAI;CAEzC,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,wDAAwD;CAG1E,MAAM,SAAS,aAAa,MAAM,MAAM;CACxC,MAAM,gBAAgB,sBAAsB,MAAM,UAAU,SAAS;CACrE,MAAM,gBAAgB,KAAK,KAAK,eAAe,UAAU;CACzD,MAAM,mBAAmB,MAAM,GAAG,WAAW,aAAa;CAE1D,MAAM,iCAAiC,aAAa;CACpD,MAAM,YAAY,MAAM,kBAAkB,QAAQ,eAAe,SAAS;CAE1E,MAAM,GAAG,UAAU,aAAa;CAChC,MAAM,eAAe,eAAe,OAAO,MAAM,OAAO,WAAW;CAEnE,MAAM,QAA2B,CAAC;CAClC,MAAM,mBAA6B,CAAC;CAEpC,IAAI;EACF,KAAK,MAAM,YAAY,WAAW;GAChC,MAAM,SAAS,MAAM,kBAAkB,UAAU,aAAa;GAE9D,IAAI,OAAO,WAAW,WACpB,iBAAiB,KAAK,OAAO,QAAQ;GAGvC,MAAM,KAAK,MAAM;EACnB;CACF,SAAS,OAAO;EACd,MAAM,qBAAqB;GACzB;GACA;GACA;GACA;EACF,CAAC;EAED,MAAM;CACR;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;AAEA,SAAS,aAAa,QAA8C;CAClE,MAAM,uBAAO,IAAI,IAA2B;CAE5C,OAAO,OAAO,QAAQ,UAAU;EAC9B,IAAI,KAAK,IAAI,MAAM,EAAE,GACnB,OAAO;EAGT,KAAK,IAAI,MAAM,EAAE;EACjB,OAAO;CACT,CAAC;AACH;AAEA,eAAe,eACb,eACA,MACA,aACe;CACf,MAAM,GACH,UAAU,eAAe,oBAAoB,MAAM,WAAW,GAAG,EAChE,MAAM,KACR,CAAC,EACA,OAAO,UAAmB;EACzB,IAAI,YAAY,KAAK,KAAK,MAAM,SAAS,UACvC,MAAM,IAAI,MAAM,uCAAuC,eAAe;EAGxE,MAAM;CACR,CAAC;AACL;AAEA,eAAe,iCAAiC,eAAsC;CACpF,MAAM,gCAAgC,KAAK,QAAQ,aAAa,CAAC;CAEjE,MAAM,wBAAwB,MAAM,YAAY,aAAa;CAE7D,IAAI,yBAAyB,CAAC,sBAAsB,YAAY,GAC9D,MAAM,IAAI,MAAM,+BAA+B,cAAc,iBAAiB;CAGhF,IAAI,uBAAuB;EACzB,MAAM,WAAW,MAAM,GAAG,QAAQ,aAAa;EAE/C,IAAI,SAAS,SAAS,KAAK,CAAC,SAAS,SAAS,UAAU,GACtD,MAAM,IAAI,MACR,+BAA+B,cAAc,kCAC/C;CAEJ;AACF;AAEA,SAAS,oBAAoB,MAAc,aAA6B;CACtE,OAAO;QACD,YAAY,IAAI,EAAE;eACX,KAAK,UAAU,YAAY,KAAK,CAAC,EAAE;;;IAG9C,KAAK,KAAK,EAAE;;EAEd,YAAY,KAAK,EAAE;;AAErB;AAEA,eAAe,kBACb,QACA,eACA,WAC0B;CAC1B,OAAO,QAAQ,IACb,OAAO,IAAI,OAAO,UAAU;EAC1B,MAAM,WAAW,KAAK,KAAK,MAAM,gBAAgB,SAAS;EAC1D,MAAM,gCAAgC,KAAK,QAAQ,QAAQ,CAAC;EAE5D,MAAM,WAAW,MAAM,YAAY,QAAQ;EAE3C,IAAI,CAAC,UACH,OAAO;GACL;GACA;GACA,QAAQ;EACV;EAGF,IAAI,CAAC,SAAS,eAAe,GAC3B,MAAM,IAAI,MACR,6BAA6B,SAAS,sCACxC;EAGF,MAAM,SAAS,MAAM,GAAG,SAAS,QAAQ;EACzC,MAAM,iBAAiB,KAAK,QAAQ,KAAK,QAAQ,QAAQ,GAAG,MAAM;EAElE,IAAI,iBAAiB,cAAc,MAAM,iBAAiB,aAAa,GACrE,MAAM,IAAI,MAAM,6BAA6B,SAAS,aAAa,eAAe,EAAE;EAGtF,OAAO;GACL;GACA;GACA,QAAQ;EACV;CACF,CAAC,CACH;AACF;AAEA,eAAe,gCAAgC,eAAsC;CACnF,MAAM,WAAW,MAAM,YAAY,aAAa;CAEhD,IAAI,UAAU;EACZ,IAAI,CAAC,SAAS,YAAY,GACxB,MAAM,IAAI,MACR,mCAAmC,cAAc,wCACnD;EAGF;CACF;CAEA,MAAM,aAAa,KAAK,QAAQ,aAAa;CAE7C,IAAI,eAAe,eACjB;CAGF,MAAM,gCAAgC,UAAU;AAClD;AAEA,eAAe,kBACb,MACA,eAC0B;CAC1B,IAAI,KAAK,WAAW,QAClB,OAAO;EACL,OAAO,KAAK;EACZ,UAAU,KAAK;EACf,QAAQ;CACV;CAGF,MAAM,GAAG,UAAU,KAAK,QAAQ,KAAK,QAAQ,CAAC;CAC9C,MAAM,aAAa,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,GAAG,aAAa;CAC3E,MAAM,GAAG,QAAQ,YAAY,KAAK,UAAU,KAAK;CAEjD,OAAO;EACL,OAAO,KAAK;EACZ,UAAU,KAAK;EACf,QAAQ;CACV;AACF;AAEA,eAAe,qBAAqB,SAKlB;CAChB,MAAM,QAAQ,IAAI,QAAQ,iBAAiB,KAAK,aAAa,GAAG,OAAO,QAAQ,CAAC,CAAC;CAEjF,IAAI,QAAQ,kBAAkB;EAC5B,MAAM,GAAG,OAAO,QAAQ,aAAa;EACrC;CACF;CAEA,MAAM,GAAG,OAAO,QAAQ,aAAa;AACvC;;;AC/PA,SAAS,aAAgB,OAAsB;CAC7C,IAAI,SAAS,KAAK,GAAG;EACnB,MAAM,WAAW;EACjB,QAAQ,KAAK,CAAC;CAChB;CAEA,OAAO;AACT;AAEA,SAAS,gBAAgB,SAAiB;CACxC,QAAQ,UAAmB;EACzB,IAAI,EAAE,SAAS,IAAI,KAAK,GACtB,OAAO;CAGX;AACF;AAEA,SAAgB,gBAAyB;CACvC,MAAM,UAAU,IAAI,QAAQ,QAAQ;CAEpC,QAAQ,YAAY,gEAAgE;CAEpF,QAAQ,OAAO,YAAY;EACzB,MAAM,6BAA6B;EAEnC,MAAM,OAAO,aACX,MAAM,KAAK;GACT,SAAS;GACT,aAAa;GACb,UAAU,gBAAgB,qBAAqB;EACjD,CAAC,CACH;EAEA,MAAM,cAAc,aAClB,MAAM,KAAK;GACT,SAAS;GACT,aAAa;GACb,UAAU,gBAAgB,4BAA4B;EACxD,CAAC,CACH;EAEA,MAAM,WAAW,aACf,MAAM,OAAO;GACX,SAAS;GACT,SAAS,CACP;IACE,OAAO;IACP,OAAO;IACP,MAAM;GACR,GACA;IACE,OAAO;IACP,OAAO;IACP,MAAM;GACR,CACF;EACF,CAAC,CACH;EAEA,MAAM,mBAAmB,aACvB,MAAM,YAAY;GAChB,SAAS;GACT,SAAS,gBAAgB,KAAK,WAAW;IACvC,OAAO,MAAM;IACb,OAAO,MAAM;GACf,EAAE;GACF,UAAU;EACZ,CAAC,CACH;EAEA,MAAM,oBAAoB,6BAA6B,MAAM,QAAQ;EAErE,MAAM,SAAS,cADQ,cAAc,MAAM,EAAE,MAAM,gBACT,GAAG,iBAAiB;EAC9D,MAAM,WAAW,QAAQ;EAEzB,SAAS,MAAM,uCAAuC;EAEtD,IAAI;GACF,MAAM,SAAS,MAAM,YAAY;IAC/B;IACA;IACA,UAAU;IACV;GACF,CAAC;GAED,SAAS,KAAK,eAAe;GAE7B,KACE,CACE,cAAc,OAAO,iBACrB,GAAG,OAAO,MAAM,KAAK,SAAS,GAAG,KAAK,MAAM,MAAM,IAAI,KAAK,SAAS,IAAI,KAAK,OAAO,EAAE,CACxF,EAAE,KAAK,IAAI,GACX,OAAO,SACT;GAEA,MAAM,MAAM;EACd,SAAS,OAAO;GACd,SAAS,KAAK,wBAAwB;GACtC,MAAM,iBAAiB,QAAQ,MAAM,UAAU,eAAe;GAC9D,QAAQ,KAAK,CAAC;EAChB;CACF,CAAC;CAED,OAAO;AACT;;;AC1GA,MAAM,UAAU,IAAI,QAAQ;AAE5B,QAAQ,KAAK,OAAO,EAAE,YAAY,qCAAqC,EAAE,QAAQ,OAAO;AAExF,QAAQ,WAAW,cAAc,CAAC;AAElC,MAAM,QAAQ,WAAW"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@michaeltroya/pouch",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create and link shared agent skills",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agents",
|
|
7
|
+
"ai",
|
|
8
|
+
"claude",
|
|
9
|
+
"cli",
|
|
10
|
+
"codex",
|
|
11
|
+
"cursor",
|
|
12
|
+
"skills"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/michaeltroya/pouch#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/michaeltroya/pouch/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Michael Troya",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/michaeltroya/pouch.git"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"pouch": "./dist/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"dev": "tsx src/index.ts",
|
|
36
|
+
"build": "vp pack",
|
|
37
|
+
"check": "vp check",
|
|
38
|
+
"check:fix": "vp check --fix",
|
|
39
|
+
"format": "vp fmt",
|
|
40
|
+
"lint": "vp lint",
|
|
41
|
+
"prepack": "vp pack",
|
|
42
|
+
"test": "vp test",
|
|
43
|
+
"prepare": "vp config"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@clack/prompts": "^1.5.1",
|
|
47
|
+
"commander": "^15.0.0",
|
|
48
|
+
"fs-extra": "^11.3.5",
|
|
49
|
+
"zod": "^4.4.3"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/fs-extra": "^11.0.4",
|
|
53
|
+
"@types/node": "^25.9.3",
|
|
54
|
+
"oxfmt": "^0.54.0",
|
|
55
|
+
"oxlint": "^1.69.0",
|
|
56
|
+
"tsx": "^4.22.4",
|
|
57
|
+
"typescript": "^6.0.3",
|
|
58
|
+
"vite-plus": "catalog:",
|
|
59
|
+
"vitest": "catalog:"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=22.13.0"
|
|
63
|
+
},
|
|
64
|
+
"packageManager": "pnpm@11.7.0+sha512.19cc852c120c7125760f2443ee6be0ca5b40f9f50598de1a09a1f177503e010e57c23c77646e01e761de59bf874fb22a3398c33ab9691fc13eb946b6f0f4d620"
|
|
65
|
+
}
|