cursor-template 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 +141 -0
- package/bin/cli.js +5 -0
- package/dist/cli/args.d.ts +30 -0
- package/dist/cli/args.js +156 -0
- package/dist/cli/run.d.ts +11 -0
- package/dist/cli/run.js +210 -0
- package/dist/config/constants.d.ts +27 -0
- package/dist/config/constants.js +27 -0
- package/dist/core/adapters.d.ts +7 -0
- package/dist/core/adapters.js +28 -0
- package/dist/core/doctor.d.ts +8 -0
- package/dist/core/doctor.js +77 -0
- package/dist/core/install.d.ts +29 -0
- package/dist/core/install.js +206 -0
- package/dist/core/lifecycle.d.ts +14 -0
- package/dist/core/lifecycle.js +58 -0
- package/dist/core/manifest.d.ts +10 -0
- package/dist/core/manifest.js +63 -0
- package/dist/core/mcp.d.ts +22 -0
- package/dist/core/mcp.js +130 -0
- package/dist/core/paths.d.ts +13 -0
- package/dist/core/paths.js +54 -0
- package/dist/core/types.d.ts +80 -0
- package/dist/core/types.js +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/services/github.d.ts +28 -0
- package/dist/services/github.js +211 -0
- package/dist/utils/errors.d.ts +6 -0
- package/dist/utils/errors.js +17 -0
- package/dist/utils/frontmatter.d.ts +6 -0
- package/dist/utils/frontmatter.js +32 -0
- package/dist/utils/fs.d.ts +19 -0
- package/dist/utils/fs.js +106 -0
- package/dist/utils/ids.d.ts +6 -0
- package/dist/utils/ids.js +30 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cursor-template 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,141 @@
|
|
|
1
|
+
# cursor-template
|
|
2
|
+
|
|
3
|
+
[](https://github.com/joeneldeasis/cursor-template/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/joeneldeasis/cursor-template/actions/workflows/codeql.yml)
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
Install [aitmpl.com](https://aitmpl.com/) skills, commands, agents, and MCPs into Cursor.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx cursor-template --skill creative-design/frontend-design
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g cursor-template
|
|
18
|
+
cursor-template --help
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or run it once without a global install:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx cursor-template@latest --skill creative-design/frontend-design
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
Global install is the default. Add `--project` or `--directory <path>` to write into a repository.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx cursor-template --skill creative-design/frontend-design
|
|
33
|
+
npx cursor-template --command testing/generate-tests
|
|
34
|
+
npx cursor-template --agent development-team/frontend-developer
|
|
35
|
+
npx cursor-template --mcp web/web-fetch
|
|
36
|
+
npx cursor-template --doctor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
| Component | Location |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| Skill | `~/.cursor/skills/<name>/` |
|
|
42
|
+
| Command | `~/.cursor/commands/<name>.md` |
|
|
43
|
+
| Agent | `~/.cursor/agents/<name>.md` |
|
|
44
|
+
| MCP | merged into `~/.cursor/mcp.json` |
|
|
45
|
+
|
|
46
|
+
The category prefix is dropped, so `creative-design/frontend-design` installs as `frontend-design`.
|
|
47
|
+
|
|
48
|
+
## Commands and options
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
cursor-template [options]
|
|
52
|
+
|
|
53
|
+
--skill <ids> Install skills, comma-separated
|
|
54
|
+
--command <ids> Install commands, comma-separated
|
|
55
|
+
--agent <ids> Install agents, comma-separated
|
|
56
|
+
--mcp <ids> Install MCPs, comma-separated
|
|
57
|
+
-d, --directory <path> Install into a project instead of the user profile
|
|
58
|
+
--project Same as --directory .
|
|
59
|
+
-y, --yes Overwrite without prompting
|
|
60
|
+
--dry-run Show planned writes and do not change files
|
|
61
|
+
--list [type] List catalog ids
|
|
62
|
+
--search <type> <query>
|
|
63
|
+
--info <type> <id>
|
|
64
|
+
--installed
|
|
65
|
+
--remove <type> <id>
|
|
66
|
+
--update <type> <id>
|
|
67
|
+
--update --all
|
|
68
|
+
--doctor
|
|
69
|
+
--verbose
|
|
70
|
+
--debug Print stack traces
|
|
71
|
+
-h, --help
|
|
72
|
+
-V, --version
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Browse the catalog at [skills](https://aitmpl.com/skills/), [commands](https://aitmpl.com/commands/), [agents](https://aitmpl.com/agents/), and [MCPs](https://aitmpl.com/mcps/).
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
There is no config file. Scope comes from the CLI:
|
|
80
|
+
|
|
81
|
+
1. `--directory` or `--project` installs into that project
|
|
82
|
+
2. otherwise files go under the user profile
|
|
83
|
+
|
|
84
|
+
Existing MCP servers in `mcp.json` are kept. A different value for the same server asks before overwrite unless `--yes` is set.
|
|
85
|
+
|
|
86
|
+
## Environment variables
|
|
87
|
+
|
|
88
|
+
| Variable | Purpose |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `GITHUB_TOKEN` | Optional token for the GitHub catalog API when unauthenticated requests are rate-limited. The token is not printed. |
|
|
91
|
+
|
|
92
|
+
## Programmatic API
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
import { installComponent, destRoots, createCatalog } from 'cursor-template';
|
|
96
|
+
|
|
97
|
+
const roots = destRoots({ target: 'cursor', scope: 'project', directory: process.cwd() });
|
|
98
|
+
const catalog = createCatalog();
|
|
99
|
+
await installComponent({
|
|
100
|
+
target: 'cursor',
|
|
101
|
+
roots,
|
|
102
|
+
type: 'skill',
|
|
103
|
+
id: 'creative-design/frontend-design',
|
|
104
|
+
yes: true,
|
|
105
|
+
catalog,
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Also exported: `removeComponent`, `listInstalled`, `checkInstall`, and `runCli`.
|
|
110
|
+
|
|
111
|
+
## Troubleshooting
|
|
112
|
+
|
|
113
|
+
`GitHub rate limit` means the catalog request was rejected. Set `GITHUB_TOKEN` and run the command again.
|
|
114
|
+
|
|
115
|
+
`Unknown component type` means the type was not `skill`, `command`, `agent`, or `mcp`.
|
|
116
|
+
|
|
117
|
+
`not found` means that catalog id is not in `davila7/claude-code-templates`. Check the id on aitmpl.com.
|
|
118
|
+
|
|
119
|
+
Exit codes: `0` success, `1` the operation failed, `2` the arguments were invalid.
|
|
120
|
+
|
|
121
|
+
## Development
|
|
122
|
+
|
|
123
|
+
Requires Node.js 20 or newer.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm ci
|
|
127
|
+
npm run validate
|
|
128
|
+
npm run ci
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`npm run validate` lints, typechecks, builds `dist/`, tests, and writes coverage. `npm run ci` also checks the packed files and runs the tarball with npx from a clean temporary directory.
|
|
132
|
+
|
|
133
|
+
## Publishing
|
|
134
|
+
|
|
135
|
+
Publishing happens from a git tag `vX.Y.Z` whose version matches `package.json`. The release workflow runs the same validation, publishes with npm provenance, and creates a GitHub Release.
|
|
136
|
+
|
|
137
|
+
The published package contains `dist/`, `bin/cli.js`, `README.md`, and `LICENSE`. Check a packed build locally with:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run ci
|
|
141
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import type { CliOptions, Target } from '../core/types.js';
|
|
3
|
+
export declare function commandName(target: Target): string;
|
|
4
|
+
export declare function buildProgram(target: Target): Command;
|
|
5
|
+
export declare function expandArgv(argv: string[]): string[];
|
|
6
|
+
interface RawOpts {
|
|
7
|
+
skill?: string;
|
|
8
|
+
command?: string;
|
|
9
|
+
agent?: string;
|
|
10
|
+
mcp?: string;
|
|
11
|
+
directory?: string;
|
|
12
|
+
project?: boolean;
|
|
13
|
+
yes?: boolean;
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
verbose?: boolean;
|
|
16
|
+
debug?: boolean;
|
|
17
|
+
list?: string | boolean;
|
|
18
|
+
installed?: boolean;
|
|
19
|
+
doctor?: boolean;
|
|
20
|
+
search?: string;
|
|
21
|
+
info?: string;
|
|
22
|
+
remove?: string;
|
|
23
|
+
update?: string;
|
|
24
|
+
updateAll?: boolean;
|
|
25
|
+
all?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare function optionsFromCommand(opts: RawOpts): CliOptions;
|
|
28
|
+
export declare function parseCliArgs(argv: string[], target: Target): CliOptions;
|
|
29
|
+
export declare function hasAction(options: CliOptions): boolean;
|
|
30
|
+
export {};
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { VERSION } from '../config/constants.js';
|
|
3
|
+
import { UsageError } from '../utils/errors.js';
|
|
4
|
+
import { normalizeType, splitIds } from '../utils/ids.js';
|
|
5
|
+
export function commandName(target) {
|
|
6
|
+
return target === 'cursor' ? 'cursor-template' : 'codex-template';
|
|
7
|
+
}
|
|
8
|
+
export function buildProgram(target) {
|
|
9
|
+
const name = commandName(target);
|
|
10
|
+
const program = new Command()
|
|
11
|
+
.name(name)
|
|
12
|
+
.description(`Install aitmpl.com skills, commands, agents, and MCPs for ${target === 'cursor' ? 'Cursor' : 'Codex'}`)
|
|
13
|
+
.version(VERSION)
|
|
14
|
+
.option('--skill <ids>', 'install skills (comma-separated)')
|
|
15
|
+
.option('--command <ids>', 'install commands (comma-separated)')
|
|
16
|
+
.option('--agent <ids>', 'install agents (comma-separated)')
|
|
17
|
+
.option('--mcp <ids>', 'install MCPs (comma-separated)')
|
|
18
|
+
.option('-d, --directory <path>', 'install into a project directory instead of the user profile')
|
|
19
|
+
.option('--project', 'alias for --directory .')
|
|
20
|
+
.option('-y, --yes', 'overwrite without prompting')
|
|
21
|
+
.option('--dry-run', 'show planned writes without changing files')
|
|
22
|
+
.option('--verbose', 'log catalog requests (tokens are not printed)')
|
|
23
|
+
.option('--debug', 'print stack traces for unexpected errors')
|
|
24
|
+
.option('--list [type]', 'list catalog ids (skill, command, agent, mcp)')
|
|
25
|
+
.option('--search <pair>', 'search the catalog: --search <type> <query>')
|
|
26
|
+
.option('--info <pair>', 'show one component: --info <type> <id>')
|
|
27
|
+
.option('--installed', 'list components on disk')
|
|
28
|
+
.option('--remove <pair>', 'remove one component: --remove <type> <id>')
|
|
29
|
+
.option('--update <pair>', 're-fetch one component: --update <type> <id>')
|
|
30
|
+
.option('--update-all', 're-fetch every installed component')
|
|
31
|
+
.option('--all', 'with --update, refresh everything installed')
|
|
32
|
+
.option('--doctor', 'check install directories and files')
|
|
33
|
+
.allowExcessArguments(false);
|
|
34
|
+
program.addHelpText('after', `
|
|
35
|
+
Examples:
|
|
36
|
+
${name} --skill creative-design/frontend-design
|
|
37
|
+
${name} --command testing/generate-tests --project
|
|
38
|
+
${name} --search skill frontend
|
|
39
|
+
${name} --doctor
|
|
40
|
+
|
|
41
|
+
Environment:
|
|
42
|
+
GITHUB_TOKEN Optional GitHub token used when the catalog API is rate-limited
|
|
43
|
+
`);
|
|
44
|
+
return program;
|
|
45
|
+
}
|
|
46
|
+
export function expandArgv(argv) {
|
|
47
|
+
const out = argv.slice(0, 2);
|
|
48
|
+
const args = argv.slice(2);
|
|
49
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
50
|
+
const token = args[i];
|
|
51
|
+
if (token === '--update' && args[i + 1] === '--all') {
|
|
52
|
+
out.push('--update-all');
|
|
53
|
+
i += 1;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (token === '--update' && (!args[i + 1] || args[i + 1].startsWith('-'))) {
|
|
57
|
+
out.push('--update-all');
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (token === '--search' || token === '--info' || token === '--remove' || token === '--update') {
|
|
61
|
+
const left = args[i + 1];
|
|
62
|
+
const right = args[i + 2];
|
|
63
|
+
if (!left || left.startsWith('-') || !right || right.startsWith('-')) {
|
|
64
|
+
throw new UsageError(`${token} requires <type> and a second argument.\n\nUse:\n ${token} skill creative-design/frontend-design`);
|
|
65
|
+
}
|
|
66
|
+
out.push(token, `${left} ${right}`);
|
|
67
|
+
i += 2;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
out.push(token);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function splitPair(value, flag) {
|
|
75
|
+
const index = value.indexOf(' ');
|
|
76
|
+
if (index === -1)
|
|
77
|
+
throw new UsageError(`${flag} requires <type> and a second argument`);
|
|
78
|
+
return [value.slice(0, index), value.slice(index + 1)];
|
|
79
|
+
}
|
|
80
|
+
function listValue(value) {
|
|
81
|
+
if (typeof value !== 'string')
|
|
82
|
+
return value === true ? true : null;
|
|
83
|
+
return normalizeType(value);
|
|
84
|
+
}
|
|
85
|
+
export function optionsFromCommand(opts) {
|
|
86
|
+
const options = {
|
|
87
|
+
skill: splitIds(opts.skill),
|
|
88
|
+
command: splitIds(opts.command),
|
|
89
|
+
agent: splitIds(opts.agent),
|
|
90
|
+
mcp: splitIds(opts.mcp),
|
|
91
|
+
directory: opts.directory || null,
|
|
92
|
+
yes: Boolean(opts.yes),
|
|
93
|
+
dryRun: Boolean(opts.dryRun),
|
|
94
|
+
verbose: Boolean(opts.verbose),
|
|
95
|
+
debug: Boolean(opts.debug),
|
|
96
|
+
list: listValue(opts.list),
|
|
97
|
+
installed: Boolean(opts.installed),
|
|
98
|
+
doctor: Boolean(opts.doctor),
|
|
99
|
+
search: null,
|
|
100
|
+
info: null,
|
|
101
|
+
remove: null,
|
|
102
|
+
update: null,
|
|
103
|
+
};
|
|
104
|
+
if (opts.project && !options.directory)
|
|
105
|
+
options.directory = '.';
|
|
106
|
+
if (opts.search) {
|
|
107
|
+
const [type, query] = splitPair(opts.search, '--search');
|
|
108
|
+
options.search = { type: normalizeType(type), query };
|
|
109
|
+
}
|
|
110
|
+
if (opts.info) {
|
|
111
|
+
const [type, id] = splitPair(opts.info, '--info');
|
|
112
|
+
options.info = { type: normalizeType(type), id };
|
|
113
|
+
}
|
|
114
|
+
if (opts.remove) {
|
|
115
|
+
const [type, id] = splitPair(opts.remove, '--remove');
|
|
116
|
+
options.remove = { type: normalizeType(type), id };
|
|
117
|
+
}
|
|
118
|
+
if (opts.updateAll || opts.all)
|
|
119
|
+
options.update = { all: true };
|
|
120
|
+
else if (opts.update) {
|
|
121
|
+
const [type, id] = splitPair(opts.update, '--update');
|
|
122
|
+
options.update = { type: normalizeType(type), id };
|
|
123
|
+
}
|
|
124
|
+
if (opts.all && !opts.update && !opts.updateAll) {
|
|
125
|
+
throw new UsageError('Use --update --all to refresh every installed component.');
|
|
126
|
+
}
|
|
127
|
+
return options;
|
|
128
|
+
}
|
|
129
|
+
export function parseCliArgs(argv, target) {
|
|
130
|
+
const program = buildProgram(target);
|
|
131
|
+
program.exitOverride();
|
|
132
|
+
program.configureOutput({ writeOut: () => { }, writeErr: () => { } });
|
|
133
|
+
try {
|
|
134
|
+
program.parse(expandArgv(argv));
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (error instanceof UsageError)
|
|
138
|
+
throw error;
|
|
139
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
+
throw new UsageError(message);
|
|
141
|
+
}
|
|
142
|
+
return optionsFromCommand(program.opts());
|
|
143
|
+
}
|
|
144
|
+
export function hasAction(options) {
|
|
145
|
+
return Boolean(options.skill.length
|
|
146
|
+
|| options.command.length
|
|
147
|
+
|| options.agent.length
|
|
148
|
+
|| options.mcp.length
|
|
149
|
+
|| options.list !== null
|
|
150
|
+
|| options.search
|
|
151
|
+
|| options.info
|
|
152
|
+
|| options.installed
|
|
153
|
+
|| options.remove
|
|
154
|
+
|| options.update
|
|
155
|
+
|| options.doctor);
|
|
156
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Catalog, Target } from '../core/types.js';
|
|
2
|
+
export interface CliIo {
|
|
3
|
+
stdout?: (message: string) => void;
|
|
4
|
+
stderr?: (message: string) => void;
|
|
5
|
+
home?: string;
|
|
6
|
+
catalog?: Catalog;
|
|
7
|
+
fetchImpl?: typeof fetch;
|
|
8
|
+
argv?: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function printHelp(target: Target): void;
|
|
11
|
+
export declare function runCli(target: Target, argv?: string[], io?: CliIo): Promise<number>;
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { BROWSE } from '../config/constants.js';
|
|
5
|
+
import { doctor } from '../core/doctor.js';
|
|
6
|
+
import { installById, removeInstalled, sourceLine } from '../core/install.js';
|
|
7
|
+
import { collectInstalled } from '../core/lifecycle.js';
|
|
8
|
+
import { readManifest } from '../core/manifest.js';
|
|
9
|
+
import { componentDest, destRoots } from '../core/paths.js';
|
|
10
|
+
import { createCatalog } from '../services/github.js';
|
|
11
|
+
import { UsageError, formatError } from '../utils/errors.js';
|
|
12
|
+
import { assertSafeId, normalizeType } from '../utils/ids.js';
|
|
13
|
+
import { buildProgram, hasAction, parseCliArgs } from './args.js';
|
|
14
|
+
function browseLines() {
|
|
15
|
+
return [
|
|
16
|
+
` skills ${BROWSE.skills}`,
|
|
17
|
+
` commands ${BROWSE.commands}`,
|
|
18
|
+
` agents ${BROWSE.agents}`,
|
|
19
|
+
` mcps ${BROWSE.mcps}`,
|
|
20
|
+
].join('\n');
|
|
21
|
+
}
|
|
22
|
+
export function printHelp(target) {
|
|
23
|
+
buildProgram(target).outputHelp();
|
|
24
|
+
console.log(`\nBrowse:\n${browseLines()}`);
|
|
25
|
+
}
|
|
26
|
+
function rootsFor(target, options, home) {
|
|
27
|
+
return destRoots({
|
|
28
|
+
target,
|
|
29
|
+
scope: options.directory ? 'project' : 'global',
|
|
30
|
+
directory: options.directory ? path.resolve(options.directory) : undefined,
|
|
31
|
+
home,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export async function runCli(target, argv = process.argv, io = {}) {
|
|
35
|
+
const log = io.stdout || console.log;
|
|
36
|
+
const args = argv.slice(2);
|
|
37
|
+
if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
|
|
38
|
+
printHelp(target);
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
if (args.includes('-V') || args.includes('--version')) {
|
|
42
|
+
buildProgram(target).parse(argv);
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
let options;
|
|
46
|
+
try {
|
|
47
|
+
options = parseCliArgs(argv, target);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const stderr = io.stderr || ((message) => console.error(chalk.red(message)));
|
|
51
|
+
stderr(formatError(error, false));
|
|
52
|
+
return error instanceof UsageError ? error.exitCode : 2;
|
|
53
|
+
}
|
|
54
|
+
const stderr = io.stderr || ((message) => console.error(chalk.red(message)));
|
|
55
|
+
if (!hasAction(options)) {
|
|
56
|
+
printHelp(target);
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
const roots = rootsFor(target, options, io.home || os.homedir());
|
|
60
|
+
const catalog = io.catalog || createCatalog({ verbose: options.verbose || options.debug, fetchImpl: io.fetchImpl });
|
|
61
|
+
let code = 0;
|
|
62
|
+
const fail = (error) => {
|
|
63
|
+
stderr(formatError(error, options.debug));
|
|
64
|
+
code = error instanceof UsageError ? error.exitCode : 1;
|
|
65
|
+
};
|
|
66
|
+
if (options.list !== null) {
|
|
67
|
+
try {
|
|
68
|
+
const records = await catalog.list(options.list === true ? null : options.list);
|
|
69
|
+
const groups = new Map();
|
|
70
|
+
for (const record of records) {
|
|
71
|
+
const list = groups.get(record.type) || [];
|
|
72
|
+
list.push(record.id);
|
|
73
|
+
groups.set(record.type, list);
|
|
74
|
+
}
|
|
75
|
+
for (const [group, ids] of groups) {
|
|
76
|
+
log(chalk.cyan(`${group} (${ids.length})`));
|
|
77
|
+
for (const id of ids)
|
|
78
|
+
log(` ${id}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
fail(error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (options.search) {
|
|
86
|
+
try {
|
|
87
|
+
const records = await catalog.search(options.search.type, options.search.query);
|
|
88
|
+
if (!records.length)
|
|
89
|
+
log(`no ${options.search.type} matches for "${options.search.query}"`);
|
|
90
|
+
for (const record of records) {
|
|
91
|
+
const description = await catalog.descriptionFor(record);
|
|
92
|
+
log(`${record.id}${description ? `\n ${description}` : ''}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
fail(error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (options.info) {
|
|
100
|
+
try {
|
|
101
|
+
assertSafeId(options.info.id);
|
|
102
|
+
const record = await catalog.resolve(options.info.type, options.info.id);
|
|
103
|
+
const dest = componentDest({ target, roots, type: record.type, name: record.name });
|
|
104
|
+
const description = await catalog.descriptionFor(record);
|
|
105
|
+
log(`${record.type} ${record.id}`);
|
|
106
|
+
log(`name: ${record.name}`);
|
|
107
|
+
log(`source: ${sourceLine(record)}`);
|
|
108
|
+
log(`install: ${dest.path}`);
|
|
109
|
+
if (description)
|
|
110
|
+
log(`description: ${description}`);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
fail(error);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (options.installed) {
|
|
117
|
+
try {
|
|
118
|
+
const items = await collectInstalled({ target, roots });
|
|
119
|
+
if (!items.length)
|
|
120
|
+
log('nothing installed');
|
|
121
|
+
for (const item of items) {
|
|
122
|
+
const state = item.present ? '' : chalk.yellow(' missing');
|
|
123
|
+
log(`${item.type.padEnd(8)} ${item.tracked ? item.id : `${item.id} (untracked)`}${state}`);
|
|
124
|
+
log(` ${item.path}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
fail(error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (options.remove) {
|
|
132
|
+
try {
|
|
133
|
+
assertSafeId(options.remove.id);
|
|
134
|
+
const removed = await removeInstalled({
|
|
135
|
+
target,
|
|
136
|
+
roots,
|
|
137
|
+
type: options.remove.type,
|
|
138
|
+
id: options.remove.id,
|
|
139
|
+
yes: options.yes,
|
|
140
|
+
dryRun: options.dryRun,
|
|
141
|
+
});
|
|
142
|
+
if (!removed)
|
|
143
|
+
code = 1;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
fail(error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (options.update) {
|
|
150
|
+
try {
|
|
151
|
+
const jobs = [];
|
|
152
|
+
if ('all' in options.update) {
|
|
153
|
+
const manifest = await readManifest(roots.manifest);
|
|
154
|
+
jobs.push(...Object.values(manifest.components));
|
|
155
|
+
if (!jobs.length)
|
|
156
|
+
log('nothing installed to update');
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
assertSafeId(options.update.id);
|
|
160
|
+
jobs.push({ type: options.update.type, id: options.update.id });
|
|
161
|
+
}
|
|
162
|
+
for (const job of jobs) {
|
|
163
|
+
const ok = await installById({
|
|
164
|
+
target,
|
|
165
|
+
roots,
|
|
166
|
+
type: normalizeType(job.type),
|
|
167
|
+
id: job.id,
|
|
168
|
+
yes: true,
|
|
169
|
+
dryRun: options.dryRun,
|
|
170
|
+
catalog,
|
|
171
|
+
});
|
|
172
|
+
if (ok === false)
|
|
173
|
+
code = 1;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
fail(error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const type of ['skill', 'command', 'agent', 'mcp']) {
|
|
181
|
+
for (const id of options[type]) {
|
|
182
|
+
try {
|
|
183
|
+
assertSafeId(id);
|
|
184
|
+
const ok = await installById({ target, roots, type, id, yes: options.yes, dryRun: options.dryRun, catalog });
|
|
185
|
+
if (ok === false)
|
|
186
|
+
code = 1;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
fail(error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (options.doctor) {
|
|
194
|
+
try {
|
|
195
|
+
const report = await doctor({ target, roots });
|
|
196
|
+
for (const note of report.notes)
|
|
197
|
+
log(chalk.green(note));
|
|
198
|
+
for (const issue of report.issues)
|
|
199
|
+
log(chalk.yellow(issue));
|
|
200
|
+
if (report.issues.length)
|
|
201
|
+
code = 1;
|
|
202
|
+
else if (!report.notes.length)
|
|
203
|
+
log('doctor: ok');
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
fail(error);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return code;
|
|
210
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const REPO = "davila7/claude-code-templates";
|
|
2
|
+
export declare const BRANCH = "main";
|
|
3
|
+
export declare const COMPONENTS_PREFIX = "cli-tool/components/";
|
|
4
|
+
export declare const VERSION = "0.1.0";
|
|
5
|
+
export declare const USER_AGENT = "cursor-codex-templates/0.1.0";
|
|
6
|
+
export declare const BROWSE: {
|
|
7
|
+
readonly skills: "https://aitmpl.com/skills/";
|
|
8
|
+
readonly commands: "https://aitmpl.com/commands/";
|
|
9
|
+
readonly agents: "https://aitmpl.com/agents/";
|
|
10
|
+
readonly mcps: "https://aitmpl.com/mcps/";
|
|
11
|
+
};
|
|
12
|
+
export declare const TYPE_ALIASES: {
|
|
13
|
+
readonly skill: "skill";
|
|
14
|
+
readonly skills: "skill";
|
|
15
|
+
readonly command: "command";
|
|
16
|
+
readonly commands: "command";
|
|
17
|
+
readonly agent: "agent";
|
|
18
|
+
readonly agents: "agent";
|
|
19
|
+
readonly mcp: "mcp";
|
|
20
|
+
readonly mcps: "mcp";
|
|
21
|
+
};
|
|
22
|
+
export declare const CATALOG_GROUPS: {
|
|
23
|
+
readonly skills: "skill";
|
|
24
|
+
readonly commands: "command";
|
|
25
|
+
readonly agents: "agent";
|
|
26
|
+
readonly mcps: "mcp";
|
|
27
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const REPO = 'davila7/claude-code-templates';
|
|
2
|
+
export const BRANCH = 'main';
|
|
3
|
+
export const COMPONENTS_PREFIX = 'cli-tool/components/';
|
|
4
|
+
export const VERSION = '0.1.0';
|
|
5
|
+
export const USER_AGENT = `cursor-codex-templates/${VERSION}`;
|
|
6
|
+
export const BROWSE = {
|
|
7
|
+
skills: 'https://aitmpl.com/skills/',
|
|
8
|
+
commands: 'https://aitmpl.com/commands/',
|
|
9
|
+
agents: 'https://aitmpl.com/agents/',
|
|
10
|
+
mcps: 'https://aitmpl.com/mcps/',
|
|
11
|
+
};
|
|
12
|
+
export const TYPE_ALIASES = {
|
|
13
|
+
skill: 'skill',
|
|
14
|
+
skills: 'skill',
|
|
15
|
+
command: 'command',
|
|
16
|
+
commands: 'command',
|
|
17
|
+
agent: 'agent',
|
|
18
|
+
agents: 'agent',
|
|
19
|
+
mcp: 'mcp',
|
|
20
|
+
mcps: 'mcp',
|
|
21
|
+
};
|
|
22
|
+
export const CATALOG_GROUPS = {
|
|
23
|
+
skills: 'skill',
|
|
24
|
+
commands: 'command',
|
|
25
|
+
agents: 'agent',
|
|
26
|
+
mcps: 'mcp',
|
|
27
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function cursorAgentMarkdown(markdown: string): string;
|
|
2
|
+
export declare function agentMarkdownToToml(markdown: string, fallbackName: string): string;
|
|
3
|
+
export declare function wrapCommandAsSkill(markdown: string, name: string): {
|
|
4
|
+
skill: string;
|
|
5
|
+
openaiYaml: string;
|
|
6
|
+
description: string;
|
|
7
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { stringify } from 'smol-toml';
|
|
2
|
+
import { descriptionText, parseFrontmatter, stringifyFrontmatter } from '../utils/frontmatter.js';
|
|
3
|
+
export function cursorAgentMarkdown(markdown) {
|
|
4
|
+
const { data, body } = parseFrontmatter(markdown);
|
|
5
|
+
if (!Object.keys(data).length)
|
|
6
|
+
return markdown.endsWith('\n') ? markdown : `${markdown}\n`;
|
|
7
|
+
delete data.tools;
|
|
8
|
+
return stringifyFrontmatter(data, body);
|
|
9
|
+
}
|
|
10
|
+
export function agentMarkdownToToml(markdown, fallbackName) {
|
|
11
|
+
const { data, body } = parseFrontmatter(markdown);
|
|
12
|
+
const name = descriptionText(data.name) || fallbackName;
|
|
13
|
+
const description = descriptionText(data.description) || `Use the ${name} agent.`;
|
|
14
|
+
return stringify({
|
|
15
|
+
name,
|
|
16
|
+
description,
|
|
17
|
+
developer_instructions: `${body.trim()}\n`,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export function wrapCommandAsSkill(markdown, name) {
|
|
21
|
+
const { data, body } = parseFrontmatter(markdown);
|
|
22
|
+
const description = descriptionText(data.description) || `Run the ${name} workflow from aitmpl.`;
|
|
23
|
+
return {
|
|
24
|
+
skill: stringifyFrontmatter({ name, description }, body),
|
|
25
|
+
openaiYaml: 'policy:\n allow_implicit_invocation: false\n',
|
|
26
|
+
description,
|
|
27
|
+
};
|
|
28
|
+
}
|