copilot.tools 1.0.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 +255 -0
- package/LICENSE +660 -0
- package/README.md +268 -0
- package/_extract_office.js +175 -0
- package/_list_sessions.js +206 -0
- package/package.json +24 -0
- package/skills/history/SKILL.md +46 -0
- package/skills/snapshot/SKILL.md +74 -0
- package/skills/teach-me/SKILL.md +1188 -0
- package/skills/tools/tools-skill/SKILL.md +38 -0
- package/tools-install.js +104 -0
package/README.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Copilot Tools Pack
|
|
2
|
+
|
|
3
|
+
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
4
|
+
|
|
5
|
+
A portable pack of global GitHub Copilot skills: `/tools`, `/history`, `/snapshot`, and `/teach-me`.
|
|
6
|
+
|
|
7
|
+
Cross-platform — works on Windows, macOS, and Linux.
|
|
8
|
+
|
|
9
|
+
## What's inside
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
copilot.history/
|
|
13
|
+
├── _list_sessions.js # Internal: reads Copilot session database
|
|
14
|
+
├── package.json # npm package config
|
|
15
|
+
├── tools-install.js # Cross-platform installer (copies skills globally)
|
|
16
|
+
├── skills/
|
|
17
|
+
│ ├── history/SKILL.md # /history — lists Copilot chat sessions
|
|
18
|
+
│ ├── snapshot/SKILL.md # /snapshot — pre-edit file backups
|
|
19
|
+
│ ├── teach-me/SKILL.md # /teach-me — interactive code quiz
|
|
20
|
+
│ └── tools/tools-skill/SKILL.md # /tools — lists available tools
|
|
21
|
+
├── README.md # This file
|
|
22
|
+
└── CHANGELOG.md # Version history
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
### Global Installation
|
|
28
|
+
|
|
29
|
+
1. Open a terminal (Windows PowerShell, Command Prompt, macOS Terminal, or Linux shell).
|
|
30
|
+
2. Run:
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g copilot.history
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Verify Installation
|
|
36
|
+
|
|
37
|
+
Skills are automatically copied to `~/.copilot/skills/` and will be auto-discovered by GitHub Copilot:
|
|
38
|
+
|
|
39
|
+
- **Windows**: `C:\Users\<username>\.copilot\skills\`
|
|
40
|
+
- **macOS/Linux**: `~/.copilot/skills/`
|
|
41
|
+
|
|
42
|
+
To verify, check that the directory exists and contains 4 subdirectories: `history`, `snapshot`, `teach-me`, `tools`.
|
|
43
|
+
|
|
44
|
+
## How to use
|
|
45
|
+
|
|
46
|
+
After installation:
|
|
47
|
+
|
|
48
|
+
1. **Open VS Code** with the GitHub Copilot extension installed.
|
|
49
|
+
2. **Open the Copilot Chat panel** (Ctrl+Shift+I / Cmd+Shift+I).
|
|
50
|
+
3. **Type `/`** to see available skills.
|
|
51
|
+
4. **Select a skill** or type the full command:
|
|
52
|
+
- `/tools` — List all available tools
|
|
53
|
+
- `/history` — Show Copilot chat session history with tokens and costs
|
|
54
|
+
- `/snapshot` — Toggle automatic pre-edit file backups
|
|
55
|
+
- `/teach-me` — Start an interactive code quiz
|
|
56
|
+
|
|
57
|
+
## Skills Reference
|
|
58
|
+
|
|
59
|
+
### `/tools`
|
|
60
|
+
|
|
61
|
+
Lists every tool available in the current Copilot session, grouped by category (File I/O, Search, Git, Sub-agents, Shell, Planning, etc.), with a brief description of what each tool does. Read-only — no files touched.
|
|
62
|
+
|
|
63
|
+
**`/tools help`** or **`/tools readme`** — Displays the full tools documentation.
|
|
64
|
+
|
|
65
|
+
### `/history`
|
|
66
|
+
|
|
67
|
+
Lists all Copilot chat sessions with metadata:
|
|
68
|
+
|
|
69
|
+
- **Date/time** — When the session was created
|
|
70
|
+
- **Title** — Session title or first message
|
|
71
|
+
- **Messages** — Message count in that session
|
|
72
|
+
- **Tokens** — Tokens consumed (with thousands separators)
|
|
73
|
+
- **Cost** — Estimated session cost in USD
|
|
74
|
+
- **Model** — Which LLM was used
|
|
75
|
+
|
|
76
|
+
A **TOTAL** row summarizes across all sessions: total count, total messages, total tokens, and total cost.
|
|
77
|
+
|
|
78
|
+
**Note:** Session data is read from Copilot's SQLite database (`session-store.db`), which persists across VS Code restarts. Token counts and costs reflect actual Copilot API usage.
|
|
79
|
+
|
|
80
|
+
### `/snapshot`
|
|
81
|
+
|
|
82
|
+
Toggle automatic pre-edit file backups:
|
|
83
|
+
- **`/snapshot on`** — Enable backups
|
|
84
|
+
- **`/snapshot off`** — Disable backups
|
|
85
|
+
- **`/snapshot status`** — Show current status
|
|
86
|
+
|
|
87
|
+
**Why use this:** Snapshot is designed for folders that are **not** Git repos — especially folders with Office documents (`.docx`, `.pptx`, `.xlsx`, `.pdf`) that Copilot is about to edit. Before every modification, snapshot saves a timestamped copy into `_snapshots/` so you can always recover the pre-AI version:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
report.docx → _snapshots/report.2026-06-21_14-30-00.docx
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
If the workspace already has a `.git` directory, snapshot politely declines — Git already has your back.
|
|
94
|
+
|
|
95
|
+
### `/teach-me`
|
|
96
|
+
|
|
97
|
+
An interactive code-teaching quiz that randomly selects real snippets from your current project. Two independent dimensions across five difficulty levels:
|
|
98
|
+
|
|
99
|
+
**Response mode** (how you answer):
|
|
100
|
+
- **Passive** (default) — Describe your answer in the chat.
|
|
101
|
+
- **Interactive** (`teach me interactive`) — Edit the actual file, then say `done`.
|
|
102
|
+
|
|
103
|
+
**Content theme** (what it's about):
|
|
104
|
+
- **Standard** (default) — Code comprehension or line reconstruction.
|
|
105
|
+
- **SOLID** (`teach me solid`) — Anti-pattern detection and design restoration.
|
|
106
|
+
- **Spec** (`teach me spec`) — Spec-to-code traceability using office docs as source.
|
|
107
|
+
- **BA-Gates** (`teach me ba`) — BA documentation quality analysis (ambiguity, AC completeness, edge case coverage).
|
|
108
|
+
|
|
109
|
+
**Examples:**
|
|
110
|
+
- `/teach-me` — Standard code quiz, passive mode
|
|
111
|
+
- `/teach-me level 4` — Difficulty 4, standard theme
|
|
112
|
+
- `/teach-me python interactive` — Python code, edit mode
|
|
113
|
+
- `/teach-me solid` — SOLID anti-pattern restoration, passive
|
|
114
|
+
- `/teach-me spec interactive` — Spec↔code traceability, edit mode
|
|
115
|
+
- `/teach-me ba level 2` — BA documentation quality, difficulty 2
|
|
116
|
+
|
|
117
|
+
**Mid-session commands:** `hint` · `skip` · `next` · `level 3` · `easier` · `harder` · `interactive` · `passive` · `done` · `stop`
|
|
118
|
+
|
|
119
|
+
**Difficulty levels:**
|
|
120
|
+
|
|
121
|
+
| Level | Line range | What you'll face |
|
|
122
|
+
|-------|-----------|-------────────|
|
|
123
|
+
| 1 | 5–15 | Straight-line logic, if/else, basic function calls |
|
|
124
|
+
| 2 | 8–20 | Loops, list/dict operations, simple try/except |
|
|
125
|
+
| 3 | 12–30 | Comprehensions, decorators, with statements, multiple branches |
|
|
126
|
+
| 4 | 18–45 | Generators, async/await, descriptors, threading, closures |
|
|
127
|
+
| 5 | 25–55 | Metaclasses, complex async patterns, multi-threading, architectural glue |
|
|
128
|
+
|
|
129
|
+
**In SOLID theme:** the model rewrites code with deliberate anti-patterns (STUPID, WET, god objects, tight coupling), presents the degraded code, and you restore good design.
|
|
130
|
+
|
|
131
|
+
**In Spec theme:** the model scans your office documents (`.docx`, `.pptx`, `.xlsx`) and source code, then quizzes you on the correlation between them. Each round randomly picks one direction — either "what code implements this spec?" or "what spec does this code satisfy?"
|
|
132
|
+
|
|
133
|
+
**In BA-Gates theme:** the model analyzes your BA documentation files (`.docx`, `.pptx`, `.xlsx`, `.md`, `.feature`, `.txt`) against three quality gates: **Ambiguity**, **Acceptance Criteria Completeness**, and **Edge Case Coverage**. You identify which gates a fragment violates and describe the developer impact.
|
|
134
|
+
|
|
135
|
+
## Requirements
|
|
136
|
+
|
|
137
|
+
- **Node.js** (version 14 or later) — for the installer and session history lookup
|
|
138
|
+
- **VS Code** (latest version)
|
|
139
|
+
- **GitHub Copilot extension** — installed and authenticated
|
|
140
|
+
- **LLM credits/API key** — configured in your Copilot settings (enterprise or personal)
|
|
141
|
+
|
|
142
|
+
## Troubleshooting
|
|
143
|
+
|
|
144
|
+
### Skills not showing up in Copilot
|
|
145
|
+
|
|
146
|
+
1. Verify installation: Check that `~/.copilot/skills/` exists with 4 subdirectories.
|
|
147
|
+
2. Restart VS Code completely (not just the Copilot chat panel).
|
|
148
|
+
3. In Copilot chat, type `/` to see if skills appear in autocomplete.
|
|
149
|
+
|
|
150
|
+
### `/history` shows "No sessions found"
|
|
151
|
+
|
|
152
|
+
This is normal for a fresh Copilot install. Start a new chat session in Copilot, then run `/history` again — it will show the new session.
|
|
153
|
+
|
|
154
|
+
### `/teach-me` can't find code in my project
|
|
155
|
+
|
|
156
|
+
Make sure your project files are open in the VS Code workspace. The quiz needs access to the actual code files to select snippets.
|
|
157
|
+
|
|
158
|
+
## Contact & License
|
|
159
|
+
|
|
160
|
+
Developer: [species8472](mailto:swyuri@yahoo.com)
|
|
161
|
+
|
|
162
|
+
Copyright © 2026 Celestial Consulting Ltd. Licensed under the [GNU Affero General Public License v3.0](LICENSE).
|
|
163
|
+
repos — especially folders full of Office documents (`.docx`, `.pptx`, `.xlsx`,
|
|
164
|
+
`.pdf`) that an AI agent is about to edit. Before every file modification,
|
|
165
|
+
snapshot saves a timestamped copy into `_snapshots/` so you can always get back
|
|
166
|
+
to the pre-AI version:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
report.docx → _snapshots/report.2026-06-21_14-30-00.docx
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
If the workspace already has a `.git` directory, snapshot politely declines —
|
|
173
|
+
Git already has your back.
|
|
174
|
+
|
|
175
|
+
### `//teach-me`
|
|
176
|
+
An interactive code-teaching quiz that randomly selects real snippets from your
|
|
177
|
+
current project. Two independent dimensions across five difficulty levels:
|
|
178
|
+
|
|
179
|
+
**Response mode** (how you answer):
|
|
180
|
+
- **Passive** (default) — Describe your answer in the console.
|
|
181
|
+
- **Interactive** (`teach me interactive`) — Edit the actual file, say `done`.
|
|
182
|
+
|
|
183
|
+
**Content theme** (what it's about):
|
|
184
|
+
- **Standard** (default) — Code comprehension or line reconstruction.
|
|
185
|
+
- **SOLID** (`teach me solid`) — Anti-pattern detection and design restoration.
|
|
186
|
+
- **Spec** (`teach me spec`) — Spec-to-code traceability using office docs as source.
|
|
187
|
+
- **BA-Gates** (`teach me ba`) — BA documentation quality analysis (ambiguity, AC completeness, edge case coverage).
|
|
188
|
+
|
|
189
|
+
| Trigger | Response | Theme | What happens |
|
|
190
|
+
|---------|----------|-------|-------------|
|
|
191
|
+
| `teach me` | passive | standard | Present code; you explain it |
|
|
192
|
+
| `teach me interactive` | interactive | standard | Lines removed; you restore them |
|
|
193
|
+
| `teach me solid` | passive | solid | Degraded code; you describe fixes |
|
|
194
|
+
| `teach me solid interactive` | interactive | solid | Degraded code; you edit to fix it |
|
|
195
|
+
| `teach me spec` | passive | spec | Two directions: spec→code (show requirement, you find the code) or code→spec (show code, you name the requirement) |
|
|
196
|
+
| `teach me spec interactive` | interactive | spec | Two directions: spec→code (edit code to satisfy a spec) or code→spec (edit spec to match the code) |
|
|
197
|
+
| `teach me ba` | passive | ba-gates | BA doc fragment; you identify quality gate violations |
|
|
198
|
+
| `teach me ba interactive` | interactive | ba-gates | BA doc with issues; you edit to fix them |
|
|
199
|
+
|
|
200
|
+
**Triggers:** `teach me` · `quiz me` · `test my knowledge` · `code quiz` ·
|
|
201
|
+
`drill me`
|
|
202
|
+
|
|
203
|
+
**Modifiers (combine freely):**
|
|
204
|
+
|
|
205
|
+
| Modifier | Example | What it does |
|
|
206
|
+
|----------|---------|-------------|
|
|
207
|
+
| Language | `teach me python` | Restrict to Python, TypeScript, Rust, Go, Java… |
|
|
208
|
+
| Level | `teach me level 3` or `teach me l3` | Set difficulty 1–5 (default: 3) |
|
|
209
|
+
| Response | `teach me interactive` | Edit the file instead of describing |
|
|
210
|
+
| Theme | `teach me solid` | Anti-pattern detection — fix degraded design |
|
|
211
|
+
| Theme | `teach me spec` | Spec↔code traceability — either direction (show spec, find the code; or show code, name the requirement) |
|
|
212
|
+
| Theme | `teach me ba` | BA documentation quality analysis |
|
|
213
|
+
| Scope | `teach me services/` | Narrow to a specific file or folder |
|
|
214
|
+
| Concept | `teach me decorators` | Target: decorators, async, generators, context managers, comprehensions, error handling, type hints, threading |
|
|
215
|
+
|
|
216
|
+
**Difficulty levels:**
|
|
217
|
+
|
|
218
|
+
| Level | Line range | What you'll face |
|
|
219
|
+
|-------|-----------|------------------|
|
|
220
|
+
| 1 | 5–15 | Straight-line logic, `if`/`else`, basic function calls |
|
|
221
|
+
| 2 | 8–20 | Loops, list/dict operations, simple `try`/`except` |
|
|
222
|
+
| 3 | 12–30 | Comprehensions, decorators, `with` statements, multiple branches |
|
|
223
|
+
| 4 | 18–45 | Generators, `async`/`await`, descriptors, threading, closures |
|
|
224
|
+
| 5 | 25–55 | Metaclasses, complex async patterns, multi-threading, architectural glue |
|
|
225
|
+
|
|
226
|
+
**Mid-round commands:** `hint` (get a nudge) · `skip` (see the answer) ·
|
|
227
|
+
`next` (draw a new snippet) · `level N` · `easier` · `harder` ·
|
|
228
|
+
`interactive` · `passive` · `solid` · `spec` · `ba` · `done` (verify work) · `stop`
|
|
229
|
+
|
|
230
|
+
**At the end** you get a session summary: rounds completed, mode, files covered,
|
|
231
|
+
what you're strong on, what to review, and a suggested next level.
|
|
232
|
+
|
|
233
|
+
In **SOLID theme**: the model rewrites code with deliberate anti-patterns
|
|
234
|
+
(STUPID, WET, god objects, tight coupling), presents the degraded code, and you
|
|
235
|
+
restore good design. Files are always restored with `git checkout` after each
|
|
236
|
+
round.
|
|
237
|
+
|
|
238
|
+
In **Spec theme**: the model scans your office documents (`.docx`, `.pptx`,
|
|
239
|
+
`.xlsx`) and source code, then quizzes you on the correlation between them.
|
|
240
|
+
Each round randomly picks one direction — either "what code implements this
|
|
241
|
+
spec?" or "what spec does this code satisfy?" — testing whether you truly
|
|
242
|
+
understand how requirements map to implementation. In interactive mode, you
|
|
243
|
+
edit the code to satisfy the spec; in passive mode, you describe the link.
|
|
244
|
+
|
|
245
|
+
In **BA-Gates theme**: the model runs a two-pass analysis over your BA documentation
|
|
246
|
+
files (`.docx`, `.pptx`, `.xlsx`, `.md`, `.feature`, `.txt`). Pass 1 uses regex
|
|
247
|
+
triage to flag fragments against three quality gates — **Ambiguity**
|
|
248
|
+
(unmeasurable terms, passive states), **Acceptance Criteria Completeness**
|
|
249
|
+
(missing structure, unverifiable outcomes), and **Edge Case Coverage** (missing
|
|
250
|
+
negative scenarios, error states). Pass 2 runs an LLM interpretation per
|
|
251
|
+
fragment at round time to generate quiz material and concrete improvement
|
|
252
|
+
options. You identify which gates a fragment triggers and describe the developer
|
|
253
|
+
impact. On skip or strike-3, the model presents 2–3 domain-specific rewrites per
|
|
254
|
+
issue with a best-fit recommendation.
|
|
255
|
+
|
|
256
|
+
## Want to take this further
|
|
257
|
+
If you've enjoyed using //teach-me and want to audit your entire specification repository and get a formatted report with readiness scores, band distributions, and ranked improvement recommendations, check out our licensed product //spec-analysis. Contact the developer for more info.
|
|
258
|
+
|
|
259
|
+
## Requirements
|
|
260
|
+
- Node.js (for the `codewhale-history` command)
|
|
261
|
+
- [CodeWhale](https://codewhale.net/en/docs) — install from [codewhale.net](https://codewhale.net/en/docs)
|
|
262
|
+
- An API key for your preferred LLM provider (configured in CodeWhale settings)
|
|
263
|
+
|
|
264
|
+
## Contact Developer
|
|
265
|
+
[species8472](mailto:swyuri@yahoo.com)
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
Copyright © 2026 Celestial Consulting Ltd. Licensed under the [GNU Affero General Public License v3.0](LICENSE).
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (C) 2026 Celestial Consulting Ltd
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as
|
|
7
|
+
* published by the Free Software Foundation, either version 3 of the
|
|
8
|
+
* License, or (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Office Document Text Extractor
|
|
21
|
+
*
|
|
22
|
+
* Extracts plain text from .docx, .pptx, and .xlsx files.
|
|
23
|
+
* Pure Node.js — no external dependencies. Handles ZIP/Open XML internally.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* node _extract_office.js <file.docx|pptx|xlsx>
|
|
27
|
+
* codewhale-doc-extract <file.docx|pptx|xlsx>
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
const zlib = require('zlib');
|
|
32
|
+
|
|
33
|
+
const filePath = process.argv[2];
|
|
34
|
+
if (!filePath) {
|
|
35
|
+
console.error('Usage: codewhale-doc-extract <file.docx|pptx|xlsx>');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ext = filePath.toLowerCase().split('.').pop();
|
|
40
|
+
const buf = fs.readFileSync(filePath);
|
|
41
|
+
|
|
42
|
+
// ── ZIP parser ─────────────────────────────────────────────────
|
|
43
|
+
function readZip(buffer) {
|
|
44
|
+
const entries = [];
|
|
45
|
+
let offset = 0;
|
|
46
|
+
|
|
47
|
+
while (offset < buffer.length - 4) {
|
|
48
|
+
const sig = buffer.readUInt32LE(offset);
|
|
49
|
+
if (sig !== 0x04034b50) break;
|
|
50
|
+
|
|
51
|
+
const compression = buffer.readUInt16LE(offset + 8);
|
|
52
|
+
const compressedSize = buffer.readUInt32LE(offset + 18);
|
|
53
|
+
const nameLen = buffer.readUInt16LE(offset + 26);
|
|
54
|
+
const extraLen = buffer.readUInt16LE(offset + 28);
|
|
55
|
+
|
|
56
|
+
const name = buffer.toString('utf-8', offset + 30, offset + 30 + nameLen);
|
|
57
|
+
const dataStart = offset + 30 + nameLen + extraLen;
|
|
58
|
+
|
|
59
|
+
let data;
|
|
60
|
+
if (compression === 0) {
|
|
61
|
+
data = buffer.slice(dataStart, dataStart + compressedSize);
|
|
62
|
+
} else if (compression === 8) {
|
|
63
|
+
const raw = buffer.slice(dataStart, dataStart + compressedSize);
|
|
64
|
+
data = zlib.inflateRawSync(raw);
|
|
65
|
+
} else {
|
|
66
|
+
offset = dataStart + compressedSize;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
entries.push({ name, data: data.toString('utf-8') });
|
|
71
|
+
offset = dataStart + compressedSize;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return entries;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── XML text stripper ──────────────────────────────────────────
|
|
78
|
+
function stripXml(xml) {
|
|
79
|
+
return xml
|
|
80
|
+
.replace(/<[^>]+>/g, ' ')
|
|
81
|
+
.replace(/&/g, '&')
|
|
82
|
+
.replace(/</g, '<')
|
|
83
|
+
.replace(/>/g, '>')
|
|
84
|
+
.replace(/"/g, '"')
|
|
85
|
+
.replace(/'/g, "'")
|
|
86
|
+
.replace(/&#?\w+;/g, ' ')
|
|
87
|
+
.replace(/\s+/g, ' ')
|
|
88
|
+
.trim();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── Format-specific extraction ─────────────────────────────────
|
|
92
|
+
const entries = readZip(buf);
|
|
93
|
+
let text = '';
|
|
94
|
+
|
|
95
|
+
if (ext === 'docx') {
|
|
96
|
+
const doc = entries.find(e => e.name === 'word/document.xml');
|
|
97
|
+
if (doc) text = stripXml(doc.data);
|
|
98
|
+
|
|
99
|
+
for (const e of entries) {
|
|
100
|
+
if (e.name.startsWith('word/header') || e.name.startsWith('word/footer')) {
|
|
101
|
+
text += '\n' + stripXml(e.data);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else if (ext === 'pptx') {
|
|
105
|
+
const slides = entries
|
|
106
|
+
.filter(e => /^ppt\/slides\/slide\d+\.xml$/.test(e.name))
|
|
107
|
+
.sort((a, b) => {
|
|
108
|
+
const na = parseInt(a.name.match(/slide(\d+)/)[1]);
|
|
109
|
+
const nb = parseInt(b.name.match(/slide(\d+)/)[1]);
|
|
110
|
+
return na - nb;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i < slides.length; i++) {
|
|
114
|
+
text += '\n--- Slide ' + (i + 1) + ' ---\n';
|
|
115
|
+
text += stripXml(slides[i].data) + '\n';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const e of entries) {
|
|
119
|
+
if (e.name.startsWith('ppt/notesSlides/notesSlide')) {
|
|
120
|
+
text += '\n[Notes]\n' + stripXml(e.data) + '\n';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} else if (ext === 'xlsx') {
|
|
124
|
+
const sst = entries.find(e => e.name === 'xl/sharedStrings.xml');
|
|
125
|
+
const strings = sst ? extractSharedStrings(sst.data) : [];
|
|
126
|
+
|
|
127
|
+
const sheets = entries
|
|
128
|
+
.filter(e => /^xl\/worksheets\/sheet\d+\.xml$/.test(e.name))
|
|
129
|
+
.sort((a, b) => {
|
|
130
|
+
const na = parseInt(a.name.match(/sheet(\d+)/)[1]);
|
|
131
|
+
const nb = parseInt(b.name.match(/sheet(\d+)/)[1]);
|
|
132
|
+
return na - nb;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < sheets.length; i++) {
|
|
136
|
+
text += '\n--- Sheet ' + (i + 1) + ' ---\n';
|
|
137
|
+
text += renderSheet(sheets[i].data, strings) + '\n';
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
console.error('Unsupported format. Use .docx, .pptx, or .xlsx');
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log(text.trim());
|
|
145
|
+
|
|
146
|
+
// ── XLSX helpers ───────────────────────────────────────────────
|
|
147
|
+
function extractSharedStrings(xml) {
|
|
148
|
+
const matches = xml.match(/<si>([\s\S]*?)<\/si>/g) || [];
|
|
149
|
+
return matches.map(si => stripXml(si));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function renderSheet(sheetXml, strings) {
|
|
153
|
+
const rows = sheetXml.match(/<row[\s\S]*?<\/row>/g) || [];
|
|
154
|
+
let output = '';
|
|
155
|
+
for (const row of rows) {
|
|
156
|
+
const cells = row.match(/<c[^>]*>[\s\S]*?<\/c>/g) || [];
|
|
157
|
+
const rowValues = [];
|
|
158
|
+
for (const cell of cells) {
|
|
159
|
+
const tMatch = cell.match(/t="([^"]*)"/);
|
|
160
|
+
const type = tMatch ? tMatch[1] : null;
|
|
161
|
+
const vMatch = cell.match(/<v>([^<]*)<\/v>/);
|
|
162
|
+
if (!vMatch) continue;
|
|
163
|
+
if (type === 's') {
|
|
164
|
+
const idx = parseInt(vMatch[1]);
|
|
165
|
+
rowValues.push(strings[idx] || '');
|
|
166
|
+
} else {
|
|
167
|
+
rowValues.push(vMatch[1]);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (rowValues.some(v => v.trim())) {
|
|
171
|
+
output += rowValues.join(' | ') + '\n';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return output;
|
|
175
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (C) 2026 Celestial Consulting Ltd
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as
|
|
7
|
+
* published by the Free Software Foundation, either version 3 of the
|
|
8
|
+
* License, or (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Copilot Session Lister
|
|
21
|
+
* Queries Copilot's SQLite session database to display all chat sessions
|
|
22
|
+
* with tokens consumed, cost, and model information.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
const os = require('os');
|
|
28
|
+
|
|
29
|
+
// Determine platform-specific path to Copilot's session store
|
|
30
|
+
const getSessionDbPath = () => {
|
|
31
|
+
const home = os.homedir();
|
|
32
|
+
if (process.platform === 'win32') {
|
|
33
|
+
return path.join(home, 'AppData', 'Roaming', 'Code', 'User', 'globalStorage', 'github.copilot-chat', 'session-store.db');
|
|
34
|
+
} else {
|
|
35
|
+
const baseDir = process.platform === 'darwin'
|
|
36
|
+
? path.join(home, 'Library', 'Application Support')
|
|
37
|
+
: path.join(home, '.config');
|
|
38
|
+
return path.join(baseDir, 'Code', 'User', 'globalStorage', 'github.copilot-chat', 'session-store.db');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const dbPath = getSessionDbPath();
|
|
43
|
+
|
|
44
|
+
// Check if database exists
|
|
45
|
+
if (!fs.existsSync(dbPath)) {
|
|
46
|
+
console.log('No Copilot sessions found.');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Try to use better-sqlite3 for fast synchronous access
|
|
51
|
+
let db;
|
|
52
|
+
try {
|
|
53
|
+
const Database = require('better-sqlite3');
|
|
54
|
+
db = new Database(dbPath, { readonly: true });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// Fall back to sqlite3 async
|
|
57
|
+
try {
|
|
58
|
+
const sqlite3 = require('sqlite3').verbose();
|
|
59
|
+
queryWithAsync(sqlite3);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
} catch (err2) {
|
|
62
|
+
console.error('');
|
|
63
|
+
console.error('❌ Cannot query Copilot session database.');
|
|
64
|
+
console.error('Required: Install sqlite3 package');
|
|
65
|
+
console.error('');
|
|
66
|
+
console.error('Run: npm install better-sqlite3');
|
|
67
|
+
console.error('');
|
|
68
|
+
console.error('Database location:', dbPath);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Query synchronously with better-sqlite3
|
|
74
|
+
if (db) {
|
|
75
|
+
querySessions(db);
|
|
76
|
+
db.close();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function querySessions(db) {
|
|
80
|
+
try {
|
|
81
|
+
// List all tables to understand schema
|
|
82
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name").all();
|
|
83
|
+
|
|
84
|
+
// Try to find chat sessions in common table names
|
|
85
|
+
const sessionTable = tables.find(t => t.name.toLowerCase().includes('session') || t.name.toLowerCase().includes('chat'));
|
|
86
|
+
|
|
87
|
+
if (!sessionTable) {
|
|
88
|
+
console.log('No session data found in Copilot database.');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Query the session table
|
|
93
|
+
const stmt = db.prepare(`SELECT * FROM "${sessionTable.name}" ORDER BY rowid DESC`);
|
|
94
|
+
const sessions = stmt.all();
|
|
95
|
+
|
|
96
|
+
if (sessions.length === 0) {
|
|
97
|
+
console.log('No Copilot sessions found.');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const results = [];
|
|
102
|
+
for (const session of sessions) {
|
|
103
|
+
// Extract available fields (schema may vary)
|
|
104
|
+
const created = formatDate(session.created_at || session.createdAt || session.timestamp || new Date());
|
|
105
|
+
const title = truncate(session.title || session.name || 'Untitled', 30);
|
|
106
|
+
const msgs = session.message_count || session.messages || 0;
|
|
107
|
+
const tokens = session.total_tokens || session.tokens || 0;
|
|
108
|
+
const cost = session.cost_usd || session.cost || 0;
|
|
109
|
+
const model = session.model || '?';
|
|
110
|
+
|
|
111
|
+
console.log(
|
|
112
|
+
`${created} | ${title} | ${msgs} msgs | ${typeof tokens === 'number' ? tokens.toLocaleString() : tokens} tok | $${parseFloat(cost).toFixed(4)} | ${model}`
|
|
113
|
+
);
|
|
114
|
+
results.push({ msgs, tokens, cost: parseFloat(cost) });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Print totals
|
|
118
|
+
if (results.length > 0) {
|
|
119
|
+
const totalMsgs = results.reduce((s, r) => s + r.msgs, 0);
|
|
120
|
+
const totalTokens = results.reduce((s, r) => s + r.tokens, 0);
|
|
121
|
+
const totalCost = results.reduce((s, r) => s + r.cost, 0);
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log(
|
|
124
|
+
`TOTAL | ${results.length} sessions | ${totalMsgs} msgs | ${totalTokens.toLocaleString()} tok | $${totalCost.toFixed(4)} |`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error('Error querying Copilot session database:', err.message);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function queryWithAsync(sqlite3) {
|
|
134
|
+
const db = new sqlite3.Database(dbPath, sqlite3.OPEN_READONLY, (err) => {
|
|
135
|
+
if (err) {
|
|
136
|
+
console.error('Error opening database:', err);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
db.all("SELECT name FROM sqlite_master WHERE type='table'", (err, tables) => {
|
|
141
|
+
if (err) {
|
|
142
|
+
console.error('Error reading schema:', err);
|
|
143
|
+
db.close();
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const sessionTable = tables?.find(t => t.name.toLowerCase().includes('session')) || tables?.[0];
|
|
148
|
+
if (!sessionTable) {
|
|
149
|
+
console.log('No session data found.');
|
|
150
|
+
db.close();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
db.all(`SELECT * FROM "${sessionTable.name}" ORDER BY rowid DESC`, (err, sessions) => {
|
|
155
|
+
if (err || !sessions || sessions.length === 0) {
|
|
156
|
+
console.log('No Copilot sessions found.');
|
|
157
|
+
db.close();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const results = [];
|
|
162
|
+
for (const session of sessions) {
|
|
163
|
+
const created = formatDate(session.created_at || session.createdAt || session.timestamp || new Date());
|
|
164
|
+
const title = truncate(session.title || session.name || 'Untitled', 30);
|
|
165
|
+
const msgs = session.message_count || session.messages || 0;
|
|
166
|
+
const tokens = session.total_tokens || session.tokens || 0;
|
|
167
|
+
const cost = session.cost_usd || session.cost || 0;
|
|
168
|
+
const model = session.model || '?';
|
|
169
|
+
|
|
170
|
+
console.log(
|
|
171
|
+
`${created} | ${title} | ${msgs} msgs | ${typeof tokens === 'number' ? tokens.toLocaleString() : tokens} tok | $${parseFloat(cost).toFixed(4)} | ${model}`
|
|
172
|
+
);
|
|
173
|
+
results.push({ msgs, tokens, cost: parseFloat(cost) });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (results.length > 0) {
|
|
177
|
+
const totalMsgs = results.reduce((s, r) => s + r.msgs, 0);
|
|
178
|
+
const totalTokens = results.reduce((s, r) => s + r.tokens, 0);
|
|
179
|
+
const totalCost = results.reduce((s, r) => s + r.cost, 0);
|
|
180
|
+
console.log('');
|
|
181
|
+
console.log(
|
|
182
|
+
`TOTAL | ${results.length} sessions | ${totalMsgs} msgs | ${totalTokens.toLocaleString()} tok | $${totalCost.toFixed(4)} |`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
db.close();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function formatDate(dateInput) {
|
|
193
|
+
let date;
|
|
194
|
+
if (typeof dateInput === 'string') {
|
|
195
|
+
date = new Date(dateInput);
|
|
196
|
+
} else if (typeof dateInput === 'number') {
|
|
197
|
+
date = new Date(dateInput * 1000); // SQLite timestamps are seconds
|
|
198
|
+
} else {
|
|
199
|
+
date = dateInput;
|
|
200
|
+
}
|
|
201
|
+
return date.toISOString().substring(0, 16);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function truncate(str, len) {
|
|
205
|
+
return str.length > len ? str.substring(0, len - 3) + '...' : str;
|
|
206
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "copilot.tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Copilot skills: /tools, /history, /snapshot, /teach-me — session tracking, file snapshots, interactive code quiz — global install",
|
|
5
|
+
"bin": {
|
|
6
|
+
"copilot-history": "./_list_sessions.js",
|
|
7
|
+
"copilot-tools-install": "./tools-install.js",
|
|
8
|
+
"copilot-doc-extract": "./_extract_office.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"_list_sessions.js",
|
|
12
|
+
"_extract_office.js",
|
|
13
|
+
"tools-install.js",
|
|
14
|
+
"README.md",
|
|
15
|
+
"CHANGELOG.md",
|
|
16
|
+
"skills"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"postinstall": "node ./tools-install.js"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=14"
|
|
23
|
+
}
|
|
24
|
+
}
|