@jaybeeuu/agent-cortex 1.20.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.
Files changed (154) hide show
  1. package/.beads/README.md +81 -0
  2. package/.beads/config.yaml +1 -0
  3. package/.beads/issues.jsonl +236 -0
  4. package/.beads/metadata.json +7 -0
  5. package/.claude-plugin/marketplace.json +16 -0
  6. package/.github/workflows/ci.test.ts +217 -0
  7. package/.github/workflows/ci.yml +35 -0
  8. package/.github/workflows/publish.yml +36 -0
  9. package/.junie/guidelines.md +87 -0
  10. package/.junie/mcp/mcp.json +10 -0
  11. package/AGENTS.md +102 -0
  12. package/CHANGELOG.md +705 -0
  13. package/README.md +209 -0
  14. package/agents/README.md +235 -0
  15. package/agents/plan.agent.md +56 -0
  16. package/agents/ralph-plan.agent.md +119 -0
  17. package/agents/ralph.agent.md +305 -0
  18. package/agents/strategy.agent.md +109 -0
  19. package/agents-native/ralph.md +193 -0
  20. package/bin/agent-cortex.mjs +27 -0
  21. package/claude/.claude-plugin/plugin.json +11 -0
  22. package/claude/.mcp.json +18 -0
  23. package/claude/agents/plan.md +56 -0
  24. package/claude/agents/ralph-plan.md +119 -0
  25. package/claude/agents/ralph.md +193 -0
  26. package/claude/agents/strategy.md +109 -0
  27. package/claude/hooks.json +23 -0
  28. package/claude/scripts/statusline-command.sh +124 -0
  29. package/docs/ideas/auto-model-selection.md +75 -0
  30. package/docs/ideas/improve-pi-system-prompt.md +34 -0
  31. package/docs/ideas/model-selector.md +40 -0
  32. package/docs/ideas/pi-task-memory.md +34 -0
  33. package/docs/ideas/qa-extension.md +43 -0
  34. package/docs/inspirations.md +33 -0
  35. package/docs/research/gas-city-research.md +256 -0
  36. package/docs/research/skill-design-research.md +286 -0
  37. package/docs/research/skill-improvements-analysis.md +117 -0
  38. package/docs/research/skill-patterns-research.md +24 -0
  39. package/docs/skills/skill-anatomy.md +149 -0
  40. package/docs/user-preferences.md +17 -0
  41. package/extensions/agent-modes/index.ts +507 -0
  42. package/extensions/auto-name.ts +107 -0
  43. package/extensions/context-loader.md +35 -0
  44. package/extensions/context-loader.ts +65 -0
  45. package/extensions/lib/tiny-model.ts +212 -0
  46. package/extensions/notify/index.ts +293 -0
  47. package/extensions/session-start.ts +44 -0
  48. package/extensions/skill-stats/README.md +74 -0
  49. package/extensions/skill-stats/context-loader.test.ts +107 -0
  50. package/extensions/skill-stats/discover.ts +119 -0
  51. package/extensions/skill-stats/format.ts +13 -0
  52. package/extensions/skill-stats/index.test.ts +452 -0
  53. package/extensions/skill-stats/index.ts +222 -0
  54. package/extensions/skill-stats/package.json +19 -0
  55. package/extensions/skill-stats/pnpm-lock.yaml +329 -0
  56. package/extensions/skill-stats/record.ts +48 -0
  57. package/extensions/skill-stats/store.ts +104 -0
  58. package/extensions/skill-stats/tsconfig.json +12 -0
  59. package/extensions/subagent/README.md +175 -0
  60. package/extensions/subagent/agents/planner.md +37 -0
  61. package/extensions/subagent/agents/reviewer.md +35 -0
  62. package/extensions/subagent/agents/scout.md +50 -0
  63. package/extensions/subagent/agents/worker.md +24 -0
  64. package/extensions/subagent/agents.ts +126 -0
  65. package/extensions/subagent/index.ts +1138 -0
  66. package/extensions/subagent/prompts/implement-and-review.md +10 -0
  67. package/extensions/subagent/prompts/implement.md +10 -0
  68. package/extensions/subagent/prompts/scout-and-plan.md +9 -0
  69. package/hooks.json +10 -0
  70. package/lib/cli.mjs +37 -0
  71. package/package.json +39 -0
  72. package/pi/keybindings.json +3 -0
  73. package/pi/settings.json +29 -0
  74. package/plugin.json +35 -0
  75. package/pnpm-workspace.yaml +7 -0
  76. package/scripts/build-claude-agents.mjs +179 -0
  77. package/scripts/install-instructions.sh +82 -0
  78. package/skills/engineering/design-an-interface/SKILL.md +94 -0
  79. package/skills/engineering/grill-with-docs/SKILL.md +88 -0
  80. package/skills/engineering/improve-codebase-architecture/REFERENCE.md +78 -0
  81. package/skills/engineering/improve-codebase-architecture/SKILL.md +81 -0
  82. package/skills/engineering/tdd/SKILL.md +150 -0
  83. package/skills/engineering/tdd/deep-modules.md +33 -0
  84. package/skills/engineering/tdd/interface-design.md +31 -0
  85. package/skills/engineering/tdd/mocking.md +59 -0
  86. package/skills/engineering/tdd/refactoring.md +10 -0
  87. package/skills/engineering/tdd/tests.md +61 -0
  88. package/skills/engineering/technical-direction/SKILL.md +86 -0
  89. package/skills/planning/classify-bead/SKILL.md +67 -0
  90. package/skills/planning/create-task/SKILL.md +96 -0
  91. package/skills/planning/create-task/pipeline.json +33 -0
  92. package/skills/planning/create-task/scripts/create-chores.ts +112 -0
  93. package/skills/planning/create-task/scripts/package.json +14 -0
  94. package/skills/planning/create-task/scripts/pnpm-lock.yaml +342 -0
  95. package/skills/planning/create-task/scripts/tsconfig.json +12 -0
  96. package/skills/planning/create-task/templates/code.md +40 -0
  97. package/skills/planning/create-task/templates/document.md +40 -0
  98. package/skills/planning/create-task/templates/fix.md +27 -0
  99. package/skills/planning/create-task/templates/review.md +36 -0
  100. package/skills/planning/create-task/templates/verify.md +43 -0
  101. package/skills/planning/prd-to-tasks/SKILL.md +227 -0
  102. package/skills/planning/record-idea/SKILL.md +62 -0
  103. package/skills/planning/record-idea/scripts/new-idea.sh +89 -0
  104. package/skills/planning/request-refactor-plan/SKILL.md +73 -0
  105. package/skills/planning/write-a-prd/SKILL.md +74 -0
  106. package/skills/planning/write-a-ticket/SKILL.md +97 -0
  107. package/skills/productivity/bd-tool/SKILL.md +90 -0
  108. package/skills/productivity/grill-me/SKILL.md +10 -0
  109. package/skills/productivity/handoff/SKILL.md +15 -0
  110. package/skills/productivity/init-beads/SKILL.md +93 -0
  111. package/skills/productivity/teach/GLOSSARY-FORMAT.md +35 -0
  112. package/skills/productivity/teach/LEARNING-RECORD-FORMAT.md +46 -0
  113. package/skills/productivity/teach/MISSION-FORMAT.md +31 -0
  114. package/skills/productivity/teach/RESOURCES-FORMAT.md +32 -0
  115. package/skills/productivity/teach/SKILL.md +109 -0
  116. package/skills/productivity/validate-agent-dir/scripts/package.json +15 -0
  117. package/skills/productivity/validate-agent-dir/scripts/pnpm-lock.yaml +329 -0
  118. package/skills/productivity/validate-agent-dir/scripts/pnpm-workspace.yaml +2 -0
  119. package/skills/productivity/validate-agent-dir/scripts/tsconfig.json +19 -0
  120. package/skills/productivity/validate-agent-dir/scripts/validate-agent-dir.test.ts +317 -0
  121. package/skills/productivity/validate-agent-dir/scripts/validate-agent-dir.ts +127 -0
  122. package/skills/productivity/write-a-skill/SKILL.md +74 -0
  123. package/skills/review/maintain-agent-docs/REFERENCE.md +246 -0
  124. package/skills/review/maintain-agent-docs/SKILL.md +83 -0
  125. package/skills/review/refactor-skill/EXAMPLES.md +20 -0
  126. package/skills/review/refactor-skill/SKILL.md +148 -0
  127. package/skills/review/review-security/SKILL.md +107 -0
  128. package/skills/review/review-security/scripts/install-gitleaks.sh +43 -0
  129. package/skills/style/style-code/REFERENCE.md +38 -0
  130. package/skills/style/style-code/SKILL.md +113 -0
  131. package/skills/style/style-comms/SKILL.md +93 -0
  132. package/skills/style/style-documentation/SKILL.md +112 -0
  133. package/skills/style/style-tests/SKILL.md +97 -0
  134. package/skills/workflow/git-workflow/SKILL.md +85 -0
  135. package/skills/workflow/hitl-collab/SKILL.md +29 -0
  136. package/skills/workflow/plan/SKILL.md +272 -0
  137. package/skills/workflow/ralph/REFERENCE.md +202 -0
  138. package/skills/workflow/ralph/SKILL.md +105 -0
  139. package/skills/workflow/run-pipeline-stage/SKILL.md +151 -0
  140. package/skills/workflow/run-pipeline-stage/playbooks/code.md +17 -0
  141. package/skills/workflow/run-pipeline-stage/playbooks/document.md +18 -0
  142. package/skills/workflow/run-pipeline-stage/playbooks/fix.md +17 -0
  143. package/skills/workflow/run-pipeline-stage/playbooks/review.md +19 -0
  144. package/skills/workflow/run-pipeline-stage/playbooks/verify.md +14 -0
  145. package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +20 -0
  146. package/skills/workflow/run-pipeline-stage/scripts/generate-progress.test.ts +783 -0
  147. package/skills/workflow/run-pipeline-stage/scripts/generate-progress.ts +586 -0
  148. package/skills/workflow/run-pipeline-stage/scripts/package.json +15 -0
  149. package/skills/workflow/run-pipeline-stage/scripts/pnpm-lock.yaml +342 -0
  150. package/skills/workflow/run-pipeline-stage/scripts/pnpm-workspace.yaml +2 -0
  151. package/skills/workflow/run-pipeline-stage/scripts/tsconfig.json +12 -0
  152. package/skills/workflow/using-agent-skills/SKILL.md +109 -0
  153. package/skills/workflow/using-agent-skills/scripts/validate-skill-structure.test.ts +73 -0
  154. package/test/cli.test.mjs +149 -0
@@ -0,0 +1,222 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import path from "node:path";
3
+
4
+ // Re-export public API for tests and consumers
5
+ export { type ProjectCounts, type SkillStats, type UsageStore, type RawEvent, loadStore, saveStore, ageOutStore } from "./store.js";
6
+ export { recordUsage } from "./record.js";
7
+ export { type SkillRecord, globalSkillDirs, scanSkills, extractSkillName, discoverProjectSkills, resolveSkillFromPath } from "./discover.js";
8
+ export { formatTable } from "./format.js";
9
+
10
+ import { loadStore, saveStore } from "./store.js";
11
+ import { recordUsage } from "./record.js";
12
+ import { resolveSkillFromPath, globalSkillDirs, scanSkills, discoverProjectSkills } from "./discover.js";
13
+ import { formatTable } from "./format.js";
14
+
15
+ // ── Extension entry point ─────────────────────────────────────────────────────
16
+
17
+ export default function (pi: ExtensionAPI) {
18
+ // ── Lazy skill map, refreshed on startup and reload ─────────────────────
19
+ let knownSkills = new Map<string, import("./discover.js").SkillRecord>();
20
+
21
+ function refreshSkills(cwd: string): void {
22
+ knownSkills = new Map();
23
+ for (const d of globalSkillDirs()) scanSkills(d, knownSkills);
24
+ discoverProjectSkills(cwd, knownSkills);
25
+ }
26
+
27
+ refreshSkills(process.cwd());
28
+
29
+ // ── 1. Track skills loaded into system prompt ─────────────────────────
30
+ pi.on("before_agent_start", async (event, ctx) => {
31
+ const skills = event.systemPromptOptions?.skills;
32
+ if (!skills?.length) return;
33
+
34
+ const store = loadStore();
35
+ const project = ctx.cwd;
36
+ for (const skill of skills) {
37
+ const name = typeof skill === "string" ? skill : skill.name;
38
+ if (name) recordUsage(store, name, "loaded", project);
39
+ }
40
+ saveStore(store);
41
+ });
42
+
43
+ // ── 2. Track /skill:name invocations ──────────────────────────────────
44
+ pi.on("input", async (event, ctx) => {
45
+ const m = event.text.match(/^\/skill:(\S+)/);
46
+ if (!m) return;
47
+
48
+ const store = loadStore();
49
+ recordUsage(store, m[1], "invoked", ctx.cwd);
50
+ saveStore(store);
51
+ });
52
+
53
+ // ── 3. Track SKILL.md reads by the LLM ────────────────────────────────
54
+ pi.on("tool_call", async (event, ctx) => {
55
+ if (event.toolName !== "read") return;
56
+
57
+ const input = event.input as { path?: string } | undefined;
58
+ const filePath = input?.path;
59
+ if (!filePath) return;
60
+
61
+ const skillName = resolveSkillFromPath(filePath, knownSkills);
62
+ if (!skillName) return;
63
+
64
+ const store = loadStore();
65
+ recordUsage(store, skillName, "read", ctx.cwd);
66
+ saveStore(store);
67
+ });
68
+
69
+ // ── 4. Track turns ────────────────────────────────────────────────────
70
+ // Only count unique turns (not reloads). We use a simple in-memory flag
71
+ // per reload cycle; a `/reload` resets the flag, avoiding double-counts.
72
+ let countedTurnThisCycle = false;
73
+
74
+ pi.on("turn_end", async (_event, _ctx) => {
75
+ if (countedTurnThisCycle) return;
76
+ countedTurnThisCycle = true;
77
+
78
+ const store = loadStore();
79
+ store.totalTurns++;
80
+ saveStore(store);
81
+ });
82
+
83
+ // Reset the flag after reload so the next turn is counted
84
+ pi.on("session_start", async () => {
85
+ countedTurnThisCycle = false;
86
+ });
87
+
88
+ // ── 5. /skill-stats command ───────────────────────────────────────────
89
+ pi.registerCommand("skill-stats", {
90
+ description:
91
+ "Show skill usage stats. Filter by project: /skill-stats --project <path>",
92
+ handler: async (args, ctx) => {
93
+ const store = loadStore();
94
+
95
+ let projectFilter: string | null = null;
96
+ if (args) {
97
+ const m = args.match(/--project\s+(\S+)/);
98
+ if (m) projectFilter = path.resolve(ctx.cwd, m[1]);
99
+ }
100
+
101
+ const skillNames = Object.keys(store.skills).sort();
102
+ if (skillNames.length === 0) {
103
+ ctx.ui.notify("No skill usage recorded yet.", "info");
104
+ return;
105
+ }
106
+
107
+ // Build rows — each row is [skill, loaded, invoked, read, total, lastUsed]
108
+ const rows: string[][] = [];
109
+ for (const name of skillNames) {
110
+ const s = store.skills[name];
111
+
112
+ let loaded: number, invoked: number, read: number, lastUsed: string;
113
+ if (projectFilter) {
114
+ const p = s.byProject[projectFilter];
115
+ if (!p) continue;
116
+ loaded = p.loadedCount;
117
+ invoked = p.invokedCount;
118
+ read = p.readCount;
119
+ lastUsed = p.lastUsed ? new Date(p.lastUsed).toLocaleDateString() : "-";
120
+ } else {
121
+ loaded = s.loadedCount;
122
+ invoked = s.invokedCount;
123
+ read = s.readCount;
124
+ lastUsed = s.lastUsed ? new Date(s.lastUsed).toLocaleDateString() : "-";
125
+ }
126
+
127
+ const total = loaded + invoked + read;
128
+ if (total === 0) continue; // hide zero-usage rows in filtered view
129
+
130
+ rows.push([
131
+ name,
132
+ String(loaded),
133
+ String(invoked),
134
+ String(read),
135
+ String(total),
136
+ lastUsed,
137
+ ]);
138
+ }
139
+
140
+ rows.sort((a, b) => Number(b[4]) - Number(a[4]));
141
+
142
+ // ── Assemble output ──────────────────────────────────────────
143
+ const lines: string[] = [];
144
+
145
+ if (projectFilter) {
146
+ lines.push(`Skill usage for project: ${projectFilter}`);
147
+ lines.push("");
148
+ }
149
+
150
+ lines.push(`Total skills tracked: ${rows.length}`);
151
+ lines.push(`Total agent turns recorded: ${store.totalTurns}`);
152
+ lines.push("");
153
+
154
+ if (rows.length > 0) {
155
+ const top = rows.slice(0, 3);
156
+ lines.push("Top 3:");
157
+ lines.push(...top.map((r, i) => ` ${i + 1}. ${r[0]} (${r[4]} total)`));
158
+ }
159
+
160
+ if (rows.length > 3) {
161
+ lines.push("");
162
+ const bottom = rows.slice(-3);
163
+ lines.push("Bottom 3:");
164
+ lines.push(...bottom.map((r, i) => ` ${i + 1}. ${r[0]} (${r[4]} total)`));
165
+ }
166
+
167
+ lines.push("");
168
+ lines.push(formatTable(
169
+ ["Skill", "Loaded", "Invoked", "Read", "Total", "Last Used"],
170
+ rows,
171
+ ));
172
+ lines.push("");
173
+
174
+ // Show all projects if not filtering
175
+ if (!projectFilter) {
176
+ const allProjects = new Set<string>();
177
+ for (const name of skillNames) {
178
+ for (const proj of Object.keys(store.skills[name].byProject)) {
179
+ allProjects.add(proj);
180
+ }
181
+ }
182
+ if (allProjects.size > 1) {
183
+ lines.push(`Projects with activity (${allProjects.size}):`);
184
+ for (const proj of [...allProjects].sort()) {
185
+ lines.push(` ${proj}`);
186
+ }
187
+ lines.push("");
188
+ lines.push("Filter: /skill-stats --project <path>");
189
+ }
190
+ }
191
+
192
+ const output = lines.join("\n");
193
+ ctx.ui.notify(
194
+ `Skill stats: ${rows.length} skills. Top: ${rows[0]?.[0] ?? "-"}.`,
195
+ "info",
196
+ );
197
+ console.log(output);
198
+ },
199
+ });
200
+
201
+ // ── 6. /skill-usage-reset command ─────────────────────────────────────
202
+ pi.registerCommand("skill-usage-reset", {
203
+ description: "Delete all recorded skill usage data",
204
+ handler: async (_args, ctx) => {
205
+ if (!ctx.hasUI) return;
206
+ const ok = await ctx.ui.confirm(
207
+ "Reset all skill stats?",
208
+ "This deletes ALL recorded usage data across all projects.",
209
+ );
210
+ if (!ok) return;
211
+
212
+ saveStore({
213
+ version: 1,
214
+ skills: {},
215
+ totalTurns: 0,
216
+ lastUpdated: new Date().toISOString(),
217
+ recentEvents: [],
218
+ });
219
+ ctx.ui.notify("Skill usage statistics reset.", "info");
220
+ },
221
+ });
222
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@agent-cortex/skill-stats",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "node --test --import tsx/esm index.test.ts",
8
+ "typecheck": "tsc --noEmit"
9
+ },
10
+ "devDependencies": {
11
+ "@earendil-works/pi-coding-agent": "^0.78.1",
12
+ "@types/node": "^22.0.0",
13
+ "tsx": "^4.0.0",
14
+ "typescript": "^5.0.0"
15
+ },
16
+ "peerDependencies": {
17
+ "@earendil-works/pi-coding-agent": "^0.78.1"
18
+ }
19
+ }
@@ -0,0 +1,329 @@
1
+ lockfileVersion: '9.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ importers:
8
+
9
+ .:
10
+ devDependencies:
11
+ '@types/node':
12
+ specifier: ^22.0.0
13
+ version: 22.19.20
14
+ tsx:
15
+ specifier: ^4.0.0
16
+ version: 4.22.4
17
+ typescript:
18
+ specifier: ^5.0.0
19
+ version: 5.9.3
20
+
21
+ packages:
22
+
23
+ '@esbuild/aix-ppc64@0.28.0':
24
+ resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==}
25
+ engines: {node: '>=18'}
26
+ cpu: [ppc64]
27
+ os: [aix]
28
+
29
+ '@esbuild/android-arm64@0.28.0':
30
+ resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==}
31
+ engines: {node: '>=18'}
32
+ cpu: [arm64]
33
+ os: [android]
34
+
35
+ '@esbuild/android-arm@0.28.0':
36
+ resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==}
37
+ engines: {node: '>=18'}
38
+ cpu: [arm]
39
+ os: [android]
40
+
41
+ '@esbuild/android-x64@0.28.0':
42
+ resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==}
43
+ engines: {node: '>=18'}
44
+ cpu: [x64]
45
+ os: [android]
46
+
47
+ '@esbuild/darwin-arm64@0.28.0':
48
+ resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==}
49
+ engines: {node: '>=18'}
50
+ cpu: [arm64]
51
+ os: [darwin]
52
+
53
+ '@esbuild/darwin-x64@0.28.0':
54
+ resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==}
55
+ engines: {node: '>=18'}
56
+ cpu: [x64]
57
+ os: [darwin]
58
+
59
+ '@esbuild/freebsd-arm64@0.28.0':
60
+ resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==}
61
+ engines: {node: '>=18'}
62
+ cpu: [arm64]
63
+ os: [freebsd]
64
+
65
+ '@esbuild/freebsd-x64@0.28.0':
66
+ resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==}
67
+ engines: {node: '>=18'}
68
+ cpu: [x64]
69
+ os: [freebsd]
70
+
71
+ '@esbuild/linux-arm64@0.28.0':
72
+ resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==}
73
+ engines: {node: '>=18'}
74
+ cpu: [arm64]
75
+ os: [linux]
76
+
77
+ '@esbuild/linux-arm@0.28.0':
78
+ resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==}
79
+ engines: {node: '>=18'}
80
+ cpu: [arm]
81
+ os: [linux]
82
+
83
+ '@esbuild/linux-ia32@0.28.0':
84
+ resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==}
85
+ engines: {node: '>=18'}
86
+ cpu: [ia32]
87
+ os: [linux]
88
+
89
+ '@esbuild/linux-loong64@0.28.0':
90
+ resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==}
91
+ engines: {node: '>=18'}
92
+ cpu: [loong64]
93
+ os: [linux]
94
+
95
+ '@esbuild/linux-mips64el@0.28.0':
96
+ resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==}
97
+ engines: {node: '>=18'}
98
+ cpu: [mips64el]
99
+ os: [linux]
100
+
101
+ '@esbuild/linux-ppc64@0.28.0':
102
+ resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==}
103
+ engines: {node: '>=18'}
104
+ cpu: [ppc64]
105
+ os: [linux]
106
+
107
+ '@esbuild/linux-riscv64@0.28.0':
108
+ resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==}
109
+ engines: {node: '>=18'}
110
+ cpu: [riscv64]
111
+ os: [linux]
112
+
113
+ '@esbuild/linux-s390x@0.28.0':
114
+ resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==}
115
+ engines: {node: '>=18'}
116
+ cpu: [s390x]
117
+ os: [linux]
118
+
119
+ '@esbuild/linux-x64@0.28.0':
120
+ resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==}
121
+ engines: {node: '>=18'}
122
+ cpu: [x64]
123
+ os: [linux]
124
+
125
+ '@esbuild/netbsd-arm64@0.28.0':
126
+ resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==}
127
+ engines: {node: '>=18'}
128
+ cpu: [arm64]
129
+ os: [netbsd]
130
+
131
+ '@esbuild/netbsd-x64@0.28.0':
132
+ resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==}
133
+ engines: {node: '>=18'}
134
+ cpu: [x64]
135
+ os: [netbsd]
136
+
137
+ '@esbuild/openbsd-arm64@0.28.0':
138
+ resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==}
139
+ engines: {node: '>=18'}
140
+ cpu: [arm64]
141
+ os: [openbsd]
142
+
143
+ '@esbuild/openbsd-x64@0.28.0':
144
+ resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==}
145
+ engines: {node: '>=18'}
146
+ cpu: [x64]
147
+ os: [openbsd]
148
+
149
+ '@esbuild/openharmony-arm64@0.28.0':
150
+ resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==}
151
+ engines: {node: '>=18'}
152
+ cpu: [arm64]
153
+ os: [openharmony]
154
+
155
+ '@esbuild/sunos-x64@0.28.0':
156
+ resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==}
157
+ engines: {node: '>=18'}
158
+ cpu: [x64]
159
+ os: [sunos]
160
+
161
+ '@esbuild/win32-arm64@0.28.0':
162
+ resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==}
163
+ engines: {node: '>=18'}
164
+ cpu: [arm64]
165
+ os: [win32]
166
+
167
+ '@esbuild/win32-ia32@0.28.0':
168
+ resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==}
169
+ engines: {node: '>=18'}
170
+ cpu: [ia32]
171
+ os: [win32]
172
+
173
+ '@esbuild/win32-x64@0.28.0':
174
+ resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==}
175
+ engines: {node: '>=18'}
176
+ cpu: [x64]
177
+ os: [win32]
178
+
179
+ '@types/node@22.19.20':
180
+ resolution: {integrity: sha512-6tELRwSDYWW9EdZhbeZmYGZ1/7Djkt+Ah3/ScEYT9cDord7UJzasR/4D3VONg9tQI5CDp+/CZC1AXj2pCFOvpw==}
181
+
182
+ esbuild@0.28.0:
183
+ resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==}
184
+ engines: {node: '>=18'}
185
+ hasBin: true
186
+
187
+ fsevents@2.3.3:
188
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
189
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
190
+ os: [darwin]
191
+
192
+ tsx@4.22.4:
193
+ resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==}
194
+ engines: {node: '>=18.0.0'}
195
+ hasBin: true
196
+
197
+ typescript@5.9.3:
198
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
199
+ engines: {node: '>=14.17'}
200
+ hasBin: true
201
+
202
+ undici-types@6.21.0:
203
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
204
+
205
+ snapshots:
206
+
207
+ '@esbuild/aix-ppc64@0.28.0':
208
+ optional: true
209
+
210
+ '@esbuild/android-arm64@0.28.0':
211
+ optional: true
212
+
213
+ '@esbuild/android-arm@0.28.0':
214
+ optional: true
215
+
216
+ '@esbuild/android-x64@0.28.0':
217
+ optional: true
218
+
219
+ '@esbuild/darwin-arm64@0.28.0':
220
+ optional: true
221
+
222
+ '@esbuild/darwin-x64@0.28.0':
223
+ optional: true
224
+
225
+ '@esbuild/freebsd-arm64@0.28.0':
226
+ optional: true
227
+
228
+ '@esbuild/freebsd-x64@0.28.0':
229
+ optional: true
230
+
231
+ '@esbuild/linux-arm64@0.28.0':
232
+ optional: true
233
+
234
+ '@esbuild/linux-arm@0.28.0':
235
+ optional: true
236
+
237
+ '@esbuild/linux-ia32@0.28.0':
238
+ optional: true
239
+
240
+ '@esbuild/linux-loong64@0.28.0':
241
+ optional: true
242
+
243
+ '@esbuild/linux-mips64el@0.28.0':
244
+ optional: true
245
+
246
+ '@esbuild/linux-ppc64@0.28.0':
247
+ optional: true
248
+
249
+ '@esbuild/linux-riscv64@0.28.0':
250
+ optional: true
251
+
252
+ '@esbuild/linux-s390x@0.28.0':
253
+ optional: true
254
+
255
+ '@esbuild/linux-x64@0.28.0':
256
+ optional: true
257
+
258
+ '@esbuild/netbsd-arm64@0.28.0':
259
+ optional: true
260
+
261
+ '@esbuild/netbsd-x64@0.28.0':
262
+ optional: true
263
+
264
+ '@esbuild/openbsd-arm64@0.28.0':
265
+ optional: true
266
+
267
+ '@esbuild/openbsd-x64@0.28.0':
268
+ optional: true
269
+
270
+ '@esbuild/openharmony-arm64@0.28.0':
271
+ optional: true
272
+
273
+ '@esbuild/sunos-x64@0.28.0':
274
+ optional: true
275
+
276
+ '@esbuild/win32-arm64@0.28.0':
277
+ optional: true
278
+
279
+ '@esbuild/win32-ia32@0.28.0':
280
+ optional: true
281
+
282
+ '@esbuild/win32-x64@0.28.0':
283
+ optional: true
284
+
285
+ '@types/node@22.19.20':
286
+ dependencies:
287
+ undici-types: 6.21.0
288
+
289
+ esbuild@0.28.0:
290
+ optionalDependencies:
291
+ '@esbuild/aix-ppc64': 0.28.0
292
+ '@esbuild/android-arm': 0.28.0
293
+ '@esbuild/android-arm64': 0.28.0
294
+ '@esbuild/android-x64': 0.28.0
295
+ '@esbuild/darwin-arm64': 0.28.0
296
+ '@esbuild/darwin-x64': 0.28.0
297
+ '@esbuild/freebsd-arm64': 0.28.0
298
+ '@esbuild/freebsd-x64': 0.28.0
299
+ '@esbuild/linux-arm': 0.28.0
300
+ '@esbuild/linux-arm64': 0.28.0
301
+ '@esbuild/linux-ia32': 0.28.0
302
+ '@esbuild/linux-loong64': 0.28.0
303
+ '@esbuild/linux-mips64el': 0.28.0
304
+ '@esbuild/linux-ppc64': 0.28.0
305
+ '@esbuild/linux-riscv64': 0.28.0
306
+ '@esbuild/linux-s390x': 0.28.0
307
+ '@esbuild/linux-x64': 0.28.0
308
+ '@esbuild/netbsd-arm64': 0.28.0
309
+ '@esbuild/netbsd-x64': 0.28.0
310
+ '@esbuild/openbsd-arm64': 0.28.0
311
+ '@esbuild/openbsd-x64': 0.28.0
312
+ '@esbuild/openharmony-arm64': 0.28.0
313
+ '@esbuild/sunos-x64': 0.28.0
314
+ '@esbuild/win32-arm64': 0.28.0
315
+ '@esbuild/win32-ia32': 0.28.0
316
+ '@esbuild/win32-x64': 0.28.0
317
+
318
+ fsevents@2.3.3:
319
+ optional: true
320
+
321
+ tsx@4.22.4:
322
+ dependencies:
323
+ esbuild: 0.28.0
324
+ optionalDependencies:
325
+ fsevents: 2.3.3
326
+
327
+ typescript@5.9.3: {}
328
+
329
+ undici-types@6.21.0: {}
@@ -0,0 +1,48 @@
1
+ import type { UsageStore, RawEvent } from "./store.js";
2
+
3
+ // ── Event recording ──────────────────────────────────────────────────────────
4
+
5
+ export function recordUsage(
6
+ store: UsageStore,
7
+ skillName: string,
8
+ source: RawEvent["source"],
9
+ project: string,
10
+ ): void {
11
+ let skill = store.skills[skillName];
12
+ if (!skill) {
13
+ skill = {
14
+ loadedCount: 0,
15
+ invokedCount: 0,
16
+ readCount: 0,
17
+ lastUsed: null,
18
+ byProject: {},
19
+ };
20
+ store.skills[skillName] = skill;
21
+ }
22
+
23
+ const now = new Date().toISOString();
24
+
25
+ // Global counters
26
+ switch (source) {
27
+ case "loaded": skill.loadedCount++; break;
28
+ case "invoked": skill.invokedCount++; break;
29
+ case "read": skill.readCount++; break;
30
+ }
31
+ skill.lastUsed = now;
32
+
33
+ // Per-project counters
34
+ let proj = skill.byProject[project];
35
+ if (!proj) {
36
+ proj = { loadedCount: 0, invokedCount: 0, readCount: 0, lastUsed: null };
37
+ skill.byProject[project] = proj;
38
+ }
39
+ switch (source) {
40
+ case "loaded": proj.loadedCount++; break;
41
+ case "invoked": proj.invokedCount++; break;
42
+ case "read": proj.readCount++; break;
43
+ }
44
+ proj.lastUsed = now;
45
+
46
+ // Event log
47
+ store.recentEvents.push({ timestamp: now, skill: skillName, source, project });
48
+ }