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/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # Copilot Tools Pack
2
+
3
+ [![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](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(/&amp;/g, '&')
82
+ .replace(/&lt;/g, '<')
83
+ .replace(/&gt;/g, '>')
84
+ .replace(/&quot;/g, '"')
85
+ .replace(/&apos;/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
+ }