copilot-metrics 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/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/RELEASE.md +74 -0
- package/bin/copilot-metrics.js +16 -0
- package/package.json +48 -0
- package/scripts/manual-copilot-cli-flow.js +134 -0
- package/skills/copilot-metrics/SKILL.md +36 -0
- package/src/cli.js +288 -0
- package/src/hook-logger.js +79 -0
- package/src/ingest.js +71 -0
- package/src/jsonl.js +28 -0
- package/src/label-extractors.js +120 -0
- package/src/labels.js +56 -0
- package/src/otel.js +126 -0
- package/src/paths.js +51 -0
- package/src/pricing.js +66 -0
- package/src/reports.js +245 -0
- package/src/setup.js +196 -0
- package/src/sqlite-store.js +290 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - 2026-05-30
|
|
4
|
+
|
|
5
|
+
First local release candidate for `copilot-metrics`.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- CLI setup helpers for central local data directories, VS Code Copilot OTel settings, Copilot CLI OTel environment exports, and local/global hook config.
|
|
10
|
+
- Redacted hook logger that captures safe attribution metadata with content capture disabled by default.
|
|
11
|
+
- Local SQLite-backed import for VS Code Copilot OTel JSONL, Copilot CLI OTel JSONL, and Copilot CLI hook JSONL.
|
|
12
|
+
- LLM span normalization, root-agent double-count prevention, token extraction, model pricing estimates, AI Credit estimates, malformed-row warnings, and unknown-model warnings.
|
|
13
|
+
- Jira-style label extraction with evidence-preserving attribution by source, field, session, repo, branch, cwd, and confidence.
|
|
14
|
+
- Configurable custom label extractors loaded from local config without modifying package source.
|
|
15
|
+
- CLI reports for label overview, single-label summary/detail, models, repos/directories, and unattributed usage, with human and JSON output.
|
|
16
|
+
- Release smoke checks, package verification, GitHub Actions npm publishing workflow, MIT license, and release checklist.
|
|
17
|
+
|
|
18
|
+
### Notes
|
|
19
|
+
|
|
20
|
+
- Cost and AI Credit values are estimates only. GitHub billing remains the source of truth.
|
|
21
|
+
- Prompt/content capture is disabled by default.
|
|
22
|
+
- Official usage reconciliation, collector mode, richer privacy controls, and dashboard views are deferred.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 copilot-metrics 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,174 @@
|
|
|
1
|
+
# Copilot Metrics
|
|
2
|
+
|
|
3
|
+
`copilot-metrics` is a local-first CLI for estimating GitHub Copilot usage from local OpenTelemetry and hook metadata. It helps answer which Jira-style labels, repos, models, and Copilot surfaces are driving estimated AI Credit usage.
|
|
4
|
+
|
|
5
|
+
Costs are estimates, not official billing records. GitHub billing remains the source of truth.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
From npm:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx copilot-metrics@0.1.0 --help
|
|
13
|
+
npx copilot-metrics@0.1.0 init
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
From this checkout:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm ci
|
|
20
|
+
npm test
|
|
21
|
+
npm run cli -- --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Data Directory
|
|
25
|
+
|
|
26
|
+
By default, all metadata is stored in a user-level local folder:
|
|
27
|
+
|
|
28
|
+
- Linux: `$XDG_DATA_HOME/copilot-metrics` or `~/.local/share/copilot-metrics`
|
|
29
|
+
- macOS: `~/Library/Application Support/copilot-metrics`
|
|
30
|
+
- Windows: `%LOCALAPPDATA%\\copilot-metrics`
|
|
31
|
+
|
|
32
|
+
Override it with:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
export COPILOT_METRICS_HOME=/path/to/copilot-metrics-data
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Useful commands:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx copilot-metrics@0.1.0 init
|
|
42
|
+
npx copilot-metrics@0.1.0 paths --json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Configure Telemetry
|
|
46
|
+
|
|
47
|
+
Print VS Code Insiders Copilot Chat OpenTelemetry settings:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx copilot-metrics@0.1.0 setup vscode
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Print Copilot CLI OpenTelemetry environment exports:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npx copilot-metrics@0.1.0 setup copilot-cli
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Content capture is disabled by default. Do not enable richer prompt capture unless you explicitly accept the privacy tradeoff.
|
|
60
|
+
|
|
61
|
+
## Configure Hooks
|
|
62
|
+
|
|
63
|
+
Preview repo-local hook config. The default `--surface both` emits the Copilot CLI lower camel case hook format:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx copilot-metrics@0.1.0 hooks preview --scope local --surface both
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Install repo-local or user-global hook config:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx copilot-metrics@0.1.0 hooks install --scope local --surface both
|
|
73
|
+
npx copilot-metrics@0.1.0 hooks install --scope global --surface both
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Local install writes `.github/hooks/copilot-metrics.json`. Global install updates `~/.copilot/settings.json` idempotently, replacing prior `copilot-metrics` hook entries while preserving other settings and hooks. Use `--surface vscode` for VS Code-only PascalCase events or `--surface copilot-cli` for CLI-native lower camel case events. The hook logger writes redacted JSONL metadata to the central data directory. It extracts Jira-style labels such as `DEMO-12345` from safe metadata and does not store full prompt text by default.
|
|
77
|
+
|
|
78
|
+
## Import Telemetry
|
|
79
|
+
|
|
80
|
+
Initialize the local SQLite store and import JSONL files:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx copilot-metrics@0.1.0 store init
|
|
84
|
+
npx copilot-metrics@0.1.0 import --source vscode --file ~/.local/share/copilot-metrics/telemetry/vscode-copilot-otel.jsonl
|
|
85
|
+
npx copilot-metrics@0.1.0 import --source copilot-cli --file ~/.local/share/copilot-metrics/telemetry/copilot-cli-otel.jsonl
|
|
86
|
+
npx copilot-metrics@0.1.0 import --source hooks --file ~/.local/share/copilot-metrics/hooks/copilot-hooks.jsonl
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Imports persist raw records, normalized LLM usage records, hook events, label evidence, and import warnings.
|
|
90
|
+
|
|
91
|
+
## Reports
|
|
92
|
+
|
|
93
|
+
Run local reports from the SQLite store:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx copilot-metrics@0.1.0 report labels
|
|
97
|
+
npx copilot-metrics@0.1.0 report label DEMO-12345
|
|
98
|
+
npx copilot-metrics@0.1.0 report label DEMO-12345 --detail
|
|
99
|
+
npx copilot-metrics@0.1.0 report models
|
|
100
|
+
npx copilot-metrics@0.1.0 report repos
|
|
101
|
+
npx copilot-metrics@0.1.0 report unattributed
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Every report supports `--json`:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npx copilot-metrics@0.1.0 report labels --json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Attribution Model
|
|
111
|
+
|
|
112
|
+
The default extractor finds Jira-style labels such as `DEMO-12345` from safe metadata including hook labels, branch names, cwd/path values, repo metadata, and task hints.
|
|
113
|
+
|
|
114
|
+
Attribution is stored as evidence with source, field, session, repo, branch, cwd, confidence, and related usage or hook record IDs. This makes the data useful for later analysis, such as deciding whether a label was the main task or a sidetrack.
|
|
115
|
+
|
|
116
|
+
Full prompt content is not stored by default. Prompt-like fields are only used to extract labels and the stored source value is reduced to the matched label.
|
|
117
|
+
|
|
118
|
+
## Custom Label Extractors
|
|
119
|
+
|
|
120
|
+
Custom extractors are configured in the local `config.json`; you do not modify package source.
|
|
121
|
+
|
|
122
|
+
After `copilot-metrics init`, add a module path:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"labelExtractors": ["/absolute/path/to/my-extractor.cjs"]
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Relative paths are resolved from the current working directory when the CLI runs.
|
|
131
|
+
|
|
132
|
+
The module should export a function, or an object with `extractLabels`. Each extractor receives:
|
|
133
|
+
|
|
134
|
+
- `sourceType`: for example `usage` or `hook`
|
|
135
|
+
- `sourceData`: safe metadata for that source
|
|
136
|
+
|
|
137
|
+
It returns zero or more labels, either as strings or evidence objects:
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
const extractor = (sourceType, sourceData) => {
|
|
141
|
+
if (sourceData.branch === 'main') return [];
|
|
142
|
+
return [{ label: 'TEAM-123', source_field: 'branch', source_type: sourceType, confidence: 0.5 }];
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
module.exports = extractor;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Release Verification
|
|
149
|
+
|
|
150
|
+
For a release candidate checkout:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm test
|
|
154
|
+
npm run check
|
|
155
|
+
npm run smoke
|
|
156
|
+
npm run verify:package
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Manual Copilot CLI validation is local-only and not run in CI:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
node scripts/manual-copilot-cli-flow.js --setup-only
|
|
163
|
+
node scripts/manual-copilot-cli-flow.js --run-prompt --model gpt-5-mini
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The manual prompt performs one harmless tool call so Copilot CLI hook execution can be validated; answer quality is not part of the check. During the prompt run, the helper temporarily adds generated hooks to `~/.copilot/settings.json` and restores the original settings afterward.
|
|
167
|
+
|
|
168
|
+
## Current Limits
|
|
169
|
+
|
|
170
|
+
- Costs are estimates, not official billing records.
|
|
171
|
+
- Official GitHub usage report reconciliation is not included in `0.1.0`.
|
|
172
|
+
- Local OTLP collector mode is not included in `0.1.0`.
|
|
173
|
+
- Richer prompt/content capture and redaction controls are not included in `0.1.0`.
|
|
174
|
+
- Dashboard views are deferred until the CLI/query model proves useful.
|
package/RELEASE.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Release Checklist
|
|
2
|
+
|
|
3
|
+
This project publishes `copilot-metrics` to npm through GitHub Actions. The human gate is creating the GitHub release or tag that triggers the workflow; the workflow runs verification and then `npm publish`.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- GitHub repository exists at `nnexai/copilot-metrics`.
|
|
8
|
+
- npm Trusted Publishing is configured for package `copilot-metrics`:
|
|
9
|
+
- Publisher: GitHub Actions
|
|
10
|
+
- Repository: `nnexai/copilot-metrics`
|
|
11
|
+
- Workflow filename: `npm-publish.yml`
|
|
12
|
+
- Allowed action: `npm publish`
|
|
13
|
+
- npm package name is available or owned by the publishing account.
|
|
14
|
+
- Local working tree is clean before tagging.
|
|
15
|
+
|
|
16
|
+
## Local Verification
|
|
17
|
+
|
|
18
|
+
Run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm ci
|
|
22
|
+
npm test
|
|
23
|
+
npm run check
|
|
24
|
+
npm run smoke
|
|
25
|
+
npm run verify:package
|
|
26
|
+
npm pack --dry-run --json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The package must not include `.planning/`, `.codex/`, `test/`, fixture data, local telemetry, or generated SQLite stores.
|
|
30
|
+
|
|
31
|
+
## Manual Copilot CLI Validation
|
|
32
|
+
|
|
33
|
+
This is not a CI step because it depends on a local authenticated Copilot CLI and can call external services.
|
|
34
|
+
|
|
35
|
+
Run setup/dry-run first:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
node scripts/manual-copilot-cli-flow.js --setup-only
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Run the full local validation from this checkout:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
node scripts/manual-copilot-cli-flow.js --run-prompt --model gpt-5-mini
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The script creates an example workspace, configures `copilot-metrics`, installs repo-local hook config, temporarily applies user-level Copilot CLI hook settings for the prompt run, imports collected telemetry/hook JSONL, prints report output, and restores the original Copilot settings.
|
|
48
|
+
|
|
49
|
+
## GitHub Actions Publish
|
|
50
|
+
|
|
51
|
+
1. Confirm `package.json` version is `0.1.0`.
|
|
52
|
+
2. Commit all release changes.
|
|
53
|
+
3. Push `main`.
|
|
54
|
+
4. Create a GitHub release for `v0.1.0`.
|
|
55
|
+
5. Confirm the `Node.js Package` workflow passes and publishes to npm through Trusted Publishing.
|
|
56
|
+
|
|
57
|
+
## Post-Publish Verification
|
|
58
|
+
|
|
59
|
+
After the workflow publishes:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm view copilot-metrics@0.1.0 version
|
|
63
|
+
npm view copilot-metrics@0.1.0 dist.tarball
|
|
64
|
+
npx copilot-metrics@0.1.0 --help
|
|
65
|
+
npx copilot-metrics@0.1.0 paths --json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Do Not Publish
|
|
69
|
+
|
|
70
|
+
- Local telemetry JSONL files
|
|
71
|
+
- Generated SQLite stores
|
|
72
|
+
- `.planning/` artifacts
|
|
73
|
+
- `.codex/` runtime files
|
|
74
|
+
- Prompt content or hook logs
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { main } = require('../src/cli');
|
|
5
|
+
|
|
6
|
+
main(process.argv.slice(2), {
|
|
7
|
+
stdin: process.stdin,
|
|
8
|
+
stdout: process.stdout,
|
|
9
|
+
stderr: process.stderr,
|
|
10
|
+
env: process.env,
|
|
11
|
+
cwd: process.cwd(),
|
|
12
|
+
commandPath: process.argv[1],
|
|
13
|
+
}).catch((error) => {
|
|
14
|
+
process.stderr.write(`copilot-metrics: ${error.message}\n`);
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "copilot-metrics",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local-first Copilot usage telemetry setup and reporting tools.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"homepage": "https://github.com/nnexai/copilot-metrics#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+ssh://git@github.com/nnexai/copilot-metrics.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/nnexai/copilot-metrics/issues"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"copilot-metrics": "bin/copilot-metrics.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin/",
|
|
19
|
+
"src/",
|
|
20
|
+
"skills/",
|
|
21
|
+
"scripts/manual-copilot-cli-flow.js",
|
|
22
|
+
"README.md",
|
|
23
|
+
"CHANGELOG.md",
|
|
24
|
+
"RELEASE.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"cli": "node bin/copilot-metrics.js",
|
|
29
|
+
"check": "node --check bin/copilot-metrics.js && node --check src/*.js",
|
|
30
|
+
"smoke": "node scripts/smoke.js",
|
|
31
|
+
"verify:package": "node scripts/verify-package.js",
|
|
32
|
+
"test": "node --test"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"copilot",
|
|
36
|
+
"telemetry",
|
|
37
|
+
"opentelemetry",
|
|
38
|
+
"cli",
|
|
39
|
+
"ai-credits"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"sql.js": "^1.14.1"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { execFileSync, spawnSync } = require('node:child_process');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const { mergeGlobalSettingsHooks } = require('../src/setup');
|
|
9
|
+
|
|
10
|
+
const root = path.join(__dirname, '..');
|
|
11
|
+
const cli = path.join(root, 'bin', 'copilot-metrics.js');
|
|
12
|
+
|
|
13
|
+
function hasFlag(name) {
|
|
14
|
+
return process.argv.includes(name);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function option(name, fallback) {
|
|
18
|
+
const index = process.argv.indexOf(name);
|
|
19
|
+
return index >= 0 && process.argv[index + 1] ? process.argv[index + 1] : fallback;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function run(command, args, options = {}) {
|
|
23
|
+
return execFileSync(command, args, {
|
|
24
|
+
encoding: 'utf8',
|
|
25
|
+
stdio: options.stdio || 'pipe',
|
|
26
|
+
cwd: options.cwd,
|
|
27
|
+
env: options.env,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function runCli(args, cwd, home) {
|
|
32
|
+
return run(process.execPath, [cli, ...args, '--home', home], { cwd });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const workspace = path.resolve(option('--workspace', fs.mkdtempSync(path.join(os.tmpdir(), 'copilot-metrics-example-'))));
|
|
36
|
+
const metricsHome = path.resolve(option('--home', path.join(workspace, '.copilot-metrics-data')));
|
|
37
|
+
const model = option('--model', 'gpt-5-mini');
|
|
38
|
+
const prompt = option(
|
|
39
|
+
'--prompt',
|
|
40
|
+
'Run pwd once, then reply with exactly: copilot-metrics validation ok',
|
|
41
|
+
);
|
|
42
|
+
const runPrompt = hasFlag('--run-prompt');
|
|
43
|
+
const setupOnly = hasFlag('--setup-only') || !runPrompt;
|
|
44
|
+
|
|
45
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
46
|
+
fs.writeFileSync(path.join(workspace, 'README.md'), '# copilot-metrics validation workspace\n', { flag: 'a' });
|
|
47
|
+
if (!fs.existsSync(path.join(workspace, '.git'))) {
|
|
48
|
+
run('git', ['init'], { cwd: workspace });
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
run('git', ['remote', 'get-url', 'origin'], { cwd: workspace });
|
|
52
|
+
} catch {
|
|
53
|
+
run('git', ['remote', 'add', 'origin', 'https://github.com/nnexai/copilot-metrics.git'], { cwd: workspace });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
runCli(['init', '--json'], workspace, metricsHome);
|
|
57
|
+
const hookInstall = JSON.parse(runCli(['hooks', 'install', '--scope', 'local', '--surface', 'both', '--json'], workspace, metricsHome));
|
|
58
|
+
const envConfig = JSON.parse(runCli(['setup', 'copilot-cli', '--json'], workspace, metricsHome));
|
|
59
|
+
const paths = JSON.parse(runCli(['paths', '--json'], workspace, metricsHome));
|
|
60
|
+
|
|
61
|
+
const envFile = path.join(workspace, '.copilot-metrics.env');
|
|
62
|
+
fs.writeFileSync(envFile, `${Object.entries(envConfig).map(([key, value]) => `export ${key}=${JSON.stringify(value)}`).join('\n')}\n`);
|
|
63
|
+
|
|
64
|
+
const result = {
|
|
65
|
+
workspace,
|
|
66
|
+
metricsHome,
|
|
67
|
+
envFile,
|
|
68
|
+
hookConfig: hookInstall.target,
|
|
69
|
+
temporaryGlobalSettings: path.join(process.env.COPILOT_HOME || path.join(os.homedir(), '.copilot'), 'settings.json'),
|
|
70
|
+
copilotCliOtelJsonl: paths.copilotCliOtelJsonl,
|
|
71
|
+
hookEventsJsonl: paths.hookEventsJsonl,
|
|
72
|
+
ranPrompt: false,
|
|
73
|
+
telemetryExists: false,
|
|
74
|
+
hooksExist: false,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (setupOnly) {
|
|
78
|
+
process.stdout.write(`${JSON.stringify({ ...result, next: 'Re-run with --run-prompt to call Copilot CLI.' }, null, 2)}\n`);
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const settingsExisted = fs.existsSync(result.temporaryGlobalSettings);
|
|
83
|
+
const originalSettings = settingsExisted ? fs.readFileSync(result.temporaryGlobalSettings, 'utf8') : null;
|
|
84
|
+
let copilot;
|
|
85
|
+
try {
|
|
86
|
+
const settings = settingsExisted ? JSON.parse(originalSettings) : {};
|
|
87
|
+
fs.mkdirSync(path.dirname(result.temporaryGlobalSettings), { recursive: true });
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
result.temporaryGlobalSettings,
|
|
90
|
+
`${JSON.stringify(mergeGlobalSettingsHooks(settings, hookInstall.config.hooks), null, 2)}\n`,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
copilot = spawnSync('copilot', ['-p', prompt, '--yolo', '--model', model, '--no-auto-update', '--silent'], {
|
|
94
|
+
cwd: workspace,
|
|
95
|
+
env: {
|
|
96
|
+
...process.env,
|
|
97
|
+
...envConfig,
|
|
98
|
+
COPILOT_METRICS_HOME: metricsHome,
|
|
99
|
+
},
|
|
100
|
+
encoding: 'utf8',
|
|
101
|
+
maxBuffer: 1024 * 1024 * 8,
|
|
102
|
+
});
|
|
103
|
+
} finally {
|
|
104
|
+
if (settingsExisted) fs.writeFileSync(result.temporaryGlobalSettings, originalSettings);
|
|
105
|
+
else if (fs.existsSync(result.temporaryGlobalSettings)) fs.rmSync(result.temporaryGlobalSettings);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
result.ranPrompt = true;
|
|
109
|
+
result.exitCode = copilot.status;
|
|
110
|
+
result.stdout = (copilot.stdout || '').slice(0, 2000);
|
|
111
|
+
result.stderr = (copilot.stderr || '').slice(0, 2000);
|
|
112
|
+
|
|
113
|
+
if (copilot.status !== 0) {
|
|
114
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
115
|
+
process.exit(copilot.status || 1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
result.telemetryExists = fs.existsSync(paths.copilotCliOtelJsonl) && fs.statSync(paths.copilotCliOtelJsonl).size > 0;
|
|
119
|
+
result.hooksExist = fs.existsSync(paths.hookEventsJsonl) && fs.statSync(paths.hookEventsJsonl).size > 0;
|
|
120
|
+
|
|
121
|
+
if (result.telemetryExists) {
|
|
122
|
+
result.import = JSON.parse(runCli(['import', '--source', 'copilot-cli', '--file', paths.copilotCliOtelJsonl, '--json'], workspace, metricsHome));
|
|
123
|
+
result.models = JSON.parse(runCli(['report', 'models', '--json'], workspace, metricsHome));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (result.hooksExist) {
|
|
127
|
+
result.hooksImport = JSON.parse(runCli(['import', '--source', 'hooks', '--file', paths.hookEventsJsonl, '--json'], workspace, metricsHome));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
131
|
+
|
|
132
|
+
if (!result.telemetryExists || !result.hooksExist) {
|
|
133
|
+
process.exitCode = 2;
|
|
134
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: copilot-metrics
|
|
3
|
+
description: Query local Copilot Metrics data through the CLI without reading sensitive raw prompt content by default.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Copilot Metrics Skill
|
|
7
|
+
|
|
8
|
+
Use this skill when the user asks which Jira labels, repos, models, or Copilot surfaces are driving estimated local Copilot usage.
|
|
9
|
+
|
|
10
|
+
## Rules
|
|
11
|
+
|
|
12
|
+
- Use the `copilot-metrics` CLI instead of reading raw telemetry files directly.
|
|
13
|
+
- Start with `copilot-metrics paths --json` to find the local data directory.
|
|
14
|
+
- If the data directory is missing, suggest `copilot-metrics init`.
|
|
15
|
+
- Treat all costs as estimates, not official GitHub billing records.
|
|
16
|
+
- Do not read or display full prompts unless the user explicitly says content capture is enabled and asks for that content.
|
|
17
|
+
- Prefer machine-readable output when summarizing for another tool: use `--json` where available.
|
|
18
|
+
|
|
19
|
+
## Useful Commands
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
copilot-metrics paths --json
|
|
23
|
+
copilot-metrics setup vscode --json
|
|
24
|
+
copilot-metrics setup copilot-cli --json
|
|
25
|
+
copilot-metrics hooks preview --scope local --json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Future report commands should be preferred when present, for example:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
copilot-metrics report labels --json
|
|
32
|
+
copilot-metrics report label DEMO-12345 --json
|
|
33
|
+
copilot-metrics report unattributed --json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
If a future report command is unavailable, explain that ingestion and reporting have not been implemented in this checkout yet.
|