@scheduler-systems/gal-cli 0.1.13-beta.2-alpha.pr72 → 0.1.13

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/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@scheduler-systems/gal-cli",
3
- "version": "0.1.13-beta.2-alpha.pr72",
3
+ "version": "0.1.13",
4
4
  "description": "GAL CLI - Command-line tool for managing AI agent configurations across your organization",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
7
+ "publishConfig": {
8
+ "registry": "https://registry.npmjs.org",
9
+ "access": "public"
10
+ },
7
11
  "type": "module",
8
12
  "main": "./dist/index.cjs",
9
13
  "bin": {
@@ -12,11 +16,13 @@
12
16
  "files": [
13
17
  "dist/index.cjs",
14
18
  "scripts/postinstall.cjs",
19
+ "scripts/preuninstall.cjs",
15
20
  "README.md",
16
21
  "LICENSE"
17
22
  ],
18
23
  "scripts": {
19
24
  "postinstall": "node scripts/postinstall.cjs",
25
+ "preuninstall": "node scripts/preuninstall.cjs",
20
26
  "dev": "tsx watch src/index.ts",
21
27
  "build": "rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.cjs --format=cjs --define:__CLI_VERSION__=\\\"$(node -p \"require('./package.json').version\")\\\" --define:__DEFAULT_API_URL__=\\\"http://localhost:3000\\\" && chmod +x dist/index.cjs",
22
28
  "build:publish": "rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node18 --minify --outfile=dist/index.cjs --format=cjs --define:__CLI_VERSION__=\\\"$(node -p \"require('./package.json').version\")\\\" --define:__DEFAULT_API_URL__=\\\"${DEFAULT_API_URL:-https://gal-api-s3pvf4isfa-uc.a.run.app}\\\" && sed -i '' '1s/^#!.*$//' dist/index.cjs && printf '%s\\n' '#!/usr/bin/env node' | cat - dist/index.cjs > dist/temp.cjs && mv dist/temp.cjs dist/index.cjs && chmod +x dist/index.cjs",
@@ -74,4 +80,4 @@
74
80
  "typescript": "^5.7.2",
75
81
  "vitest": "^1.0.0"
76
82
  }
77
- }
83
+ }
@@ -5,6 +5,7 @@
5
5
  * Automatically installs Claude Code integrations when GAL CLI is installed:
6
6
  * 1. SessionStart hook → ~/.claude/hooks/gal-sync-reminder.js
7
7
  * 2. Status line script → ~/.claude/status_lines/gal-sync-status.py
8
+ * 3. GAL CLI rules → ~/.claude/rules/gal-cli.md
8
9
  *
9
10
  * These are CLI-level configs (not org-specific).
10
11
  * Org-specific configs are handled by `gal sync --pull`.
@@ -27,6 +28,31 @@ const CLI_VERSION = cliPackageJson.version;
27
28
  // Version markers for idempotency
28
29
  const HOOK_VERSION = '2.1.0'; // SessionStart version (2.1.0: Added telemetry tracking)
29
30
  const STATUS_LINE_VERSION = '1.0.0';
31
+ const RULES_VERSION = '1.0.0';
32
+
33
+ // =============================================================================
34
+ // GAL CLI Rules Content
35
+ // =============================================================================
36
+ // This is injected into ~/.claude/rules/gal-cli.md
37
+ // Provides persistent GAL CLI awareness without running on every prompt
38
+ // =============================================================================
39
+
40
+ const GAL_CLI_RULES_CONTENT = `# GAL CLI
41
+
42
+ <!-- GAL_RULES_VERSION = "${RULES_VERSION}" -->
43
+
44
+ The \`gal\` CLI is available for managing org-approved AI agent configurations.
45
+
46
+ ## Available Commands
47
+ - \`gal sync --pull\` - Download latest approved config from your organization
48
+ - \`gal auth login\` - Authenticate with GitHub
49
+ - \`gal --help\` - See all available commands
50
+
51
+ ## Behavior Rules
52
+ - **Confirmation Required**: Always ask the user before running any \`gal\` command
53
+ - **Self-Discovery**: If unsure about syntax, run \`gal --help\` or \`gal <command> --help\` first
54
+ - **Sync Notifications**: When you see a GAL sync notification, ask: "Do you want me to sync gal now?"
55
+ `;
30
56
 
31
57
  // =============================================================================
32
58
  // SessionStart Hook Content
@@ -124,11 +150,16 @@ function isGalInstalled() {
124
150
 
125
151
  function selfClean() {
126
152
  const hookPath = __filename;
127
- const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
153
+ const claudeDir = path.join(os.homedir(), '.claude');
154
+ const settingsPath = path.join(claudeDir, 'settings.json');
155
+ const rulesPath = path.join(claudeDir, 'rules', 'gal-cli.md');
128
156
 
129
157
  // Remove hook file
130
158
  try { fs.unlinkSync(hookPath); } catch {}
131
159
 
160
+ // Remove rules file
161
+ try { fs.unlinkSync(rulesPath); } catch {}
162
+
132
163
  // Remove hook entries from settings.json
133
164
  try {
134
165
  if (fs.existsSync(settingsPath)) {
@@ -484,6 +515,48 @@ function installHook() {
484
515
  }
485
516
  }
486
517
 
518
+ /**
519
+ * Install GAL CLI rules to ~/.claude/rules/
520
+ *
521
+ * Key behaviors:
522
+ * - Idempotent: Checks version before writing
523
+ * - Provides persistent GAL CLI awareness
524
+ * - No longer runs on every prompt (unlike hooks)
525
+ */
526
+ function installRules() {
527
+ const claudeDir = path.join(os.homedir(), '.claude');
528
+ const rulesDir = path.join(claudeDir, 'rules');
529
+ const rulesPath = path.join(rulesDir, 'gal-cli.md');
530
+
531
+ try {
532
+ // Create rules directory if needed
533
+ if (!fs.existsSync(rulesDir)) {
534
+ fs.mkdirSync(rulesDir, { recursive: true });
535
+ }
536
+
537
+ // Check if rules file already exists with current version
538
+ let needsUpdate = true;
539
+ if (fs.existsSync(rulesPath)) {
540
+ const existingContent = fs.readFileSync(rulesPath, 'utf-8');
541
+ const versionMatch = existingContent.match(/GAL_RULES_VERSION = "([^"]+)"/);
542
+ if (versionMatch && versionMatch[1] === RULES_VERSION) {
543
+ needsUpdate = false;
544
+ }
545
+ }
546
+
547
+ // Write the rules file if needed
548
+ if (needsUpdate) {
549
+ fs.writeFileSync(rulesPath, GAL_CLI_RULES_CONTENT, 'utf-8');
550
+ console.log('✓ GAL CLI rules installed');
551
+ }
552
+
553
+ return true;
554
+ } catch (error) {
555
+ // Silent fail - rules are optional enhancement
556
+ return false;
557
+ }
558
+ }
559
+
487
560
  /**
488
561
  * Queue a telemetry event for the next CLI run (GAL-114)
489
562
  * Since postinstall is CommonJS and telemetry module is ESM,
@@ -601,14 +674,15 @@ function installStatusLine() {
601
674
 
602
675
  function main() {
603
676
  const hookInstalled = installHook();
677
+ const rulesInstalled = installRules();
604
678
  const statusLineInstalled = installStatusLine();
605
679
 
606
680
  // Queue telemetry event (GAL-114)
607
681
  queueTelemetryEvent();
608
682
 
609
- if (hookInstalled || statusLineInstalled) {
683
+ if (hookInstalled || rulesInstalled || statusLineInstalled) {
610
684
  console.log('');
611
- console.log('Restart Claude Code for changes to take effect.');
685
+ console.log('Restart Claude Code/Cursor for changes to take effect.');
612
686
  console.log('');
613
687
  console.log('Next steps:');
614
688
  console.log(' 1. gal auth login - Authenticate with GitHub');
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * GAL CLI Preuninstall Script
4
+ * Automatically cleans up GAL files when the CLI is uninstalled via npm
5
+ * This runs before the package is removed
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ /**
13
+ * Remove GAL hook entries from settings.json without deleting the file
14
+ */
15
+ function removeGalHookEntries(settingsPath) {
16
+ try {
17
+ if (!fs.existsSync(settingsPath)) {
18
+ return false;
19
+ }
20
+
21
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
22
+
23
+ if (!settings.hooks?.UserPromptSubmit) {
24
+ return false;
25
+ }
26
+
27
+ // Filter out GAL hooks
28
+ const originalLength = settings.hooks.UserPromptSubmit.length;
29
+ settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter((entry) => {
30
+ if (!entry.hooks) return true;
31
+ // Keep entry only if it has non-GAL hooks
32
+ entry.hooks = entry.hooks.filter((hook) =>
33
+ !hook.command?.includes('gal-') && !hook.command?.includes('/gal/')
34
+ );
35
+ return entry.hooks.length > 0;
36
+ });
37
+
38
+ // Remove empty hooks array
39
+ if (settings.hooks.UserPromptSubmit.length === 0) {
40
+ delete settings.hooks.UserPromptSubmit;
41
+ }
42
+
43
+ // Remove empty hooks object
44
+ if (Object.keys(settings.hooks).length === 0) {
45
+ delete settings.hooks;
46
+ }
47
+
48
+ if (settings.hooks?.UserPromptSubmit?.length !== originalLength) {
49
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
50
+ return true;
51
+ }
52
+
53
+ return false;
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Clean up GAL-installed files from user's home directory
61
+ */
62
+ function cleanupUserLevel() {
63
+ const claudeDir = path.join(os.homedir(), '.claude');
64
+ const hooksDir = path.join(claudeDir, 'hooks');
65
+ const settingsPath = path.join(claudeDir, 'settings.json');
66
+
67
+ let removed = [];
68
+
69
+ // Remove GAL hook files
70
+ if (fs.existsSync(hooksDir)) {
71
+ try {
72
+ const files = fs.readdirSync(hooksDir);
73
+ for (const file of files) {
74
+ if (file.startsWith('gal-')) {
75
+ const hookPath = path.join(hooksDir, file);
76
+ try {
77
+ fs.unlinkSync(hookPath);
78
+ removed.push(hookPath);
79
+ } catch (err) {
80
+ // Silent fail
81
+ }
82
+ }
83
+ }
84
+ } catch (err) {
85
+ // Silent fail
86
+ }
87
+ }
88
+
89
+ // Remove GAL hook entries from settings.json
90
+ if (removeGalHookEntries(settingsPath)) {
91
+ removed.push(`${settingsPath} (GAL hooks removed)`);
92
+ }
93
+
94
+ return removed;
95
+ }
96
+
97
+ /**
98
+ * Clean up GAL config from user's home directory
99
+ */
100
+ function cleanupGalConfig() {
101
+ const galConfigDir = path.join(os.homedir(), '.gal');
102
+
103
+ if (fs.existsSync(galConfigDir)) {
104
+ try {
105
+ fs.rmSync(galConfigDir, { recursive: true, force: true });
106
+ return [galConfigDir];
107
+ } catch (err) {
108
+ // Silent fail
109
+ return [];
110
+ }
111
+ }
112
+
113
+ return [];
114
+ }
115
+
116
+ /**
117
+ * Main cleanup function
118
+ */
119
+ function cleanup() {
120
+ console.log('\n═══════════════════════════════════════════════════');
121
+ console.log(' GAL CLI Uninstall Cleanup');
122
+ console.log('═══════════════════════════════════════════════════\n');
123
+
124
+ const userLevelFiles = cleanupUserLevel();
125
+ const galConfigFiles = cleanupGalConfig();
126
+ const allRemoved = [...userLevelFiles, ...galConfigFiles];
127
+
128
+ if (allRemoved.length > 0) {
129
+ console.log('✓ Cleaned up GAL files:');
130
+ for (const file of allRemoved) {
131
+ console.log(` - ${file}`);
132
+ }
133
+ } else {
134
+ console.log('No GAL files found to clean up.');
135
+ }
136
+
137
+ console.log('\n═══════════════════════════════════════════════════');
138
+ console.log(' GAL CLI has been uninstalled');
139
+ console.log('═══════════════════════════════════════════════════\n');
140
+ console.log('To reinstall: npm install -g @scheduler-systems/gal-cli\n');
141
+ }
142
+
143
+ // Run cleanup
144
+ try {
145
+ cleanup();
146
+ } catch (error) {
147
+ // Silent fail - don't prevent uninstall
148
+ console.error('GAL cleanup encountered an error, but uninstall will proceed.');
149
+ }