@runchr/gstack-antigravity 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.
Files changed (297) hide show
  1. package/.agents/rules/ETHOS.md +129 -0
  2. package/.agents/rules/global-gstack.md +117 -0
  3. package/.agents/rules/persona-gstack-autoplan.md +14 -0
  4. package/.agents/rules/persona-gstack-benchmark.md +14 -0
  5. package/.agents/rules/persona-gstack-browse.md +14 -0
  6. package/.agents/rules/persona-gstack-canary.md +14 -0
  7. package/.agents/rules/persona-gstack-careful.md +14 -0
  8. package/.agents/rules/persona-gstack-codex.md +14 -0
  9. package/.agents/rules/persona-gstack-cso.md +14 -0
  10. package/.agents/rules/persona-gstack-design-consultation.md +14 -0
  11. package/.agents/rules/persona-gstack-design-review.md +14 -0
  12. package/.agents/rules/persona-gstack-document-release.md +14 -0
  13. package/.agents/rules/persona-gstack-freeze.md +14 -0
  14. package/.agents/rules/persona-gstack-gstack-upgrade.md +14 -0
  15. package/.agents/rules/persona-gstack-guard.md +14 -0
  16. package/.agents/rules/persona-gstack-investigate.md +14 -0
  17. package/.agents/rules/persona-gstack-land-and-deploy.md +14 -0
  18. package/.agents/rules/persona-gstack-office-hours.md +14 -0
  19. package/.agents/rules/persona-gstack-plan-ceo-review.md +14 -0
  20. package/.agents/rules/persona-gstack-plan-design-review.md +14 -0
  21. package/.agents/rules/persona-gstack-plan-eng-review.md +14 -0
  22. package/.agents/rules/persona-gstack-qa-only.md +14 -0
  23. package/.agents/rules/persona-gstack-qa.md +14 -0
  24. package/.agents/rules/persona-gstack-retro.md +14 -0
  25. package/.agents/rules/persona-gstack-review.md +14 -0
  26. package/.agents/rules/persona-gstack-setup-browser-cookies.md +14 -0
  27. package/.agents/rules/persona-gstack-setup-deploy.md +14 -0
  28. package/.agents/rules/persona-gstack-ship.md +14 -0
  29. package/.agents/rules/persona-gstack-unfreeze.md +14 -0
  30. package/.agents/rules/persona-gstack.md +40 -0
  31. package/.agents/rules/recursive-identities.md +22 -0
  32. package/.agents/workflows/autoplan.md +30 -0
  33. package/.agents/workflows/benchmark.md +31 -0
  34. package/.agents/workflows/browse.md +26 -0
  35. package/.agents/workflows/canary.md +33 -0
  36. package/.agents/workflows/careful.md +22 -0
  37. package/.agents/workflows/codex.md +36 -0
  38. package/.agents/workflows/cso.md +29 -0
  39. package/.agents/workflows/design-consultation.md +28 -0
  40. package/.agents/workflows/design-review.md +28 -0
  41. package/.agents/workflows/document-release.md +32 -0
  42. package/.agents/workflows/freeze.md +17 -0
  43. package/.agents/workflows/gstack-upgrade.md +54 -0
  44. package/.agents/workflows/gstack.md +56 -0
  45. package/.agents/workflows/guard.md +18 -0
  46. package/.agents/workflows/investigate.md +37 -0
  47. package/.agents/workflows/land-and-deploy.md +35 -0
  48. package/.agents/workflows/office-hours.md +27 -0
  49. package/.agents/workflows/plan-ceo-review.md +34 -0
  50. package/.agents/workflows/plan-design-review.md +31 -0
  51. package/.agents/workflows/plan-eng-review.md +28 -0
  52. package/.agents/workflows/qa-only.md +28 -0
  53. package/.agents/workflows/qa.md +73 -0
  54. package/.agents/workflows/retro.md +34 -0
  55. package/.agents/workflows/review.md +30 -0
  56. package/.agents/workflows/setup-browser-cookies.md +15 -0
  57. package/.agents/workflows/setup-cookies.md +8 -0
  58. package/.agents/workflows/setup-deploy.md +21 -0
  59. package/.agents/workflows/ship.md +93 -0
  60. package/.agents/workflows/unfreeze.md +12 -0
  61. package/LICENSE +22 -0
  62. package/README.md +189 -0
  63. package/README_KO.md +191 -0
  64. package/bin/install.js +105 -0
  65. package/gstack-origin/.agents/skills/gstack/SKILL.md +651 -0
  66. package/gstack-origin/.agents/skills/gstack-autoplan/SKILL.md +678 -0
  67. package/gstack-origin/.agents/skills/gstack-benchmark/SKILL.md +482 -0
  68. package/gstack-origin/.agents/skills/gstack-browse/SKILL.md +511 -0
  69. package/gstack-origin/.agents/skills/gstack-canary/SKILL.md +486 -0
  70. package/gstack-origin/.agents/skills/gstack-careful/SKILL.md +50 -0
  71. package/gstack-origin/.agents/skills/gstack-cso/SKILL.md +607 -0
  72. package/gstack-origin/.agents/skills/gstack-design-consultation/SKILL.md +615 -0
  73. package/gstack-origin/.agents/skills/gstack-design-review/SKILL.md +988 -0
  74. package/gstack-origin/.agents/skills/gstack-document-release/SKILL.md +604 -0
  75. package/gstack-origin/.agents/skills/gstack-freeze/SKILL.md +67 -0
  76. package/gstack-origin/.agents/skills/gstack-guard/SKILL.md +62 -0
  77. package/gstack-origin/.agents/skills/gstack-investigate/SKILL.md +415 -0
  78. package/gstack-origin/.agents/skills/gstack-land-and-deploy/SKILL.md +873 -0
  79. package/gstack-origin/.agents/skills/gstack-office-hours/SKILL.md +986 -0
  80. package/gstack-origin/.agents/skills/gstack-plan-ceo-review/SKILL.md +1268 -0
  81. package/gstack-origin/.agents/skills/gstack-plan-design-review/SKILL.md +668 -0
  82. package/gstack-origin/.agents/skills/gstack-plan-eng-review/SKILL.md +826 -0
  83. package/gstack-origin/.agents/skills/gstack-qa/SKILL.md +1006 -0
  84. package/gstack-origin/.agents/skills/gstack-qa-only/SKILL.md +626 -0
  85. package/gstack-origin/.agents/skills/gstack-retro/SKILL.md +1065 -0
  86. package/gstack-origin/.agents/skills/gstack-review/SKILL.md +704 -0
  87. package/gstack-origin/.agents/skills/gstack-setup-browser-cookies/SKILL.md +325 -0
  88. package/gstack-origin/.agents/skills/gstack-setup-deploy/SKILL.md +450 -0
  89. package/gstack-origin/.agents/skills/gstack-ship/SKILL.md +1312 -0
  90. package/gstack-origin/.agents/skills/gstack-unfreeze/SKILL.md +36 -0
  91. package/gstack-origin/.agents/skills/gstack-upgrade/SKILL.md +220 -0
  92. package/gstack-origin/.env.example +5 -0
  93. package/gstack-origin/.github/workflows/skill-docs.yml +17 -0
  94. package/gstack-origin/AGENTS.md +49 -0
  95. package/gstack-origin/ARCHITECTURE.md +359 -0
  96. package/gstack-origin/BROWSER.md +271 -0
  97. package/gstack-origin/CHANGELOG.md +800 -0
  98. package/gstack-origin/CLAUDE.md +284 -0
  99. package/gstack-origin/CONTRIBUTING.md +370 -0
  100. package/gstack-origin/ETHOS.md +129 -0
  101. package/gstack-origin/LICENSE +21 -0
  102. package/gstack-origin/README.md +228 -0
  103. package/gstack-origin/SKILL.md +657 -0
  104. package/gstack-origin/SKILL.md.tmpl +281 -0
  105. package/gstack-origin/TODOS.md +564 -0
  106. package/gstack-origin/VERSION +1 -0
  107. package/gstack-origin/autoplan/SKILL.md +689 -0
  108. package/gstack-origin/autoplan/SKILL.md.tmpl +416 -0
  109. package/gstack-origin/benchmark/SKILL.md +489 -0
  110. package/gstack-origin/benchmark/SKILL.md.tmpl +233 -0
  111. package/gstack-origin/bin/dev-setup +68 -0
  112. package/gstack-origin/bin/dev-teardown +56 -0
  113. package/gstack-origin/bin/gstack-analytics +191 -0
  114. package/gstack-origin/bin/gstack-community-dashboard +113 -0
  115. package/gstack-origin/bin/gstack-config +38 -0
  116. package/gstack-origin/bin/gstack-diff-scope +71 -0
  117. package/gstack-origin/bin/gstack-global-discover.ts +591 -0
  118. package/gstack-origin/bin/gstack-repo-mode +93 -0
  119. package/gstack-origin/bin/gstack-review-log +9 -0
  120. package/gstack-origin/bin/gstack-review-read +12 -0
  121. package/gstack-origin/bin/gstack-slug +15 -0
  122. package/gstack-origin/bin/gstack-telemetry-log +158 -0
  123. package/gstack-origin/bin/gstack-telemetry-sync +127 -0
  124. package/gstack-origin/bin/gstack-update-check +196 -0
  125. package/gstack-origin/browse/SKILL.md +517 -0
  126. package/gstack-origin/browse/SKILL.md.tmpl +141 -0
  127. package/gstack-origin/browse/bin/find-browse +21 -0
  128. package/gstack-origin/browse/bin/remote-slug +14 -0
  129. package/gstack-origin/browse/scripts/build-node-server.sh +48 -0
  130. package/gstack-origin/browse/src/browser-manager.ts +634 -0
  131. package/gstack-origin/browse/src/buffers.ts +137 -0
  132. package/gstack-origin/browse/src/bun-polyfill.cjs +109 -0
  133. package/gstack-origin/browse/src/cli.ts +420 -0
  134. package/gstack-origin/browse/src/commands.ts +111 -0
  135. package/gstack-origin/browse/src/config.ts +150 -0
  136. package/gstack-origin/browse/src/cookie-import-browser.ts +417 -0
  137. package/gstack-origin/browse/src/cookie-picker-routes.ts +207 -0
  138. package/gstack-origin/browse/src/cookie-picker-ui.ts +541 -0
  139. package/gstack-origin/browse/src/find-browse.ts +61 -0
  140. package/gstack-origin/browse/src/meta-commands.ts +269 -0
  141. package/gstack-origin/browse/src/platform.ts +17 -0
  142. package/gstack-origin/browse/src/read-commands.ts +335 -0
  143. package/gstack-origin/browse/src/server.ts +369 -0
  144. package/gstack-origin/browse/src/snapshot.ts +398 -0
  145. package/gstack-origin/browse/src/url-validation.ts +91 -0
  146. package/gstack-origin/browse/src/write-commands.ts +352 -0
  147. package/gstack-origin/browse/test/bun-polyfill.test.ts +72 -0
  148. package/gstack-origin/browse/test/commands.test.ts +1836 -0
  149. package/gstack-origin/browse/test/config.test.ts +250 -0
  150. package/gstack-origin/browse/test/cookie-import-browser.test.ts +397 -0
  151. package/gstack-origin/browse/test/cookie-picker-routes.test.ts +205 -0
  152. package/gstack-origin/browse/test/find-browse.test.ts +50 -0
  153. package/gstack-origin/browse/test/fixtures/basic.html +33 -0
  154. package/gstack-origin/browse/test/fixtures/cursor-interactive.html +22 -0
  155. package/gstack-origin/browse/test/fixtures/dialog.html +15 -0
  156. package/gstack-origin/browse/test/fixtures/empty.html +2 -0
  157. package/gstack-origin/browse/test/fixtures/forms.html +55 -0
  158. package/gstack-origin/browse/test/fixtures/qa-eval-checkout.html +108 -0
  159. package/gstack-origin/browse/test/fixtures/qa-eval-spa.html +98 -0
  160. package/gstack-origin/browse/test/fixtures/qa-eval.html +51 -0
  161. package/gstack-origin/browse/test/fixtures/responsive.html +49 -0
  162. package/gstack-origin/browse/test/fixtures/snapshot.html +55 -0
  163. package/gstack-origin/browse/test/fixtures/spa.html +24 -0
  164. package/gstack-origin/browse/test/fixtures/states.html +17 -0
  165. package/gstack-origin/browse/test/fixtures/upload.html +25 -0
  166. package/gstack-origin/browse/test/gstack-config.test.ts +125 -0
  167. package/gstack-origin/browse/test/gstack-update-check.test.ts +467 -0
  168. package/gstack-origin/browse/test/handoff.test.ts +235 -0
  169. package/gstack-origin/browse/test/path-validation.test.ts +63 -0
  170. package/gstack-origin/browse/test/platform.test.ts +37 -0
  171. package/gstack-origin/browse/test/snapshot.test.ts +467 -0
  172. package/gstack-origin/browse/test/test-server.ts +57 -0
  173. package/gstack-origin/browse/test/url-validation.test.ts +72 -0
  174. package/gstack-origin/canary/SKILL.md +493 -0
  175. package/gstack-origin/canary/SKILL.md.tmpl +220 -0
  176. package/gstack-origin/careful/SKILL.md +59 -0
  177. package/gstack-origin/careful/SKILL.md.tmpl +57 -0
  178. package/gstack-origin/careful/bin/check-careful.sh +112 -0
  179. package/gstack-origin/codex/SKILL.md +677 -0
  180. package/gstack-origin/codex/SKILL.md.tmpl +356 -0
  181. package/gstack-origin/conductor.json +6 -0
  182. package/gstack-origin/cso/SKILL.md +615 -0
  183. package/gstack-origin/cso/SKILL.md.tmpl +376 -0
  184. package/gstack-origin/design-consultation/SKILL.md +625 -0
  185. package/gstack-origin/design-consultation/SKILL.md.tmpl +369 -0
  186. package/gstack-origin/design-review/SKILL.md +998 -0
  187. package/gstack-origin/design-review/SKILL.md.tmpl +262 -0
  188. package/gstack-origin/docs/images/github-2013.png +0 -0
  189. package/gstack-origin/docs/images/github-2026.png +0 -0
  190. package/gstack-origin/docs/skills.md +877 -0
  191. package/gstack-origin/document-release/SKILL.md +613 -0
  192. package/gstack-origin/document-release/SKILL.md.tmpl +357 -0
  193. package/gstack-origin/freeze/SKILL.md +82 -0
  194. package/gstack-origin/freeze/SKILL.md.tmpl +80 -0
  195. package/gstack-origin/freeze/bin/check-freeze.sh +68 -0
  196. package/gstack-origin/gstack-upgrade/SKILL.md +226 -0
  197. package/gstack-origin/gstack-upgrade/SKILL.md.tmpl +224 -0
  198. package/gstack-origin/guard/SKILL.md +82 -0
  199. package/gstack-origin/guard/SKILL.md.tmpl +80 -0
  200. package/gstack-origin/investigate/SKILL.md +435 -0
  201. package/gstack-origin/investigate/SKILL.md.tmpl +196 -0
  202. package/gstack-origin/land-and-deploy/SKILL.md +880 -0
  203. package/gstack-origin/land-and-deploy/SKILL.md.tmpl +575 -0
  204. package/gstack-origin/office-hours/SKILL.md +996 -0
  205. package/gstack-origin/office-hours/SKILL.md.tmpl +624 -0
  206. package/gstack-origin/package.json +55 -0
  207. package/gstack-origin/plan-ceo-review/SKILL.md +1277 -0
  208. package/gstack-origin/plan-ceo-review/SKILL.md.tmpl +838 -0
  209. package/gstack-origin/plan-design-review/SKILL.md +676 -0
  210. package/gstack-origin/plan-design-review/SKILL.md.tmpl +314 -0
  211. package/gstack-origin/plan-eng-review/SKILL.md +836 -0
  212. package/gstack-origin/plan-eng-review/SKILL.md.tmpl +279 -0
  213. package/gstack-origin/qa/SKILL.md +1016 -0
  214. package/gstack-origin/qa/SKILL.md.tmpl +316 -0
  215. package/gstack-origin/qa/references/issue-taxonomy.md +85 -0
  216. package/gstack-origin/qa/templates/qa-report-template.md +126 -0
  217. package/gstack-origin/qa-only/SKILL.md +633 -0
  218. package/gstack-origin/qa-only/SKILL.md.tmpl +101 -0
  219. package/gstack-origin/retro/SKILL.md +1072 -0
  220. package/gstack-origin/retro/SKILL.md.tmpl +833 -0
  221. package/gstack-origin/review/SKILL.md +849 -0
  222. package/gstack-origin/review/SKILL.md.tmpl +259 -0
  223. package/gstack-origin/review/TODOS-format.md +62 -0
  224. package/gstack-origin/review/checklist.md +190 -0
  225. package/gstack-origin/review/design-checklist.md +132 -0
  226. package/gstack-origin/review/greptile-triage.md +220 -0
  227. package/gstack-origin/scripts/analytics.ts +190 -0
  228. package/gstack-origin/scripts/dev-skill.ts +82 -0
  229. package/gstack-origin/scripts/eval-compare.ts +96 -0
  230. package/gstack-origin/scripts/eval-list.ts +116 -0
  231. package/gstack-origin/scripts/eval-select.ts +86 -0
  232. package/gstack-origin/scripts/eval-summary.ts +187 -0
  233. package/gstack-origin/scripts/eval-watch.ts +172 -0
  234. package/gstack-origin/scripts/gen-skill-docs.ts +2414 -0
  235. package/gstack-origin/scripts/skill-check.ts +167 -0
  236. package/gstack-origin/setup +269 -0
  237. package/gstack-origin/setup-browser-cookies/SKILL.md +330 -0
  238. package/gstack-origin/setup-browser-cookies/SKILL.md.tmpl +74 -0
  239. package/gstack-origin/setup-deploy/SKILL.md +459 -0
  240. package/gstack-origin/setup-deploy/SKILL.md.tmpl +220 -0
  241. package/gstack-origin/ship/SKILL.md +1457 -0
  242. package/gstack-origin/ship/SKILL.md.tmpl +528 -0
  243. package/gstack-origin/supabase/config.sh +10 -0
  244. package/gstack-origin/supabase/functions/community-pulse/index.ts +59 -0
  245. package/gstack-origin/supabase/functions/telemetry-ingest/index.ts +135 -0
  246. package/gstack-origin/supabase/functions/update-check/index.ts +37 -0
  247. package/gstack-origin/supabase/migrations/001_telemetry.sql +89 -0
  248. package/gstack-origin/test/analytics.test.ts +277 -0
  249. package/gstack-origin/test/codex-e2e.test.ts +197 -0
  250. package/gstack-origin/test/fixtures/coverage-audit-fixture.ts +76 -0
  251. package/gstack-origin/test/fixtures/eval-baselines.json +7 -0
  252. package/gstack-origin/test/fixtures/qa-eval-checkout-ground-truth.json +43 -0
  253. package/gstack-origin/test/fixtures/qa-eval-ground-truth.json +43 -0
  254. package/gstack-origin/test/fixtures/qa-eval-spa-ground-truth.json +43 -0
  255. package/gstack-origin/test/fixtures/review-eval-design-slop.css +86 -0
  256. package/gstack-origin/test/fixtures/review-eval-design-slop.html +41 -0
  257. package/gstack-origin/test/fixtures/review-eval-enum-diff.rb +30 -0
  258. package/gstack-origin/test/fixtures/review-eval-enum.rb +27 -0
  259. package/gstack-origin/test/fixtures/review-eval-vuln.rb +14 -0
  260. package/gstack-origin/test/gemini-e2e.test.ts +173 -0
  261. package/gstack-origin/test/gen-skill-docs.test.ts +1049 -0
  262. package/gstack-origin/test/global-discover.test.ts +187 -0
  263. package/gstack-origin/test/helpers/codex-session-runner.ts +282 -0
  264. package/gstack-origin/test/helpers/e2e-helpers.ts +239 -0
  265. package/gstack-origin/test/helpers/eval-store.test.ts +548 -0
  266. package/gstack-origin/test/helpers/eval-store.ts +689 -0
  267. package/gstack-origin/test/helpers/gemini-session-runner.test.ts +104 -0
  268. package/gstack-origin/test/helpers/gemini-session-runner.ts +201 -0
  269. package/gstack-origin/test/helpers/llm-judge.ts +130 -0
  270. package/gstack-origin/test/helpers/observability.test.ts +283 -0
  271. package/gstack-origin/test/helpers/session-runner.test.ts +96 -0
  272. package/gstack-origin/test/helpers/session-runner.ts +357 -0
  273. package/gstack-origin/test/helpers/skill-parser.ts +206 -0
  274. package/gstack-origin/test/helpers/touchfiles.ts +260 -0
  275. package/gstack-origin/test/hook-scripts.test.ts +373 -0
  276. package/gstack-origin/test/skill-e2e-browse.test.ts +293 -0
  277. package/gstack-origin/test/skill-e2e-deploy.test.ts +279 -0
  278. package/gstack-origin/test/skill-e2e-design.test.ts +614 -0
  279. package/gstack-origin/test/skill-e2e-plan.test.ts +538 -0
  280. package/gstack-origin/test/skill-e2e-qa-bugs.test.ts +194 -0
  281. package/gstack-origin/test/skill-e2e-qa-workflow.test.ts +412 -0
  282. package/gstack-origin/test/skill-e2e-review.test.ts +535 -0
  283. package/gstack-origin/test/skill-e2e-workflow.test.ts +586 -0
  284. package/gstack-origin/test/skill-e2e.test.ts +3325 -0
  285. package/gstack-origin/test/skill-llm-eval.test.ts +787 -0
  286. package/gstack-origin/test/skill-parser.test.ts +179 -0
  287. package/gstack-origin/test/skill-routing-e2e.test.ts +605 -0
  288. package/gstack-origin/test/skill-validation.test.ts +1520 -0
  289. package/gstack-origin/test/telemetry.test.ts +278 -0
  290. package/gstack-origin/test/touchfiles.test.ts +262 -0
  291. package/gstack-origin/unfreeze/SKILL.md +40 -0
  292. package/gstack-origin/unfreeze/SKILL.md.tmpl +38 -0
  293. package/package.json +38 -0
  294. package/scripts/install-antigravity-skill.ps1 +33 -0
  295. package/scripts/install-antigravity-skill.sh +41 -0
  296. package/scripts/sync-gstack-origin.ps1 +37 -0
  297. package/scripts/sync-gstack-origin.sh +35 -0
@@ -0,0 +1,278 @@
1
+ import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
2
+ import { execSync } from 'child_process';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import * as os from 'os';
6
+
7
+ const ROOT = path.resolve(import.meta.dir, '..');
8
+ const BIN = path.join(ROOT, 'bin');
9
+
10
+ // Each test gets a fresh temp directory for GSTACK_STATE_DIR
11
+ let tmpDir: string;
12
+
13
+ function run(cmd: string, env: Record<string, string> = {}): string {
14
+ return execSync(cmd, {
15
+ cwd: ROOT,
16
+ env: { ...process.env, GSTACK_STATE_DIR: tmpDir, GSTACK_DIR: ROOT, ...env },
17
+ encoding: 'utf-8',
18
+ timeout: 10000,
19
+ }).trim();
20
+ }
21
+
22
+ function setConfig(key: string, value: string) {
23
+ run(`${BIN}/gstack-config set ${key} ${value}`);
24
+ }
25
+
26
+ function readJsonl(): string[] {
27
+ const file = path.join(tmpDir, 'analytics', 'skill-usage.jsonl');
28
+ if (!fs.existsSync(file)) return [];
29
+ return fs.readFileSync(file, 'utf-8').trim().split('\n').filter(Boolean);
30
+ }
31
+
32
+ function parseJsonl(): any[] {
33
+ return readJsonl().map(line => JSON.parse(line));
34
+ }
35
+
36
+ beforeEach(() => {
37
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-tel-'));
38
+ });
39
+
40
+ afterEach(() => {
41
+ fs.rmSync(tmpDir, { recursive: true, force: true });
42
+ });
43
+
44
+ describe('gstack-telemetry-log', () => {
45
+ test('appends valid JSONL when tier=anonymous', () => {
46
+ setConfig('telemetry', 'anonymous');
47
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 142 --outcome success --session-id test-123`);
48
+
49
+ const events = parseJsonl();
50
+ expect(events).toHaveLength(1);
51
+ expect(events[0].v).toBe(1);
52
+ expect(events[0].skill).toBe('qa');
53
+ expect(events[0].duration_s).toBe(142);
54
+ expect(events[0].outcome).toBe('success');
55
+ expect(events[0].session_id).toBe('test-123');
56
+ expect(events[0].event_type).toBe('skill_run');
57
+ expect(events[0].os).toBeTruthy();
58
+ expect(events[0].gstack_version).toBeTruthy();
59
+ });
60
+
61
+ test('produces no output when tier=off', () => {
62
+ setConfig('telemetry', 'off');
63
+ run(`${BIN}/gstack-telemetry-log --skill ship --duration 30 --outcome success --session-id test-456`);
64
+
65
+ expect(readJsonl()).toHaveLength(0);
66
+ });
67
+
68
+ test('defaults to off for invalid tier value', () => {
69
+ setConfig('telemetry', 'invalid_value');
70
+ run(`${BIN}/gstack-telemetry-log --skill ship --duration 30 --outcome success --session-id test-789`);
71
+
72
+ expect(readJsonl()).toHaveLength(0);
73
+ });
74
+
75
+ test('includes installation_id for community tier', () => {
76
+ setConfig('telemetry', 'community');
77
+ run(`${BIN}/gstack-telemetry-log --skill review --duration 100 --outcome success --session-id comm-123`);
78
+
79
+ const events = parseJsonl();
80
+ expect(events).toHaveLength(1);
81
+ // installation_id should be a SHA-256 hash (64 hex chars)
82
+ expect(events[0].installation_id).toMatch(/^[a-f0-9]{64}$/);
83
+ });
84
+
85
+ test('installation_id is null for anonymous tier', () => {
86
+ setConfig('telemetry', 'anonymous');
87
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 50 --outcome success --session-id anon-123`);
88
+
89
+ const events = parseJsonl();
90
+ expect(events[0].installation_id).toBeNull();
91
+ });
92
+
93
+ test('includes error_class when provided', () => {
94
+ setConfig('telemetry', 'anonymous');
95
+ run(`${BIN}/gstack-telemetry-log --skill browse --duration 10 --outcome error --error-class timeout --session-id err-123`);
96
+
97
+ const events = parseJsonl();
98
+ expect(events[0].error_class).toBe('timeout');
99
+ expect(events[0].outcome).toBe('error');
100
+ });
101
+
102
+ test('handles missing duration gracefully', () => {
103
+ setConfig('telemetry', 'anonymous');
104
+ run(`${BIN}/gstack-telemetry-log --skill qa --outcome success --session-id nodur-123`);
105
+
106
+ const events = parseJsonl();
107
+ expect(events[0].duration_s).toBeNull();
108
+ });
109
+
110
+ test('supports event_type flag', () => {
111
+ setConfig('telemetry', 'anonymous');
112
+ run(`${BIN}/gstack-telemetry-log --event-type upgrade_prompted --skill "" --outcome success --session-id up-123`);
113
+
114
+ const events = parseJsonl();
115
+ expect(events[0].event_type).toBe('upgrade_prompted');
116
+ });
117
+
118
+ test('includes local-only fields (_repo_slug, _branch)', () => {
119
+ setConfig('telemetry', 'anonymous');
120
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 50 --outcome success --session-id local-123`);
121
+
122
+ const events = parseJsonl();
123
+ // These should be present in local JSONL
124
+ expect(events[0]).toHaveProperty('_repo_slug');
125
+ expect(events[0]).toHaveProperty('_branch');
126
+ });
127
+
128
+ test('creates analytics directory if missing', () => {
129
+ // Remove analytics dir
130
+ const analyticsDir = path.join(tmpDir, 'analytics');
131
+ if (fs.existsSync(analyticsDir)) fs.rmSync(analyticsDir, { recursive: true });
132
+
133
+ setConfig('telemetry', 'anonymous');
134
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 50 --outcome success --session-id mkdir-123`);
135
+
136
+ expect(fs.existsSync(analyticsDir)).toBe(true);
137
+ expect(readJsonl()).toHaveLength(1);
138
+ });
139
+ });
140
+
141
+ describe('.pending marker', () => {
142
+ test('finalizes stale .pending from another session as outcome:unknown', () => {
143
+ setConfig('telemetry', 'anonymous');
144
+
145
+ // Write a fake .pending marker from a different session
146
+ const analyticsDir = path.join(tmpDir, 'analytics');
147
+ fs.mkdirSync(analyticsDir, { recursive: true });
148
+ fs.writeFileSync(
149
+ path.join(analyticsDir, '.pending-old-123'),
150
+ '{"skill":"old-skill","ts":"2026-03-18T00:00:00Z","session_id":"old-123","gstack_version":"0.6.4"}'
151
+ );
152
+
153
+ // Run telemetry-log with a DIFFERENT session — should finalize the old pending marker
154
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 50 --outcome success --session-id new-456`);
155
+
156
+ const events = parseJsonl();
157
+ expect(events).toHaveLength(2);
158
+
159
+ // First event: finalized pending
160
+ expect(events[0].skill).toBe('old-skill');
161
+ expect(events[0].outcome).toBe('unknown');
162
+ expect(events[0].session_id).toBe('old-123');
163
+
164
+ // Second event: new event
165
+ expect(events[1].skill).toBe('qa');
166
+ expect(events[1].outcome).toBe('success');
167
+ });
168
+
169
+ test('.pending-SESSION file is removed after finalization', () => {
170
+ setConfig('telemetry', 'anonymous');
171
+
172
+ const analyticsDir = path.join(tmpDir, 'analytics');
173
+ fs.mkdirSync(analyticsDir, { recursive: true });
174
+ const pendingPath = path.join(analyticsDir, '.pending-stale-session');
175
+ fs.writeFileSync(pendingPath, '{"skill":"stale","ts":"2026-03-18T00:00:00Z","session_id":"stale-session","gstack_version":"v"}');
176
+
177
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 50 --outcome success --session-id new-456`);
178
+
179
+ expect(fs.existsSync(pendingPath)).toBe(false);
180
+ });
181
+
182
+ test('does not finalize own session pending marker', () => {
183
+ setConfig('telemetry', 'anonymous');
184
+
185
+ const analyticsDir = path.join(tmpDir, 'analytics');
186
+ fs.mkdirSync(analyticsDir, { recursive: true });
187
+ // Create pending for same session ID we'll use
188
+ const pendingPath = path.join(analyticsDir, '.pending-same-session');
189
+ fs.writeFileSync(pendingPath, '{"skill":"in-flight","ts":"2026-03-18T00:00:00Z","session_id":"same-session","gstack_version":"v"}');
190
+
191
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 50 --outcome success --session-id same-session`);
192
+
193
+ // Should only have 1 event (the new one), not finalize own pending
194
+ const events = parseJsonl();
195
+ expect(events).toHaveLength(1);
196
+ expect(events[0].skill).toBe('qa');
197
+ });
198
+
199
+ test('tier=off still clears own session pending', () => {
200
+ setConfig('telemetry', 'off');
201
+
202
+ const analyticsDir = path.join(tmpDir, 'analytics');
203
+ fs.mkdirSync(analyticsDir, { recursive: true });
204
+ const pendingPath = path.join(analyticsDir, '.pending-off-123');
205
+ fs.writeFileSync(pendingPath, '{"skill":"stale","ts":"2026-03-18T00:00:00Z","session_id":"off-123","gstack_version":"v"}');
206
+
207
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 50 --outcome success --session-id off-123`);
208
+
209
+ expect(fs.existsSync(pendingPath)).toBe(false);
210
+ // But no JSONL entries since tier=off
211
+ expect(readJsonl()).toHaveLength(0);
212
+ });
213
+ });
214
+
215
+ describe('gstack-analytics', () => {
216
+ test('shows "no data" for empty JSONL', () => {
217
+ const output = run(`${BIN}/gstack-analytics`);
218
+ expect(output).toContain('no data');
219
+ });
220
+
221
+ test('renders usage dashboard with events', () => {
222
+ setConfig('telemetry', 'anonymous');
223
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 120 --outcome success --session-id a-1`);
224
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 60 --outcome success --session-id a-2`);
225
+ run(`${BIN}/gstack-telemetry-log --skill ship --duration 30 --outcome error --error-class timeout --session-id a-3`);
226
+
227
+ const output = run(`${BIN}/gstack-analytics all`);
228
+ expect(output).toContain('/qa');
229
+ expect(output).toContain('/ship');
230
+ expect(output).toContain('2 runs');
231
+ expect(output).toContain('1 runs');
232
+ expect(output).toContain('Success rate: 66%');
233
+ expect(output).toContain('Errors: 1');
234
+ });
235
+
236
+ test('filters by time window', () => {
237
+ setConfig('telemetry', 'anonymous');
238
+ run(`${BIN}/gstack-telemetry-log --skill qa --duration 60 --outcome success --session-id t-1`);
239
+
240
+ const output7d = run(`${BIN}/gstack-analytics 7d`);
241
+ expect(output7d).toContain('/qa');
242
+ expect(output7d).toContain('last 7 days');
243
+ });
244
+ });
245
+
246
+ describe('gstack-telemetry-sync', () => {
247
+ test('exits silently with no endpoint configured', () => {
248
+ // Default: GSTACK_TELEMETRY_ENDPOINT is not set → exit 0
249
+ const result = run(`${BIN}/gstack-telemetry-sync`);
250
+ expect(result).toBe('');
251
+ });
252
+
253
+ test('exits silently with no JSONL file', () => {
254
+ const result = run(`${BIN}/gstack-telemetry-sync`, { GSTACK_TELEMETRY_ENDPOINT: 'http://localhost:9999' });
255
+ expect(result).toBe('');
256
+ });
257
+ });
258
+
259
+ describe('gstack-community-dashboard', () => {
260
+ test('shows unconfigured message when no Supabase config available', () => {
261
+ // Use a fake GSTACK_DIR with no supabase/config.sh
262
+ const output = run(`${BIN}/gstack-community-dashboard`, {
263
+ GSTACK_DIR: tmpDir,
264
+ GSTACK_SUPABASE_URL: '',
265
+ GSTACK_SUPABASE_ANON_KEY: '',
266
+ });
267
+ expect(output).toContain('Supabase not configured');
268
+ expect(output).toContain('gstack-analytics');
269
+ });
270
+
271
+ test('connects to Supabase when config exists', () => {
272
+ // Use the real GSTACK_DIR which has supabase/config.sh
273
+ const output = run(`${BIN}/gstack-community-dashboard`);
274
+ expect(output).toContain('gstack community dashboard');
275
+ // Should not show "not configured" since config.sh exists
276
+ expect(output).not.toContain('Supabase not configured');
277
+ });
278
+ });
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Unit tests for diff-based test selection.
3
+ * Free (no API calls), runs with `bun test`.
4
+ */
5
+
6
+ import { describe, test, expect } from 'bun:test';
7
+ import { spawnSync } from 'child_process';
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import * as os from 'os';
11
+ import {
12
+ matchGlob,
13
+ selectTests,
14
+ detectBaseBranch,
15
+ E2E_TOUCHFILES,
16
+ LLM_JUDGE_TOUCHFILES,
17
+ GLOBAL_TOUCHFILES,
18
+ } from './helpers/touchfiles';
19
+
20
+ const ROOT = path.resolve(import.meta.dir, '..');
21
+
22
+ // --- matchGlob ---
23
+
24
+ describe('matchGlob', () => {
25
+ test('** matches any depth of path segments', () => {
26
+ expect(matchGlob('browse/src/commands.ts', 'browse/src/**')).toBe(true);
27
+ expect(matchGlob('browse/src/deep/nested/file.ts', 'browse/src/**')).toBe(true);
28
+ expect(matchGlob('browse/src/cli.ts', 'browse/src/**')).toBe(true);
29
+ });
30
+
31
+ test('** does not match unrelated paths', () => {
32
+ expect(matchGlob('browse/src/commands.ts', 'qa/**')).toBe(false);
33
+ expect(matchGlob('review/SKILL.md', 'qa/**')).toBe(false);
34
+ });
35
+
36
+ test('exact match works', () => {
37
+ expect(matchGlob('SKILL.md', 'SKILL.md')).toBe(true);
38
+ expect(matchGlob('SKILL.md.tmpl', 'SKILL.md')).toBe(false);
39
+ expect(matchGlob('qa/SKILL.md', 'SKILL.md')).toBe(false);
40
+ });
41
+
42
+ test('* matches within a single segment', () => {
43
+ expect(matchGlob('test/fixtures/review-eval-enum.rb', 'test/fixtures/review-eval-enum*.rb')).toBe(true);
44
+ expect(matchGlob('test/fixtures/review-eval-enum-diff.rb', 'test/fixtures/review-eval-enum*.rb')).toBe(true);
45
+ expect(matchGlob('test/fixtures/review-eval-vuln.rb', 'test/fixtures/review-eval-enum*.rb')).toBe(false);
46
+ });
47
+
48
+ test('dots in patterns are escaped correctly', () => {
49
+ expect(matchGlob('SKILL.md', 'SKILL.md')).toBe(true);
50
+ expect(matchGlob('SKILLxmd', 'SKILL.md')).toBe(false);
51
+ });
52
+
53
+ test('** at end matches files in the directory', () => {
54
+ expect(matchGlob('qa/SKILL.md', 'qa/**')).toBe(true);
55
+ expect(matchGlob('qa/SKILL.md.tmpl', 'qa/**')).toBe(true);
56
+ expect(matchGlob('qa/templates/report.md', 'qa/**')).toBe(true);
57
+ });
58
+ });
59
+
60
+ // --- selectTests ---
61
+
62
+ describe('selectTests', () => {
63
+ test('browse/src change selects browse and qa tests', () => {
64
+ const result = selectTests(['browse/src/commands.ts'], E2E_TOUCHFILES);
65
+ expect(result.selected).toContain('browse-basic');
66
+ expect(result.selected).toContain('browse-snapshot');
67
+ expect(result.selected).toContain('qa-quick');
68
+ expect(result.selected).toContain('qa-fix-loop');
69
+ expect(result.selected).toContain('design-review-fix');
70
+ expect(result.reason).toBe('diff');
71
+ // Should NOT include unrelated tests
72
+ expect(result.selected).not.toContain('plan-ceo-review');
73
+ expect(result.selected).not.toContain('retro');
74
+ expect(result.selected).not.toContain('document-release');
75
+ });
76
+
77
+ test('skill-specific change selects only that skill and related tests', () => {
78
+ const result = selectTests(['plan-ceo-review/SKILL.md'], E2E_TOUCHFILES);
79
+ expect(result.selected).toContain('plan-ceo-review');
80
+ expect(result.selected).toContain('plan-ceo-review-selective');
81
+ expect(result.selected).toContain('plan-ceo-review-benefits');
82
+ expect(result.selected).toContain('autoplan-core');
83
+ expect(result.selected.length).toBe(4);
84
+ expect(result.skipped.length).toBe(Object.keys(E2E_TOUCHFILES).length - 4);
85
+ });
86
+
87
+ test('global touchfile triggers ALL tests', () => {
88
+ const result = selectTests(['test/helpers/session-runner.ts'], E2E_TOUCHFILES);
89
+ expect(result.selected.length).toBe(Object.keys(E2E_TOUCHFILES).length);
90
+ expect(result.skipped.length).toBe(0);
91
+ expect(result.reason).toContain('global');
92
+ });
93
+
94
+ test('gen-skill-docs.ts is a global touchfile', () => {
95
+ const result = selectTests(['scripts/gen-skill-docs.ts'], E2E_TOUCHFILES);
96
+ expect(result.selected.length).toBe(Object.keys(E2E_TOUCHFILES).length);
97
+ expect(result.reason).toContain('global');
98
+ });
99
+
100
+ test('unrelated file selects nothing', () => {
101
+ const result = selectTests(['README.md'], E2E_TOUCHFILES);
102
+ expect(result.selected).toEqual([]);
103
+ expect(result.skipped.length).toBe(Object.keys(E2E_TOUCHFILES).length);
104
+ });
105
+
106
+ test('empty changed files selects nothing', () => {
107
+ const result = selectTests([], E2E_TOUCHFILES);
108
+ expect(result.selected).toEqual([]);
109
+ });
110
+
111
+ test('multiple changed files union their selections', () => {
112
+ const result = selectTests(
113
+ ['plan-ceo-review/SKILL.md', 'retro/SKILL.md.tmpl'],
114
+ E2E_TOUCHFILES,
115
+ );
116
+ expect(result.selected).toContain('plan-ceo-review');
117
+ expect(result.selected).toContain('plan-ceo-review-selective');
118
+ expect(result.selected).toContain('retro');
119
+ expect(result.selected).toContain('retro-base-branch');
120
+ // Also selects journey routing tests (*/SKILL.md.tmpl matches retro/SKILL.md.tmpl)
121
+ expect(result.selected.length).toBeGreaterThanOrEqual(4);
122
+ });
123
+
124
+ test('works with LLM_JUDGE_TOUCHFILES', () => {
125
+ const result = selectTests(['qa/SKILL.md'], LLM_JUDGE_TOUCHFILES);
126
+ expect(result.selected).toContain('qa/SKILL.md workflow');
127
+ expect(result.selected).toContain('qa/SKILL.md health rubric');
128
+ expect(result.selected).toContain('qa/SKILL.md anti-refusal');
129
+ expect(result.selected.length).toBe(3);
130
+ });
131
+
132
+ test('SKILL.md.tmpl root template selects root-dependent tests and routing tests', () => {
133
+ const result = selectTests(['SKILL.md.tmpl'], E2E_TOUCHFILES);
134
+ // Should select the 7 tests that depend on root SKILL.md
135
+ expect(result.selected).toContain('skillmd-setup-discovery');
136
+ expect(result.selected).toContain('contributor-mode');
137
+ expect(result.selected).toContain('session-awareness');
138
+ // Also selects journey routing tests (SKILL.md.tmpl in their touchfiles)
139
+ expect(result.selected).toContain('journey-ideation');
140
+ // Should NOT select unrelated non-routing tests
141
+ expect(result.selected).not.toContain('plan-ceo-review');
142
+ expect(result.selected).not.toContain('retro');
143
+ });
144
+
145
+ test('global touchfiles work for LLM-judge tests too', () => {
146
+ const result = selectTests(['scripts/gen-skill-docs.ts'], LLM_JUDGE_TOUCHFILES);
147
+ expect(result.selected.length).toBe(Object.keys(LLM_JUDGE_TOUCHFILES).length);
148
+ });
149
+ });
150
+
151
+ // --- detectBaseBranch ---
152
+
153
+ describe('detectBaseBranch', () => {
154
+ test('detects local main branch', () => {
155
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'touchfiles-test-'));
156
+ const run = (cmd: string, args: string[]) =>
157
+ spawnSync(cmd, args, { cwd: dir, stdio: 'pipe', timeout: 5000 });
158
+
159
+ run('git', ['init']);
160
+ run('git', ['config', 'user.email', 'test@test.com']);
161
+ run('git', ['config', 'user.name', 'Test']);
162
+ fs.writeFileSync(path.join(dir, 'test.txt'), 'hello\n');
163
+ run('git', ['add', '.']);
164
+ run('git', ['commit', '-m', 'init']);
165
+
166
+ const result = detectBaseBranch(dir);
167
+ // Should find 'main' (or 'master' depending on git default)
168
+ expect(result).toMatch(/^(main|master)$/);
169
+
170
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
171
+ });
172
+
173
+ test('returns null for empty repo with no branches', () => {
174
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'touchfiles-test-'));
175
+ const run = (cmd: string, args: string[]) =>
176
+ spawnSync(cmd, args, { cwd: dir, stdio: 'pipe', timeout: 5000 });
177
+
178
+ run('git', ['init']);
179
+ // No commits = no branches
180
+ const result = detectBaseBranch(dir);
181
+ expect(result).toBeNull();
182
+
183
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
184
+ });
185
+
186
+ test('returns null for non-git directory', () => {
187
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'touchfiles-test-'));
188
+ const result = detectBaseBranch(dir);
189
+ expect(result).toBeNull();
190
+
191
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
192
+ });
193
+ });
194
+
195
+ // --- Completeness: every testName in skill-e2e-*.test.ts has a TOUCHFILES entry ---
196
+
197
+ describe('TOUCHFILES completeness', () => {
198
+ test('every E2E testName has a TOUCHFILES entry', () => {
199
+ // Read all split E2E test files
200
+ const testDir = path.join(ROOT, 'test');
201
+ const e2eFiles = fs.readdirSync(testDir).filter(f => f.startsWith('skill-e2e-') && f.endsWith('.test.ts'));
202
+ let e2eContent = '';
203
+ for (const f of e2eFiles) {
204
+ e2eContent += fs.readFileSync(path.join(testDir, f), 'utf-8') + '\n';
205
+ }
206
+
207
+ // Extract all testName: 'value' entries
208
+ const testNameRegex = /testName:\s*['"`]([^'"`]+)['"`]/g;
209
+ const testNames: string[] = [];
210
+ let match;
211
+ while ((match = testNameRegex.exec(e2eContent)) !== null) {
212
+ let name = match[1];
213
+ // Handle template literals like `qa-${label}` — these expand to
214
+ // qa-b6-static, qa-b7-spa, qa-b8-checkout
215
+ if (name.includes('${')) continue; // skip template literals, check expanded forms below
216
+ testNames.push(name);
217
+ }
218
+
219
+ // Add the template-expanded testNames from runPlantedBugEval calls
220
+ const plantedBugRegex = /runPlantedBugEval\([^,]+,\s*[^,]+,\s*['"`]([^'"`]+)['"`]\)/g;
221
+ while ((match = plantedBugRegex.exec(e2eContent)) !== null) {
222
+ testNames.push(`qa-${match[1]}`);
223
+ }
224
+
225
+ expect(testNames.length).toBeGreaterThan(0);
226
+
227
+ const missing = testNames.filter(name => !(name in E2E_TOUCHFILES));
228
+ if (missing.length > 0) {
229
+ throw new Error(
230
+ `E2E tests missing TOUCHFILES entries: ${missing.join(', ')}\n` +
231
+ `Add these to E2E_TOUCHFILES in test/helpers/touchfiles.ts`,
232
+ );
233
+ }
234
+ });
235
+
236
+ test('every LLM-judge test has a TOUCHFILES entry', () => {
237
+ const llmContent = fs.readFileSync(
238
+ path.join(ROOT, 'test', 'skill-llm-eval.test.ts'),
239
+ 'utf-8',
240
+ );
241
+
242
+ // Extract test names from addTest({ name: '...' }) calls
243
+ const nameRegex = /name:\s*['"`]([^'"`]+)['"`]/g;
244
+ const testNames: string[] = [];
245
+ let match;
246
+ while ((match = nameRegex.exec(llmContent)) !== null) {
247
+ testNames.push(match[1]);
248
+ }
249
+
250
+ // Deduplicate (some tests call addTest with the same name)
251
+ const unique = [...new Set(testNames)];
252
+ expect(unique.length).toBeGreaterThan(0);
253
+
254
+ const missing = unique.filter(name => !(name in LLM_JUDGE_TOUCHFILES));
255
+ if (missing.length > 0) {
256
+ throw new Error(
257
+ `LLM-judge tests missing TOUCHFILES entries: ${missing.join(', ')}\n` +
258
+ `Add these to LLM_JUDGE_TOUCHFILES in test/helpers/touchfiles.ts`,
259
+ );
260
+ }
261
+ });
262
+ });
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: unfreeze
3
+ version: 0.1.0
4
+ description: |
5
+ Clear the freeze boundary set by /freeze, allowing edits to all directories
6
+ again. Use when you want to widen edit scope without ending the session.
7
+ Use when asked to "unfreeze", "unlock edits", "remove freeze", or
8
+ "allow all edits".
9
+ allowed-tools:
10
+ - Bash
11
+ - Read
12
+ ---
13
+ <!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->
14
+ <!-- Regenerate: bun run gen:skill-docs -->
15
+
16
+ # /unfreeze — Clear Freeze Boundary
17
+
18
+ Remove the edit restriction set by `/freeze`, allowing edits to all directories.
19
+
20
+ ```bash
21
+ mkdir -p ~/.gstack/analytics
22
+ echo '{"skill":"unfreeze","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
23
+ ```
24
+
25
+ ## Clear the boundary
26
+
27
+ ```bash
28
+ STATE_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.gstack}"
29
+ if [ -f "$STATE_DIR/freeze-dir.txt" ]; then
30
+ PREV=$(cat "$STATE_DIR/freeze-dir.txt")
31
+ rm -f "$STATE_DIR/freeze-dir.txt"
32
+ echo "Freeze boundary cleared (was: $PREV). Edits are now allowed everywhere."
33
+ else
34
+ echo "No freeze boundary was set."
35
+ fi
36
+ ```
37
+
38
+ Tell the user the result. Note that `/freeze` hooks are still registered for the
39
+ session — they will just allow everything since no state file exists. To re-freeze,
40
+ run `/freeze` again.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: unfreeze
3
+ version: 0.1.0
4
+ description: |
5
+ Clear the freeze boundary set by /freeze, allowing edits to all directories
6
+ again. Use when you want to widen edit scope without ending the session.
7
+ Use when asked to "unfreeze", "unlock edits", "remove freeze", or
8
+ "allow all edits".
9
+ allowed-tools:
10
+ - Bash
11
+ - Read
12
+ ---
13
+
14
+ # /unfreeze — Clear Freeze Boundary
15
+
16
+ Remove the edit restriction set by `/freeze`, allowing edits to all directories.
17
+
18
+ ```bash
19
+ mkdir -p ~/.gstack/analytics
20
+ echo '{"skill":"unfreeze","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
21
+ ```
22
+
23
+ ## Clear the boundary
24
+
25
+ ```bash
26
+ STATE_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.gstack}"
27
+ if [ -f "$STATE_DIR/freeze-dir.txt" ]; then
28
+ PREV=$(cat "$STATE_DIR/freeze-dir.txt")
29
+ rm -f "$STATE_DIR/freeze-dir.txt"
30
+ echo "Freeze boundary cleared (was: $PREV). Edits are now allowed everywhere."
31
+ else
32
+ echo "No freeze boundary was set."
33
+ fi
34
+ ```
35
+
36
+ Tell the user the result. Note that `/freeze` hooks are still registered for the
37
+ session — they will just allow everything since no state file exists. To re-freeze,
38
+ run `/freeze` again.
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@runchr/gstack-antigravity",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Local installer for gStack-Antigravity (.agents layout)",
6
+ "keywords": [
7
+ "antigravity",
8
+ "gstack",
9
+ "agent",
10
+ "workflow",
11
+ "skills"
12
+ ],
13
+ "homepage": "https://github.com/runchr-com/gstack-antigravity#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/runchr-com/gstack-antigravity/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/runchr-com/gstack-antigravity.git"
20
+ },
21
+ "files": [
22
+ "bin/",
23
+ "scripts/",
24
+ ".agents/workflows/",
25
+ ".agents/rules/",
26
+ "gstack-origin/",
27
+ "README.md",
28
+ "README_KO.md",
29
+ "LICENSE"
30
+ ],
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "bin": {
35
+ "gstack-antigravity": "bin/install.js"
36
+ },
37
+ "license": "MIT"
38
+ }