Skip to content

Commit fcbbde5

Browse files
committed
improve copilot agent setup: instructions, prompts, and ci environment
1 parent 2d772e9 commit fcbbde5

File tree

7 files changed

+1202
-2
lines changed

7 files changed

+1202
-2
lines changed

.github/copilot-instructions.md

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ applyTo: "**/*.ts,**/*.tsx,**/package.json,**/*.cmake,**/CMakeLists.txt,**/CMake
1818

1919
## Project conventions
2020

21-
- **Path alias**: `@cmt/*` maps to `src/*` (see `tsconfig.json`). Always use `import foo from '@cmt/foo'` — never relative paths from outside `src/`.
22-
- **Error reporting**: Use `rollbar.invokeAsync()` / `rollbar.invoke()` for top-level error boundaries around event handlers, never bare `try/catch` that silently swallows.
21+
- **Path alias**: `@cmt/*` maps to `src/*` (see `tsconfig.json`). Always use `import foo from '@cmt/foo'` — never relative paths from outside `src/`. `@test/*` maps to `test/*` (defined in both `tsconfig.json` and `test.tsconfig.json`). Use `import ... from '@test/...'` in test files.
22+
- **Error reporting**: Use `rollbar` wrappers for top-level error boundaries around event handlers, never bare `try/catch` that silently swallows.
23+
- `rollbar.invoke(what, fn)` — wraps synchronous code; catches, logs, and re-throws.
24+
- `rollbar.invokeAsync(what, fn)` — wraps async function execution; catches promise rejections.
25+
- `rollbar.takePromise(what, additional, thenable)` — attaches an error handler to an **already-created** Thenable (e.g., a return value from a VS Code API call). Use this when you have a promise but didn't create it via `invokeAsync`.
2326
- **Telemetry**: Use helpers in `src/telemetry.ts` (`logEvent`). Never call the VS Code telemetry API directly.
2427

2528
## Architecture
@@ -58,14 +61,41 @@ Identify the affected layer(s) from the architecture table above. Read the relev
5861
| Cache entries | `CMakeDriver.cmakeCacheEntries` |
5962
| Extension settings | `ConfigurationReader` (`src/config.ts`) — never `vscode.workspace.getConfiguration()` directly |
6063

64+
### Reactive configuration subscriptions
65+
66+
Use `ConfigurationReader.onChange()` for typed, per-setting change notifications:
67+
68+
```typescript
69+
const disposable = config.onChange('settingName', (newValue) => {
70+
// Respond to change
71+
});
72+
// Store disposable in a field or push to a disposables array for cleanup
73+
```
74+
75+
Do **not** use `vscode.workspace.onDidChangeConfiguration` directly — the `onChange()` API is type-safe, fires only for the specific setting, and integrates with the extension's promise tracking.
76+
6177
### Always handle both operating modes
6278

6379
When a code path touches shared logic (configure, build, test, targets, environment), check `CMakeProject.useCMakePresets` and ensure it works correctly in both presets mode and kits/variants mode. Omitting the check for one mode in shared code is a bug waiting to happen. Features that are inherently mode-specific (e.g., kit scanning, preset expansion) are fine to scope to one mode.
6480

81+
### `SpecialKits` sentinel values
82+
83+
Kit names may be sentinel values, not real compiler paths. Always check before treating `kit.name` as a file path:
84+
85+
- `SpecialKits.Unspecified` (`'__unspec__'`) — no kit selected
86+
- `SpecialKits.ScanForKits` (`'__scanforkits__'`) — triggers kit scanning
87+
- `SpecialKits.ScanSpecificDir` (`'__scan_specific_dir__'`) — scan a specific directory
88+
89+
### Kit trust model
90+
91+
Kits have an `isTrusted: boolean` property. Auto-scanned kits from untrusted paths are filtered out before selection and use. This prevents untrusted kits from executing `environmentSetupScript`. Do not bypass this trust filtering when adding kit-related code.
92+
6593
### Always handle both generator types
6694

6795
Single-config uses `CMAKE_BUILD_TYPE`; multi-config uses `--config` at build time. Check the active generator before any build-type logic.
6896

97+
Call `util.isMultiConfGeneratorFast(generator)` before any build-type logic. Multi-config generators include Visual Studio, Xcode, and Ninja Multi-Config. Note: the `cmake.setBuildTypeOnMultiConfig` setting can override multi-config behavior — check it before assuming.
98+
6999
### Localize all user-visible strings
70100

71101
Every file with user-visible text needs the `vscode-nls` boilerplate:
@@ -79,6 +109,14 @@ const localize: nls.LocalizeFunc = nls.loadMessageBundle();
79109
// ❌ bare strings in user-visible output
80110
```
81111

112+
### NLS key naming and `package.nls.json`
113+
114+
Settings: `cmake-tools.configuration.cmake.<settingName>.description` (or `.markdownDescription`)
115+
Commands: `cmake-tools.command.cmake.<commandName>.title`
116+
117+
- Update `package.nls.json` for English strings — **never** modify files under `i18n/` (translations are updated separately).
118+
- When a setting uses `markdownDescription` in `package.json`, the NLS key suffix must be `.markdownDescription`, not `.description`.
119+
82120
### Use the module-scoped logger — never `console.log`
83121

84122
```typescript
@@ -98,10 +136,33 @@ Never concatenate path strings with `/` or `\\`. No exceptions.
98136

99137
`package.json` (`contributes.configuration`), `src/config.ts` (`ConfigurationReader`), and `docs/cmake-settings.md`.
100138

139+
### Setting `scope` in `package.json`
140+
141+
Every setting in `contributes.configuration` must have an explicit `scope`:
142+
- `"resource"` — per-folder; use for anything CMake project-specific (paths, build args, generator, environment).
143+
- `"window"` — global; use for extension-wide UI/behavior settings.
144+
145+
Examples: `cmake.buildDirectory``"resource"` (project-specific path), `cmake.autoSelectActiveFolder``"window"` (global UI behavior).
146+
101147
### Every PR needs a CHANGELOG entry
102148

103149
One entry under the current version in `CHANGELOG.md`, in the appropriate section (`Features:`, `Improvements:`, or `Bug Fixes:`), describing user-visible behavior in the repo's existing tense and category style.
104150

151+
### Adding a new diagnostic parser
152+
153+
1. Create `src/diagnostics/<name>.ts` — extend `RawDiagnosticParser`, implement `handleLine()`
154+
2. Add an instance to the `Compilers` class in `src/diagnostics/build.ts`
155+
3. Add the parser name to `cmake.enabledOutputParsers` enum array **and** default array in `package.json`
156+
4. Add English strings to `package.nls.json` if descriptions change
157+
158+
Currently registered: gcc, gnuld, ghs, diab, msvc, iar, iwyu. Default-enabled: cmake, gcc, gnuld, msvc, ghs, diab.
159+
160+
### Copilot CI environment
161+
162+
In the Copilot agent CI environment, `.npmrc` is renamed to `.npmrc.bak` and `yarn.lock` registry URLs are patched to use the public npm registry. These are environment artifacts:
163+
- **Never commit** `.npmrc.bak` or the patched `yarn.lock`
164+
- If `git status` shows these as modified, run `git checkout -- .npmrc yarn.lock` before committing
165+
105166
## Testing checklist
106167

107168
- [ ] `yarn unitTests` passes
@@ -111,6 +172,20 @@ One entry under the current version in `CHANGELOG.md`, in the appropriate sectio
111172
- [ ] Behavior verified with **single-config** and **multi-config** generators
112173
- [ ] Windows/macOS/Linux differences considered (paths, env vars, MSVC toolchain, generator availability)
113174

175+
### Backend tests (`yarn backendTests`)
176+
177+
The fastest feedback loop — runs via Mocha with no VS Code instance or display required.
178+
179+
- **Files go in** `test/unit-tests/backend/<name>.test.ts`
180+
- **Framework:** Mocha TDD (`suite`/`test`), Chai `expect` assertions
181+
- **Run:** `yarn backendTests`
182+
- **Mock setup:** `test/unit-tests/backend/setup-vscode-mock.ts` provides minimal `vscode` stubs
183+
184+
**Import strategy:**
185+
1. If the module has **no transitive `vscode` dependency** (e.g., `encodingUtils`, `cmakeValue`) → import directly via `@cmt/*`
186+
2. If the module **transitively imports `vscode`** and the mock is insufficient → **mirror the pure function logic inline** in the test file. This is the established pattern in `expand.test.ts`, `targetMap.test.ts`, `shell-propagation.test.ts`.
187+
3. **Heuristic:** Try `@cmt/*` import first. If it fails due to vscode dependency at runtime, fall back to mirroring.
188+
114189
## Where to start
115190

116191
- **Configure/build/test behavior**`src/cmakeProject.ts` + `src/drivers/`
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Adding a New Command
2+
3+
Recipe for adding a new `cmake.*` command to CMake Tools.
4+
5+
## Files you must touch
6+
7+
| File | What to add |
8+
|------|-------------|
9+
| `package.json` | Command declaration in `contributes.commands` + optional menu entries |
10+
| `package.nls.json` | English title string |
11+
| `src/extension.ts` | Method name in `funs` array + handler method on `ExtensionManager` |
12+
| `CHANGELOG.md` | Entry under the current version |
13+
14+
---
15+
16+
## Step 1 — Declare the command in `package.json`
17+
18+
### 1a — `contributes.commands`
19+
20+
```jsonc
21+
// package.json → contributes.commands
22+
{
23+
"command": "cmake.myCommand",
24+
"title": "%cmake-tools.command.cmake.myCommand.title%",
25+
"category": "CMake"
26+
}
27+
```
28+
29+
### Rules
30+
31+
- **Command ID format:** `cmake.<commandName>` (camelCase).
32+
- **Title:** NLS key in the format `%cmake-tools.command.cmake.<commandName>.title%`.
33+
- **Category:** `"CMake"` — this prefixes the title in the Command Palette as `CMake: <title>`.
34+
- **`when`** (optional): controls when the command appears in the Command Palette.
35+
- **`icon`** (optional): Codicon reference like `"$(settings-gear)"` for tree-view inline buttons.
36+
37+
### 1b — `contributes.menus` (if needed)
38+
39+
Add visibility rules for where the command appears.
40+
41+
**Command Palette visibility:**
42+
43+
```jsonc
44+
// package.json → contributes.menus.commandPalette
45+
{
46+
"command": "cmake.myCommand",
47+
"when": "cmake:enableFullFeatureSet"
48+
}
49+
```
50+
51+
**Sidebar tree-view inline button:**
52+
53+
```jsonc
54+
// package.json → contributes.menus["view/item/context"]
55+
{
56+
"command": "cmake.projectStatus.myCommand",
57+
"when": "view == cmake.projectStatus && cmake:enableFullFeatureSet && viewItem == 'myItem'",
58+
"group": "inline"
59+
}
60+
```
61+
62+
Common `when` clause patterns:
63+
64+
| Pattern | Meaning |
65+
|---------|---------|
66+
| `cmake:enableFullFeatureSet` | Extension is fully activated |
67+
| `useCMakePresets` | Presets mode is active |
68+
| `!useCMakePresets` | Kits/variants mode is active |
69+
| `view == cmake.projectStatus && viewItem == 'kit'` | Specific tree-view item |
70+
| `viewItem =~ /configPreset/` | Regex match on tree-view item |
71+
72+
---
73+
74+
## Step 2 — Add the English string to `package.nls.json`
75+
76+
```json
77+
"cmake-tools.command.cmake.myCommand.title": "My Command Title"
78+
```
79+
80+
For titles containing the product name, use the object form with a translator comment:
81+
82+
```json
83+
"cmake-tools.command.cmake.myCommand.title": {
84+
"message": "Do Something with CMake Tools",
85+
"comment": ["The text 'CMake Tools' should not be localized."]
86+
}
87+
```
88+
89+
> **Do not** modify any file under `i18n/`.
90+
91+
---
92+
93+
## Step 3 — Register the command in `src/extension.ts`
94+
95+
Add the method name to the `funs` array (around line 2458). The `register()` helper
96+
auto-generates the command ID `cmake.<name>`, wraps it with debug logging, and hands
97+
the promise to `rollbar.takePromise()` for error tracking.
98+
99+
```typescript
100+
// src/extension.ts
101+
const funs: (keyof ExtensionManager)[] = [
102+
// ... existing entries ...
103+
'myCommand', // ← add here
104+
];
105+
```
106+
107+
That's it — no manual `registerCommand` call needed. The loop at line 2578 handles it:
108+
109+
```typescript
110+
for (const key of funs) {
111+
context.subscriptions.push(register(key));
112+
}
113+
```
114+
115+
> Only use manual `vscode.commands.registerCommand()` for commands that need
116+
> custom argument handling (e.g., tree-view context-menu commands that receive
117+
> a node argument). Most commands go through the `funs` array.
118+
119+
---
120+
121+
## Step 4 — Implement the handler on `ExtensionManager`
122+
123+
Add a method to the `ExtensionManager` class in `src/extension.ts`. The method
124+
name must match the string added to the `funs` array.
125+
126+
### Pattern A — Delegate to CMakeProject (most common)
127+
128+
```typescript
129+
myCommand(folder?: vscode.WorkspaceFolder) {
130+
telemetry.logEvent('myCommand');
131+
return this.runCMakeCommand(
132+
cmakeProject => cmakeProject.myCommand(),
133+
folder,
134+
undefined, // precheck (optional)
135+
true // cleanOutputChannel
136+
);
137+
}
138+
```
139+
140+
Then implement the actual logic on `CMakeProject` in `src/cmakeProject.ts`.
141+
142+
### Pattern B — Run for all projects
143+
144+
```typescript
145+
myCommandAll() {
146+
telemetry.logEvent('myCommand', { all: 'true' });
147+
return this.runCMakeCommandForAll(
148+
cmakeProject => cmakeProject.myCommand()
149+
);
150+
}
151+
```
152+
153+
### Pattern C — Direct implementation (no CMakeProject delegation)
154+
155+
```typescript
156+
async myCommand() {
157+
telemetry.logEvent('myCommand');
158+
const result = await vscode.window.showQuickPick(items);
159+
if (!result) {
160+
return;
161+
}
162+
// ... handle result ...
163+
}
164+
```
165+
166+
### Key helpers
167+
168+
| Helper | Use when |
169+
|--------|----------|
170+
| `this.runCMakeCommand(cmd, folder)` | Single-project command |
171+
| `this.runCMakeCommandForAll(cmd)` | Runs on every open CMake project |
172+
| `this.runCMakeCommandForProject(cmd, project)` | Specific project instance |
173+
174+
---
175+
176+
## Step 5 — Add a CHANGELOG entry
177+
178+
Add an entry under the current version in `CHANGELOG.md`, in the `Features:` section.
179+
180+
---
181+
182+
## Verification checklist
183+
184+
- [ ] `package.json` — command declared with NLS title and `"CMake"` category
185+
- [ ] `package.json` — menu entries added (if applicable) with correct `when` clauses
186+
- [ ] `package.nls.json` — English title string added
187+
- [ ] `src/extension.ts` — method name added to `funs` array
188+
- [ ] `src/extension.ts` — handler method implemented on `ExtensionManager`
189+
- [ ] Handler uses `telemetry.logEvent()` for telemetry
190+
- [ ] Handler delegates to `CMakeProject` via `runCMakeCommand` (if project-scoped)
191+
- [ ] `CHANGELOG.md` — entry added
192+
- [ ] `yarn compile` succeeds
193+
- [ ] No files under `i18n/` were modified
194+
195+
---
196+
197+
*See also: [`.github/copilot-instructions.md`](../copilot-instructions.md) for project-wide conventions.*

0 commit comments

Comments
 (0)