acp-vscode 0.3.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/.eslintrc.json +12 -0
- package/.github/workflows/ci.yml +21 -0
- package/.github/workflows/release.yml +50 -0
- package/CONTRIBUTING.md +57 -0
- package/PRD.md +25 -0
- package/README.md +236 -0
- package/__tests__/cache.test.js +7 -0
- package/__tests__/commands-actions.test.js +58 -0
- package/__tests__/commands.test.js +28 -0
- package/__tests__/e2e.test.js +40 -0
- package/__tests__/fetcher-tree.test.js +55 -0
- package/__tests__/fetcher.test.js +122 -0
- package/__tests__/install-ambiguous.test.js +14 -0
- package/__tests__/install-command-multi.test.js +37 -0
- package/__tests__/install-command.test.js +40 -0
- package/__tests__/install-extra.test.js +50 -0
- package/__tests__/install-multiple-names.test.js +35 -0
- package/__tests__/installer-multi.test.js +22 -0
- package/__tests__/installer-raw-and-resolve.test.js +62 -0
- package/__tests__/installer-user.test.js +19 -0
- package/__tests__/installer.test.js +14 -0
- package/__tests__/list-format.test.js +31 -0
- package/__tests__/list-items-conflict.test.js +15 -0
- package/__tests__/list-json.test.js +35 -0
- package/__tests__/search-index.test.js +14 -0
- package/__tests__/search-json.test.js +34 -0
- package/__tests__/uninstall-prefixed.test.js +52 -0
- package/__tests__/uninstall.test.js +21 -0
- package/bin/acp-vscode.js +42 -0
- package/jest.config.cjs +3 -0
- package/package.json +44 -0
- package/src/cache.js +19 -0
- package/src/commands/completion.js +14 -0
- package/src/commands/install.js +278 -0
- package/src/commands/list.js +145 -0
- package/src/commands/search.js +84 -0
- package/src/commands/uninstall.js +55 -0
- package/src/fetcher.js +190 -0
- package/src/installer.js +158 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- name: Use Node.js
|
|
15
|
+
uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: '20'
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: npm ci
|
|
20
|
+
- name: Run tests
|
|
21
|
+
run: npm test
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Best practices:
|
|
4
|
+
# - Bump the package version locally with `npm version <major|minor|patch>` which creates a git tag.
|
|
5
|
+
# Then push the tag: `git push --follow-tags` (or `git push origin vX.Y.Z`).
|
|
6
|
+
# - Alternatively, use a release manager like `semantic-release` to automate versioning and changelogs.
|
|
7
|
+
# - This workflow runs when a tag matching `v*.*.*` is pushed. Ensure you create/tag using the v-prefixed semver format.
|
|
8
|
+
# - Secrets required:
|
|
9
|
+
# - `NPM_TOKEN` (used for npm publish)
|
|
10
|
+
# - The default `GITHUB_TOKEN` is provided automatically by Actions for creating releases.
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
tags:
|
|
15
|
+
- 'v*.*.*'
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
publish:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
# Minimal permissions required for publishing to npm and creating a release
|
|
21
|
+
permissions:
|
|
22
|
+
contents: write # needed to create a release
|
|
23
|
+
packages: write # not strictly required for npm but useful if you use GitHub Packages
|
|
24
|
+
steps:
|
|
25
|
+
# Fetch full history so tags and changelog generation work correctly
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
- name: Use Node.js
|
|
30
|
+
uses: actions/setup-node@v4
|
|
31
|
+
with:
|
|
32
|
+
node-version: '20'
|
|
33
|
+
registry-url: 'https://registry.npmjs.org'
|
|
34
|
+
- name: Install
|
|
35
|
+
run: npm ci
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: npm test
|
|
38
|
+
- name: Publish to npm
|
|
39
|
+
env:
|
|
40
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
41
|
+
run: npm publish
|
|
42
|
+
- name: Create GitHub Release
|
|
43
|
+
# Create a GitHub Release for the pushed tag. Uses the git ref by default.
|
|
44
|
+
uses: ncipollo/release-action@v1
|
|
45
|
+
with:
|
|
46
|
+
# Generate release notes automatically based on merged PRs and commits
|
|
47
|
+
generateReleaseNotes: true
|
|
48
|
+
# Use the tag from the push event (no need to pass 'tag')
|
|
49
|
+
env:
|
|
50
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
## Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for contributing! This file explains how to run tests and the CLI locally.
|
|
4
|
+
|
|
5
|
+
Development setup
|
|
6
|
+
-----------------
|
|
7
|
+
|
|
8
|
+
- Install dependencies:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- Run the CLI locally (help):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
node ./bin/acp-vscode.js --help
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Running tests
|
|
21
|
+
-------------
|
|
22
|
+
|
|
23
|
+
- Run the test suite with Jest:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm test
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- Tests use `ACP_INDEX_JSON` and other environment variables in some cases. To run tests or manual checks offline, generate a JSON index and set `ACP_INDEX_JSON` before running the command:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
export ACP_INDEX_JSON='{"prompts":[],"chatmodes":[],"instructions":[]}'
|
|
33
|
+
npm test
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Key notes for contributors
|
|
37
|
+
--------------------------
|
|
38
|
+
- The fetcher writes a small on-disk cache to `./.acp-cache/index.json`. Tests may create or read this file. If you see stale results during development, remove the directory:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
rm -rf .acp-cache
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- The CLI supports `ACP_REPOS_JSON` to override the upstream repo list when fetching.
|
|
45
|
+
|
|
46
|
+
- Keep changes small and unit-tested. There are unit and integration tests under `__tests__/`.
|
|
47
|
+
|
|
48
|
+
Submitting a PR
|
|
49
|
+
----------------
|
|
50
|
+
|
|
51
|
+
- Open a pull request against `main` with an explanatory title and tests for behavior changes.
|
|
52
|
+
- The repository is configured to run tests on PRs. Ensure tests pass locally before opening the PR.
|
|
53
|
+
|
|
54
|
+
Contact
|
|
55
|
+
-------
|
|
56
|
+
|
|
57
|
+
If you need help, open an issue on the repository or mention the maintainers in your PR.
|
package/PRD.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Product Requirements Document: acp-vscode
|
|
2
|
+
|
|
3
|
+
Goal: create an npm CLI (acp-vscode) to fetch prompts, chatmodes, and instructions from https://github.com/github/awesome-copilot and install them into VS Code workspace or user profile.
|
|
4
|
+
|
|
5
|
+
Requirements:
|
|
6
|
+
- fetch resources from the GitHub repo and cache index for 30 minutes (in-memory and on-disk)
|
|
7
|
+
- install into workspace (`.github/chatmodes`, `.github/prompts`) or VS Code user profile (prompts folder under the VS Code User directory, or `.github/*` under VS Code User for chatmodes/instructions)
|
|
8
|
+
- list available items and filter by type
|
|
9
|
+
- search items using cached index
|
|
10
|
+
- install single or multiple instruction files by name
|
|
11
|
+
- include help, tests, CI (GitHub Actions), README, and publish-ready `package.json`
|
|
12
|
+
|
|
13
|
+
Additional Notes:
|
|
14
|
+
- Provide `--dry-run` mode on install to preview files without writing
|
|
15
|
+
- Add release workflow that publishes to npm when a semantic tag (vMAJOR.MINOR.PATCH) is pushed; requires the `NPM_TOKEN` secret
|
|
16
|
+
|
|
17
|
+
- Add an `uninstall` command to remove installed items from workspace or user profile
|
|
18
|
+
- Require explicit confirmation for user-targeted uninstall or allow `--yes` to bypass confirmation
|
|
19
|
+
|
|
20
|
+
Behavior and implementation details
|
|
21
|
+
|
|
22
|
+
The PRD should describe high-level goals and acceptance criteria. For concrete usage, examples, and troubleshooting steps (cache removal, env var usage, completion examples), see the `README.md` which contains command examples and diagnostic tips.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Non-goals: automatic PRs, editor integrations (this is CLI-only)
|
package/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# acp-vscode
|
|
2
|
+
|
|
3
|
+
acp-vscode is a small CLI to fetch and install chatmodes, prompts and instructions from the GitHub "awesome-copilot" repository into your VS Code workspace or VS Code User profile.
|
|
4
|
+
|
|
5
|
+
Install (when published):
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g acp-vscode
|
|
9
|
+
# or run locally
|
|
10
|
+
node ./bin/acp-vscode.js --help
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Commands:
|
|
14
|
+
- install <workspace|user> [names...]
|
|
15
|
+
- target: `workspace` or `user`
|
|
16
|
+
- names: optional list of ids or names to install (supports `repo:id` form)
|
|
17
|
+
- type: specify with the option `--type <type>` (prompts|chatmodes|instructions|all). For backwards compatibility you can still pass the type as the first positional name (e.g. `install workspace prompts p1 p2`). Note: the `install` command also accepts a deliberate typo alias `--referesh` (alias for `--refresh`) to preserve historical behavior.
|
|
18
|
+
- list [type]
|
|
19
|
+
- list items available. type can be `prompts`, `chatmodes`, `instructions`, or `all`
|
|
20
|
+
- search <query>
|
|
21
|
+
- search across items
|
|
22
|
+
- uninstall <workspace|user> <type> [names...]
|
|
23
|
+
- remove installed files from workspace or user profile; use `--yes` to skip confirmation when targeting `user`.
|
|
24
|
+
- completion [shell]
|
|
25
|
+
- print a simple shell completion script for `bash` or `zsh` (default `bash`)
|
|
26
|
+
|
|
27
|
+
Output formats
|
|
28
|
+
---------------
|
|
29
|
+
|
|
30
|
+
Both `list` and `search` support a machine-readable JSON output via the `--json` (or `-j`) flag. When provided the commands will emit an array of items (objects with `type`, `id`, and `name`) instead of the human-friendly table.
|
|
31
|
+
|
|
32
|
+
Example (JSON):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
acp-vscode list prompts --json
|
|
36
|
+
acp-vscode search "find me" --json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
|
|
41
|
+
Install all prompts to workspace:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
acp-vscode install workspace prompts
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Install specific instructions to user profile (preferred):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
acp-vscode install user --type instructions "Instruction Name"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or (legacy positional type):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
acp-vscode install user instructions "Instruction Name"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Completion examples
|
|
60
|
+
-------------------
|
|
61
|
+
|
|
62
|
+
Print a `bash` completion helper and save it for interactive use:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
acp-vscode completion bash > /etc/bash_completion.d/acp-vscode
|
|
66
|
+
# or source it in your shell for testing
|
|
67
|
+
acp-vscode completion bash | source /dev/stdin
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For `zsh` add the script snippet to your `.zshrc` (the command prints a small helper function):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
acp-vscode completion zsh >> ~/.zshrc
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Uninstall examples
|
|
77
|
+
------------------
|
|
78
|
+
|
|
79
|
+
Remove two prompts from workspace:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
acp-vscode uninstall workspace prompts one two
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Remove a prompt from the user profile without confirmation:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
acp-vscode uninstall user prompts my-prompt --yes
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Troubleshooting
|
|
92
|
+
---------------
|
|
93
|
+
|
|
94
|
+
- Cache and stale index
|
|
95
|
+
- The CLI writes a disk cache at `./.acp-cache/index.json` (30 minute TTL). If you see stale results or want to force a fresh fetch, remove the cache file and retry:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
rm -rf .acp-cache
|
|
99
|
+
acp-vscode list --refresh
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- Offline testing / injecting a local index
|
|
103
|
+
- For tests or offline usage you can set `ACP_INDEX_JSON` to a JSON string representing the index. This bypasses network fetching entirely and the CLI will use the provided index verbatim.
|
|
104
|
+
|
|
105
|
+
- Multiple upstream repos
|
|
106
|
+
- To index multiple repos set `ACP_REPOS_JSON` to a JSON array of repo descriptors. Example:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
[ { "id": "r1", "treeUrl": "https://api.github.com/repos/org/repo1/git/trees/main?recursive=1", "rawBase": "https://raw.githubusercontent.com/org/repo1/main" } ]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- Verbose logging
|
|
113
|
+
- Add `--verbose` to commands to see extra diagnostic messages during fetch, cache clearing, and install/uninstall operations.
|
|
114
|
+
|
|
115
|
+
Cache: the CLI caches the fetched GitHub index in-memory and on-disk for 30 minutes to reduce network calls. The on-disk cache is stored under the current working directory in `.acp-cache/index.json`.
|
|
116
|
+
|
|
117
|
+
Configuration (environment variables)
|
|
118
|
+
|
|
119
|
+
Environment variables
|
|
120
|
+
|
|
121
|
+
ACP_INDEX_JSON
|
|
122
|
+
|
|
123
|
+
You can inject a full, pre-built index via the `ACP_INDEX_JSON` environment variable. This should be a JSON string representing the index shape the fetcher returns, for example:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"prompts": [{ "id": "p1", "name": "Prompt 1", "repo": "r1", "url": "https://..." }],
|
|
128
|
+
"chatmodes": [],
|
|
129
|
+
"instructions": []
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
This is useful for tests or offline runs. When present, the fetcher will parse and return this value verbatim.
|
|
134
|
+
|
|
135
|
+
ACP_REPOS_JSON
|
|
136
|
+
|
|
137
|
+
To support multiple upstream repos, set `ACP_REPOS_JSON` to a JSON array describing the repositories to index. Each repo object should contain at least an `id` and a `treeUrl`. Optionally include `rawBase` (the base URL to fetch raw file contents).
|
|
138
|
+
|
|
139
|
+
Example:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
[
|
|
143
|
+
{ "id": "r1", "treeUrl": "https://api.github.com/repos/org/repo1/git/trees/main?recursive=1", "rawBase": "https://raw.githubusercontent.com/org/repo1/main" },
|
|
144
|
+
{ "id": "r2", "treeUrl": "https://api.github.com/repos/org/repo2/git/trees/main?recursive=1", "rawBase": "https://raw.githubusercontent.com/org/repo2/main" }
|
|
145
|
+
]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
When multiple repos contain files with the same `id`, the fetcher adds an `_conflicts` array to the returned index listing conflicted ids. Consumers will display items as `repo:id` when necessary to disambiguate.
|
|
149
|
+
|
|
150
|
+
Dry-run:
|
|
151
|
+
|
|
152
|
+
You can preview what would be installed without writing files using --dry-run:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
acp-vscode install workspace prompts --dry-run
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Other notes
|
|
159
|
+
|
|
160
|
+
Global flags
|
|
161
|
+
|
|
162
|
+
- `--verbose` enables extra logging across commands.
|
|
163
|
+
- `--refresh` is a global top-level flag but currently only applied by the `list` and `search` commands to force clearing in-memory and on-disk caches. The `install` command accepts a `--referesh` alias (typo preserved) which also triggers cache clearing when provided to `install`.
|
|
164
|
+
|
|
165
|
+
Commands reference
|
|
166
|
+
------------------
|
|
167
|
+
|
|
168
|
+
Short reference for each command, key options, and quick examples.
|
|
169
|
+
|
|
170
|
+
- install <workspace|user> [names...]
|
|
171
|
+
- Description: Install prompts/chatmodes/instructions into a workspace or VS Code user profile.
|
|
172
|
+
- Options: `-t, --type <type>` (prompts|chatmodes|instructions|all), `--dry-run`, `--referesh` (alias for refresh), `--verbose`
|
|
173
|
+
- Examples:
|
|
174
|
+
- Install all prompts into the current workspace:
|
|
175
|
+
- `acp-vscode install workspace prompts`
|
|
176
|
+
- Install instruction by name into user profile (preferred):
|
|
177
|
+
- `acp-vscode install user --type instructions "Instruction Name"`
|
|
178
|
+
|
|
179
|
+
- list [type]
|
|
180
|
+
- Description: List available items. Type can be `prompts`, `chatmodes`, `instructions`, or `all` (default).
|
|
181
|
+
- Options: `-r, --refresh` (clear caches and refetch), `-j, --json`, `--verbose`
|
|
182
|
+
- Examples:
|
|
183
|
+
- `acp-vscode list chatmodes`
|
|
184
|
+
- `acp-vscode list --json`
|
|
185
|
+
|
|
186
|
+
- search <query>
|
|
187
|
+
- Description: Search the index for matching items (name, id or content).
|
|
188
|
+
- Options: `-r, --refresh`, `-j, --json`, `--verbose`
|
|
189
|
+
- Examples:
|
|
190
|
+
- `acp-vscode search "temperature"`
|
|
191
|
+
|
|
192
|
+
- uninstall <workspace|user> <type> [names...]
|
|
193
|
+
- Description: Remove installed files from workspace or user profile. When targeting `user` you'll be prompted for confirmation unless you pass `--yes`.
|
|
194
|
+
- Options: `--yes`, `--verbose`
|
|
195
|
+
- Examples:
|
|
196
|
+
- `acp-vscode uninstall workspace prompts one two`
|
|
197
|
+
|
|
198
|
+
- completion [shell]
|
|
199
|
+
- Description: Print a small shell completion snippet for `bash` or `zsh`.
|
|
200
|
+
- Examples:
|
|
201
|
+
- `acp-vscode completion bash`
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
Publishing:
|
|
205
|
+
|
|
206
|
+
This repository includes a release workflow that publishes to npm when a tag like v0.1.0 is pushed. You must add an `NPM_TOKEN` secret in the repository settings for the workflow to authenticate with npm.
|
|
207
|
+
|
|
208
|
+
Publish checklist:
|
|
209
|
+
|
|
210
|
+
1. Update `package.json` fields: `version`, `repository.url`, `bugs.url`, `author`.
|
|
211
|
+
2. Create a repo secret `NPM_TOKEN` in GitHub (Settings → Secrets → Actions). Generate the token with npm's access token UI.
|
|
212
|
+
3. Create a release tag and push it, e.g.:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
git tag v0.1.0
|
|
216
|
+
git push origin v0.1.0
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
4. The release workflow will run tests and publish the package to npm on success.
|
|
220
|
+
|
|
221
|
+
Uninstall and confirmation:
|
|
222
|
+
|
|
223
|
+
To remove installed files:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
acp-vscode uninstall workspace prompts one two
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
If you're uninstalling from the VS Code user profile, the CLI will prompt for confirmation. Use `--yes` to skip the prompt:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
acp-vscode uninstall user prompts one --yes
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const cache = require('../src/cache');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function makeCli() {
|
|
6
|
+
const cli = {
|
|
7
|
+
_action: null,
|
|
8
|
+
command() { return cli; },
|
|
9
|
+
option() { return cli; },
|
|
10
|
+
action(fn) { cli._action = fn; return cli; }
|
|
11
|
+
};
|
|
12
|
+
return cli;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('command actions (search, list, install)', () => {
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
cache.del('index');
|
|
18
|
+
delete process.env.ACP_INDEX_JSON;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('searchCommand action prints matches and respects ACP_INDEX_JSON', async () => {
|
|
22
|
+
process.env.ACP_INDEX_JSON = JSON.stringify({ prompts: [{ id: 's1', name: 'SearchMe', repo: 'r1' }], chatmodes: [], instructions: [], _conflicts: [] });
|
|
23
|
+
const stubCli = makeCli();
|
|
24
|
+
const sc = require('../src/commands/search');
|
|
25
|
+
sc.searchCommand(stubCli);
|
|
26
|
+
const logs = [];
|
|
27
|
+
jest.spyOn(console, 'log').mockImplementation((...args) => logs.push(args.join(' ')));
|
|
28
|
+
await stubCli._action('searchme', { refresh: true, verbose: false });
|
|
29
|
+
expect(logs.some(l => l.includes('s1'))).toBe(true);
|
|
30
|
+
console.log.mockRestore();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('listCommand action prints header and items', async () => {
|
|
34
|
+
process.env.ACP_INDEX_JSON = JSON.stringify({ prompts: [{ id: 'p1', name: 'Prompt One', repo: 'r1' }], chatmodes: [], instructions: [], _conflicts: [] });
|
|
35
|
+
const stubCli = makeCli();
|
|
36
|
+
const lc = require('../src/commands/list');
|
|
37
|
+
lc.listCommand(stubCli);
|
|
38
|
+
const logs = [];
|
|
39
|
+
jest.spyOn(console, 'log').mockImplementation((...args) => logs.push(args.join(' ')));
|
|
40
|
+
await stubCli._action('prompts', { refresh: true, verbose: false });
|
|
41
|
+
// The printed output should include a header line with Type and ID
|
|
42
|
+
expect(logs.some(l => /Type\s+ID/.test(l))).toBe(true);
|
|
43
|
+
console.log.mockRestore();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('installCommand action supports dry-run and type option', async () => {
|
|
47
|
+
process.env.ACP_INDEX_JSON = JSON.stringify({ prompts: [{ id: 'p1', name: 'P1', repo: 'r1', content: 'x' }], chatmodes: [], instructions: [], _conflicts: [] });
|
|
48
|
+
const stubCli = makeCli();
|
|
49
|
+
const ic = require('../src/commands/install');
|
|
50
|
+
ic.installCommand(stubCli);
|
|
51
|
+
const logs = [];
|
|
52
|
+
jest.spyOn(console, 'log').mockImplementation((...args) => logs.push(args.join(' ')));
|
|
53
|
+
await stubCli._action('workspace', ['p1'], { type: 'prompts', 'dry-run': true });
|
|
54
|
+
expect(logs.some(l => l.includes('[dry-run]'))).toBe(true);
|
|
55
|
+
console.log.mockRestore();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
jest.mock('../src/fetcher');
|
|
2
|
+
const { fetchIndex } = require('../src/fetcher');
|
|
3
|
+
const cache = require('../src/cache');
|
|
4
|
+
const { listItems } = require('../src/commands/list');
|
|
5
|
+
const { searchIndex } = require('../src/commands/search');
|
|
6
|
+
|
|
7
|
+
const FIXTURE_INDEX = {
|
|
8
|
+
prompts: [{ id: 'p1', name: 'Prompt One' }],
|
|
9
|
+
chatmodes: [{ id: 'c1', name: 'Chat Mode One' }],
|
|
10
|
+
instructions: [{ id: 'i1', name: 'Instruction One' }]
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
fetchIndex.mockResolvedValue(FIXTURE_INDEX);
|
|
15
|
+
cache.del('index');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('listItems returns items', async () => {
|
|
19
|
+
const items = listItems(FIXTURE_INDEX);
|
|
20
|
+
expect(items.find(i => i.type === 'prompt')).toBeTruthy();
|
|
21
|
+
expect(items.find(i => i.type === 'chatmode')).toBeTruthy();
|
|
22
|
+
expect(items.find(i => i.type === 'instruction')).toBeTruthy();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('searchIndex finds query', async () => {
|
|
26
|
+
const results = searchIndex(FIXTURE_INDEX, 'One');
|
|
27
|
+
expect(results.length).toBeGreaterThanOrEqual(3);
|
|
28
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const { execFileSync } = require('child_process');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
const CLI = path.join(__dirname, '..', 'bin', 'acp-vscode.js');
|
|
7
|
+
|
|
8
|
+
describe('e2e CLI', () => {
|
|
9
|
+
const tmp = path.join(os.tmpdir(), `acp-e2e-${Date.now()}`);
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
await fs.ensureDir(tmp);
|
|
12
|
+
});
|
|
13
|
+
afterAll(async () => {
|
|
14
|
+
await fs.remove(tmp).catch(() => {});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('install workspace prompts writes only to workspace and cleans up', () => {
|
|
18
|
+
const index = { prompts: [{ id: 'p1', name: 'p1', content: { body: 'ok' } }] };
|
|
19
|
+
const env = Object.assign({}, process.env, { ACP_INDEX_JSON: JSON.stringify(index) });
|
|
20
|
+
// run install pointing at tmp as cwd
|
|
21
|
+
execFileSync('node', [CLI, 'install', 'workspace', 'prompts'], { cwd: tmp, env });
|
|
22
|
+
const installedDir = path.join(tmp, '.github', 'prompts');
|
|
23
|
+
expect(fs.existsSync(installedDir)).toBe(true);
|
|
24
|
+
const files = fs.readdirSync(installedDir);
|
|
25
|
+
expect(files.length).toBeGreaterThan(0);
|
|
26
|
+
// ensure VS Code user profile was not touched (we won't check actual user dir), just ensure cwd-based user didn't receive files
|
|
27
|
+
const userPrompts = path.join(tmp, 'prompts');
|
|
28
|
+
expect(fs.existsSync(userPrompts)).toBe(false);
|
|
29
|
+
// cleanup
|
|
30
|
+
fs.removeSync(installedDir);
|
|
31
|
+
expect(fs.existsSync(installedDir)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('unknown option shows friendly error', () => {
|
|
35
|
+
const { spawnSync } = require('child_process');
|
|
36
|
+
const res = spawnSync('node', [CLI, '--no-such-option'], { encoding: 'utf8' });
|
|
37
|
+
const out = `${res.stdout || ''}${res.stderr || ''}${res.error ? res.error.message : ''}`;
|
|
38
|
+
expect(out).toMatch(/Run `acp-vscode --help`/);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const axios = require('axios');
|
|
5
|
+
jest.mock('axios');
|
|
6
|
+
|
|
7
|
+
let fetcher;
|
|
8
|
+
let fetchIndex;
|
|
9
|
+
let diskPaths;
|
|
10
|
+
|
|
11
|
+
describe('fetcher tree -> index conversion', () => {
|
|
12
|
+
const tmp = path.join(os.tmpdir(), `acp-fetcher-tree-${Date.now()}`);
|
|
13
|
+
const origCwd = process.cwd();
|
|
14
|
+
|
|
15
|
+
beforeAll(async () => {
|
|
16
|
+
await fs.ensureDir(tmp);
|
|
17
|
+
process.chdir(tmp);
|
|
18
|
+
// require after chdir so diskPaths uses tmp cwd
|
|
19
|
+
fetcher = require('../src/fetcher');
|
|
20
|
+
fetchIndex = fetcher.fetchIndex;
|
|
21
|
+
diskPaths = fetcher.diskPaths;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
process.chdir(origCwd);
|
|
26
|
+
await fs.remove(tmp);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
beforeEach(async () => {
|
|
30
|
+
const { DISK_CACHE_DIR } = diskPaths();
|
|
31
|
+
await fs.remove(DISK_CACHE_DIR).catch(() => {});
|
|
32
|
+
axios.get.mockReset();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('constructs index from git tree', async () => {
|
|
36
|
+
const tree = [
|
|
37
|
+
{ path: 'prompts/p1.prompt.md', type: 'blob' },
|
|
38
|
+
{ path: 'chatmodes/c1.chatmode.md', type: 'blob' },
|
|
39
|
+
{ path: 'instructions/i1.instructions.md', type: 'blob' }
|
|
40
|
+
];
|
|
41
|
+
axios.get.mockResolvedValue({ status: 200, data: { tree } });
|
|
42
|
+
|
|
43
|
+
const idx = await fetchIndex();
|
|
44
|
+
|
|
45
|
+
expect(idx).toBeTruthy();
|
|
46
|
+
expect(Array.isArray(idx.prompts)).toBe(true);
|
|
47
|
+
expect(Array.isArray(idx.chatmodes)).toBe(true);
|
|
48
|
+
expect(Array.isArray(idx.instructions)).toBe(true);
|
|
49
|
+
|
|
50
|
+
expect(idx.prompts[0].path).toBe('prompts/p1.prompt.md');
|
|
51
|
+
expect(idx.prompts[0].url).toBe('https://raw.githubusercontent.com/github/awesome-copilot/main/prompts/p1.prompt.md');
|
|
52
|
+
expect(idx.chatmodes[0].path).toBe('chatmodes/c1.chatmode.md');
|
|
53
|
+
expect(idx.instructions[0].path).toBe('instructions/i1.instructions.md');
|
|
54
|
+
});
|
|
55
|
+
});
|