@nodesify/astria 1.0.0 → 1.0.2

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 (81) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +65 -65
  3. package/dist/commands/add.d.ts +7 -7
  4. package/dist/commands/add.js +33 -33
  5. package/dist/commands/affected.d.ts +5 -5
  6. package/dist/commands/affected.js +29 -29
  7. package/dist/commands/cluster.d.ts +1 -1
  8. package/dist/commands/cluster.js +50 -50
  9. package/dist/commands/diagnose.d.ts +4 -4
  10. package/dist/commands/diagnose.js +19 -19
  11. package/dist/commands/diff.d.ts +1 -1
  12. package/dist/commands/diff.js +35 -35
  13. package/dist/commands/explain.d.ts +3 -3
  14. package/dist/commands/explain.js +37 -37
  15. package/dist/commands/export.d.ts +6 -6
  16. package/dist/commands/export.js +32 -32
  17. package/dist/commands/feedback.d.ts +11 -11
  18. package/dist/commands/feedback.js +40 -40
  19. package/dist/commands/global.d.ts +6 -6
  20. package/dist/commands/global.js +60 -60
  21. package/dist/commands/history.d.ts +4 -4
  22. package/dist/commands/history.js +27 -27
  23. package/dist/commands/hook-guard.d.ts +1 -1
  24. package/dist/commands/hook-guard.js +144 -144
  25. package/dist/commands/hook.d.ts +2 -2
  26. package/dist/commands/hook.js +52 -52
  27. package/dist/commands/install.d.ts +2 -2
  28. package/dist/commands/install.js +39 -39
  29. package/dist/commands/map.d.ts +5 -5
  30. package/dist/commands/map.js +15 -15
  31. package/dist/commands/mcp.d.ts +3 -3
  32. package/dist/commands/mcp.js +14 -14
  33. package/dist/commands/merge.d.ts +1 -1
  34. package/dist/commands/merge.js +50 -50
  35. package/dist/commands/migrate.d.ts +3 -3
  36. package/dist/commands/migrate.js +74 -74
  37. package/dist/commands/path.d.ts +5 -5
  38. package/dist/commands/path.js +18 -18
  39. package/dist/commands/prs.d.ts +4 -4
  40. package/dist/commands/prs.js +132 -132
  41. package/dist/commands/query.d.ts +9 -9
  42. package/dist/commands/query.js +21 -21
  43. package/dist/commands/run.d.ts +9 -9
  44. package/dist/commands/run.js +68 -68
  45. package/dist/commands/stats.d.ts +3 -3
  46. package/dist/commands/stats.js +17 -17
  47. package/dist/commands/status.d.ts +3 -3
  48. package/dist/commands/status.js +56 -56
  49. package/dist/commands/tree.d.ts +5 -5
  50. package/dist/commands/tree.js +52 -52
  51. package/dist/commands/update.d.ts +6 -6
  52. package/dist/commands/update.js +65 -65
  53. package/dist/commands/watch.d.ts +3 -3
  54. package/dist/commands/watch.js +113 -113
  55. package/dist/commands/wiki.d.ts +6 -6
  56. package/dist/commands/wiki.js +59 -59
  57. package/dist/index.d.ts +4 -4
  58. package/dist/index.js +249 -249
  59. package/dist/install/hooks.d.ts +11 -11
  60. package/dist/install/hooks.js +306 -306
  61. package/dist/install/index.d.ts +2 -2
  62. package/dist/install/index.js +361 -361
  63. package/dist/install/markdown-inject.d.ts +6 -6
  64. package/dist/install/markdown-inject.js +177 -177
  65. package/dist/install/platforms.d.ts +12 -12
  66. package/dist/install/platforms.js +129 -129
  67. package/dist/install/settings-inject.d.ts +17 -17
  68. package/dist/install/settings-inject.js +424 -424
  69. package/dist/native.d.ts +31 -31
  70. package/dist/native.js +106 -106
  71. package/package.json +72 -72
  72. package/skills/skill-aider.md +40 -40
  73. package/skills/skill-codex.md +46 -46
  74. package/skills/skill-copilot.md +46 -46
  75. package/skills/skill-cursor.md +39 -39
  76. package/skills/skill-gemini.md +46 -46
  77. package/skills/skill-kiro.md +39 -39
  78. package/skills/skill-opencode.md +44 -44
  79. package/skills/skill-trae.md +46 -46
  80. package/skills/skill.md +266 -266
  81. package/dist/graphify.node +0 -4
@@ -1,307 +1,307 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.LEGACY_HOOK_PREFIXES = void 0;
37
- exports.installGitHooks = installGitHooks;
38
- exports.uninstallGitHooks = uninstallGitHooks;
39
- exports.statusGitHooks = statusGitHooks;
40
- const fs = __importStar(require("fs"));
41
- const path = __importStar(require("path"));
42
- const child_process_1 = require("child_process");
43
- const UPDATE_HELPER = `
44
- const ASTRIA_HOOK_VERSION = '3';
45
- // Prefer a workspace-local CLI, then a locally-installed package, and only
46
- // then whatever is on PATH — a stale global install would rebuild the graph
47
- // with old pipeline code and silently regress the report.
48
- function runAstriaUpdate() {
49
- if (existsSync(path.join('packages', 'astria-cli', 'dist', 'index.js'))) {
50
- execSync('node packages/astria-cli/dist/index.js update .', { stdio: 'inherit' });
51
- return;
52
- }
53
- try {
54
- execSync('npx --no-install astria update .', { stdio: 'inherit' });
55
- } catch {
56
- execSync('astria update .', { stdio: 'inherit' });
57
- }
58
- }
59
- `;
60
- const POST_COMMIT_SCRIPT = `// astria-hook-start
61
- const { execSync } = require('child_process');
62
- const { existsSync } = require('fs');
63
- const path = require('path');
64
- ${UPDATE_HELPER}
65
- try {
66
- // No shell redirects here: execSync uses cmd.exe on Windows where
67
- // "2>/dev/null" fails with "The system cannot find the path specified".
68
- // execSync captures stderr into the error object anyway.
69
- const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf-8' }).trim();
70
- const checks = [
71
- path.join(gitDir, 'rebase-merge'),
72
- path.join(gitDir, 'rebase-apply'),
73
- path.join(gitDir, 'MERGE_HEAD'),
74
- path.join(gitDir, 'CHERRY_PICK_HEAD'),
75
- ];
76
- if (checks.some(p => existsSync(p))) process.exit(0);
77
-
78
- const changed = execSync('git diff --name-only HEAD~1 HEAD || git diff --name-only HEAD', { encoding: 'utf-8' }).trim();
79
- if (!changed) process.exit(0);
80
-
81
- const codeExts = new Set(['.py', '.js', '.ts', '.tsx', '.jsx', '.rs', '.go', '.java', '.c', '.h', '.cpp', '.cc', '.cxx', '.hpp']);
82
- const hasCode = changed.split(/\\r?\\n/).some(f => codeExts.has(path.extname(f)));
83
- if (hasCode && existsSync('.astria')) {
84
- runAstriaUpdate();
85
- }
86
- } catch {}
87
- // astria-hook-end
88
- `;
89
- const POST_CHECKOUT_SCRIPT = `// astria-checkout-hook-start
90
- const { execSync } = require('child_process');
91
- const { existsSync } = require('fs');
92
- const path = require('path');
93
- ${UPDATE_HELPER}
94
- const branchSwitch = process.argv[3];
95
- if (branchSwitch !== '1') process.exit(0);
96
- if (!existsSync('.astria')) process.exit(0);
97
-
98
- try {
99
- // No shell redirects — see the note in the post-commit script.
100
- const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf-8' }).trim();
101
- if (existsSync(path.join(gitDir, 'rebase-merge')) || existsSync(path.join(gitDir, 'rebase-apply'))) process.exit(0);
102
-
103
- console.log('[astria] Branch switched - rebuilding knowledge graph...');
104
- runAstriaUpdate();
105
- } catch {}
106
- // astria-checkout-hook-end
107
- `;
108
- const LEGACY_HOOK_PREFIXES = [
109
- { js: 'nodesify-graphify' }, // 0.3–0.9 JS-format hooks
110
- { shell: 'nodesify-graphify' }, // pre-0.3 shell-format hooks
111
- ];
112
- exports.LEGACY_HOOK_PREFIXES = LEGACY_HOOK_PREFIXES;
113
- const HOOK_DEFS = [
114
- {
115
- hookName: 'post-commit',
116
- script: POST_COMMIT_SCRIPT,
117
- startMarker: '// astria-hook-start',
118
- endMarker: '// astria-hook-end',
119
- legacyMarkers: [
120
- { startMarker: '// nodesify-graphify-hook-start', endMarker: '// nodesify-graphify-hook-end' },
121
- { startMarker: '# nodesify-graphify-hook-start', endMarker: '# nodesify-graphify-hook-end' },
122
- ],
123
- },
124
- {
125
- hookName: 'post-checkout',
126
- script: POST_CHECKOUT_SCRIPT,
127
- startMarker: '// astria-checkout-hook-start',
128
- endMarker: '// astria-checkout-hook-end',
129
- legacyMarkers: [
130
- { startMarker: '// nodesify-graphify-checkout-hook-start', endMarker: '// nodesify-graphify-checkout-hook-end' },
131
- { startMarker: '# nodesify-graphify-checkout-hook-start', endMarker: '# nodesify-graphify-checkout-hook-end' },
132
- ],
133
- },
134
- ];
135
- const SHEBANGS = ['#!/bin/sh', '#!/bin/bash', '#!/usr/bin/env node'];
136
- function escapeRegExp(s) {
137
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
138
- }
139
- function stripMarkerSection(content, startMarker, endMarker) {
140
- const regex = new RegExp('\\n*' + escapeRegExp(startMarker) + '[\\s\\S]*?' + escapeRegExp(endMarker) + '\\n*', 'g');
141
- return content.replace(regex, '\n');
142
- }
143
- function stripAllLegacySections(content, def) {
144
- for (const legacy of def.legacyMarkers) {
145
- content = stripMarkerSection(content, legacy.startMarker, legacy.endMarker);
146
- }
147
- return content;
148
- }
149
- function hasLegacyMarker(content, def) {
150
- return def.legacyMarkers.some((l) => content.includes(l.startMarker));
151
- }
152
- function isOwnShebangOnly(content) {
153
- const trimmed = content.trim();
154
- return trimmed === '' || SHEBANGS.includes(trimmed);
155
- }
156
- function getGitRoot(projectDir) {
157
- try {
158
- const result = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
159
- cwd: projectDir,
160
- encoding: 'utf-8',
161
- }).trim();
162
- return result;
163
- }
164
- catch {
165
- return null;
166
- }
167
- }
168
- function getHooksDir(gitRoot) {
169
- try {
170
- const customPath = (0, child_process_1.execSync)('git config core.hooksPath', {
171
- cwd: gitRoot,
172
- encoding: 'utf-8',
173
- }).trim();
174
- if (customPath) {
175
- return path.isAbsolute(customPath) ? customPath : path.join(gitRoot, customPath);
176
- }
177
- }
178
- catch {
179
- // no custom hooks path
180
- }
181
- return path.join(gitRoot, '.git', 'hooks');
182
- }
183
- /// Git hook names come from the fixed HOOK_DEFS allowlist; guard the join
184
- /// anyway so no runtime-influenced name can reach the hooks directory path.
185
- function safeHookName(name) {
186
- return /^[a-zA-Z0-9._-]+$/.test(name);
187
- }
188
- function hookPathOrNull(hooksDir, hookName) {
189
- if (!safeHookName(hookName))
190
- return null;
191
- return path.join(hooksDir, hookName);
192
- }
193
- function installHook(hooksDir, def) {
194
- const hookPath = hookPathOrNull(hooksDir, def.hookName);
195
- if (!hookPath)
196
- return `${def.hookName}: skipped (unsafe hook name)`;
197
- if (!fs.existsSync(hooksDir)) {
198
- fs.mkdirSync(hooksDir, { recursive: true });
199
- }
200
- if (fs.existsSync(hookPath)) {
201
- let content = fs.readFileSync(hookPath, 'utf-8');
202
- const hadLegacy = hasLegacyMarker(content, def);
203
- if (hadLegacy) {
204
- content = stripAllLegacySections(content, def);
205
- }
206
- if (isOwnShebangOnly(content)) {
207
- // File contained only our legacy section - rewrite fresh in current format
208
- fs.writeFileSync(hookPath, '#!/usr/bin/env node\n\n' + def.script, 'utf-8');
209
- return hadLegacy
210
- ? `${def.hookName}: migrated legacy hook to current format`
211
- : `${def.hookName}: installed`;
212
- }
213
- if (content.includes(def.startMarker)) {
214
- // Refresh the script body when it predates the current template
215
- // (sentinel: ASTRIA_HOOK_VERSION resolver). Without this, fixed
216
- // templates would never reach already-installed hooks.
217
- if (!content.includes("ASTRIA_HOOK_VERSION = '3'")) {
218
- content = stripMarkerSection(content, def.startMarker, def.endMarker);
219
- const refreshed = content.trim() === '' || SHEBANGS.includes(content.trim())
220
- ? '#!/usr/bin/env node\n\n' + def.script
221
- : content.trimEnd() + '\n\n' + def.script;
222
- fs.writeFileSync(hookPath, refreshed, 'utf-8');
223
- return `${def.hookName}: updated script to current version`;
224
- }
225
- if (hadLegacy) {
226
- fs.writeFileSync(hookPath, content, 'utf-8');
227
- return `${def.hookName}: already installed (stale legacy section removed)`;
228
- }
229
- return `${def.hookName}: already installed`;
230
- }
231
- fs.writeFileSync(hookPath, content.trimEnd() + '\n\n' + def.script, 'utf-8');
232
- return hadLegacy
233
- ? `${def.hookName}: appended to existing hook (replaced legacy section)`
234
- : `${def.hookName}: appended to existing hook`;
235
- }
236
- fs.writeFileSync(hookPath, '#!/usr/bin/env node\n\n' + def.script, 'utf-8');
237
- try {
238
- fs.chmodSync(hookPath, 0o755);
239
- }
240
- catch { /* Windows */ }
241
- return `${def.hookName}: installed`;
242
- }
243
- function uninstallHook(hooksDir, def) {
244
- const hookPath = hookPathOrNull(hooksDir, def.hookName);
245
- if (!hookPath)
246
- return `${def.hookName}: skipped (unsafe hook name)`;
247
- if (!fs.existsSync(hookPath)) {
248
- return `${def.hookName}: not found`;
249
- }
250
- let content = fs.readFileSync(hookPath, 'utf-8');
251
- if (!content.includes(def.startMarker) && !hasLegacyMarker(content, def)) {
252
- return `${def.hookName}: not installed`;
253
- }
254
- content = stripMarkerSection(content, def.startMarker, def.endMarker);
255
- content = stripAllLegacySections(content, def);
256
- if (isOwnShebangOnly(content)) {
257
- fs.unlinkSync(hookPath);
258
- return `${def.hookName}: removed (deleted empty hook)`;
259
- }
260
- fs.writeFileSync(hookPath, content, 'utf-8');
261
- return `${def.hookName}: removed`;
262
- }
263
- function installGitHooks(projectDir) {
264
- const gitRoot = getGitRoot(projectDir);
265
- if (!gitRoot)
266
- return ['Not a git repository'];
267
- const hooksDir = getHooksDir(gitRoot);
268
- return HOOK_DEFS.map(def => installHook(hooksDir, def));
269
- }
270
- function uninstallGitHooks(projectDir) {
271
- const gitRoot = getGitRoot(projectDir);
272
- if (!gitRoot)
273
- return ['Not a git repository'];
274
- const hooksDir = getHooksDir(gitRoot);
275
- return HOOK_DEFS.map(def => uninstallHook(hooksDir, def));
276
- }
277
- function statusGitHooks(projectDir) {
278
- const gitRoot = getGitRoot(projectDir);
279
- if (!gitRoot)
280
- return ['Not a git repository'];
281
- const hooksDir = getHooksDir(gitRoot);
282
- const results = [];
283
- for (const def of HOOK_DEFS) {
284
- const hookPath = hookPathOrNull(hooksDir, def.hookName);
285
- if (!hookPath) {
286
- results.push(`${def.hookName}: skipped (unsafe hook name)`);
287
- continue;
288
- }
289
- if (fs.existsSync(hookPath)) {
290
- const content = fs.readFileSync(hookPath, 'utf-8');
291
- if (content.includes(def.startMarker)) {
292
- results.push(`${def.hookName}: installed`);
293
- }
294
- else if (hasLegacyMarker(content, def)) {
295
- results.push(`${def.hookName}: installed (legacy format - run hook install to migrate)`);
296
- }
297
- else {
298
- results.push(`${def.hookName}: not installed`);
299
- }
300
- }
301
- else {
302
- results.push(`${def.hookName}: not installed`);
303
- }
304
- }
305
- return results;
306
- }
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LEGACY_HOOK_PREFIXES = void 0;
37
+ exports.installGitHooks = installGitHooks;
38
+ exports.uninstallGitHooks = uninstallGitHooks;
39
+ exports.statusGitHooks = statusGitHooks;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const child_process_1 = require("child_process");
43
+ const UPDATE_HELPER = `
44
+ const ASTRIA_HOOK_VERSION = '3';
45
+ // Prefer a workspace-local CLI, then a locally-installed package, and only
46
+ // then whatever is on PATH — a stale global install would rebuild the graph
47
+ // with old pipeline code and silently regress the report.
48
+ function runAstriaUpdate() {
49
+ if (existsSync(path.join('packages', 'astria-cli', 'dist', 'index.js'))) {
50
+ execSync('node packages/astria-cli/dist/index.js update .', { stdio: 'inherit' });
51
+ return;
52
+ }
53
+ try {
54
+ execSync('npx --no-install astria update .', { stdio: 'inherit' });
55
+ } catch {
56
+ execSync('astria update .', { stdio: 'inherit' });
57
+ }
58
+ }
59
+ `;
60
+ const POST_COMMIT_SCRIPT = `// astria-hook-start
61
+ const { execSync } = require('child_process');
62
+ const { existsSync } = require('fs');
63
+ const path = require('path');
64
+ ${UPDATE_HELPER}
65
+ try {
66
+ // No shell redirects here: execSync uses cmd.exe on Windows where
67
+ // "2>/dev/null" fails with "The system cannot find the path specified".
68
+ // execSync captures stderr into the error object anyway.
69
+ const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf-8' }).trim();
70
+ const checks = [
71
+ path.join(gitDir, 'rebase-merge'),
72
+ path.join(gitDir, 'rebase-apply'),
73
+ path.join(gitDir, 'MERGE_HEAD'),
74
+ path.join(gitDir, 'CHERRY_PICK_HEAD'),
75
+ ];
76
+ if (checks.some(p => existsSync(p))) process.exit(0);
77
+
78
+ const changed = execSync('git diff --name-only HEAD~1 HEAD || git diff --name-only HEAD', { encoding: 'utf-8' }).trim();
79
+ if (!changed) process.exit(0);
80
+
81
+ const codeExts = new Set(['.py', '.js', '.ts', '.tsx', '.jsx', '.rs', '.go', '.java', '.c', '.h', '.cpp', '.cc', '.cxx', '.hpp']);
82
+ const hasCode = changed.split(/\\r?\\n/).some(f => codeExts.has(path.extname(f)));
83
+ if (hasCode && existsSync('.astria')) {
84
+ runAstriaUpdate();
85
+ }
86
+ } catch {}
87
+ // astria-hook-end
88
+ `;
89
+ const POST_CHECKOUT_SCRIPT = `// astria-checkout-hook-start
90
+ const { execSync } = require('child_process');
91
+ const { existsSync } = require('fs');
92
+ const path = require('path');
93
+ ${UPDATE_HELPER}
94
+ const branchSwitch = process.argv[3];
95
+ if (branchSwitch !== '1') process.exit(0);
96
+ if (!existsSync('.astria')) process.exit(0);
97
+
98
+ try {
99
+ // No shell redirects — see the note in the post-commit script.
100
+ const gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf-8' }).trim();
101
+ if (existsSync(path.join(gitDir, 'rebase-merge')) || existsSync(path.join(gitDir, 'rebase-apply'))) process.exit(0);
102
+
103
+ console.log('[astria] Branch switched - rebuilding knowledge graph...');
104
+ runAstriaUpdate();
105
+ } catch {}
106
+ // astria-checkout-hook-end
107
+ `;
108
+ const LEGACY_HOOK_PREFIXES = [
109
+ { js: 'nodesify-graphify' }, // 0.3–0.9 JS-format hooks
110
+ { shell: 'nodesify-graphify' }, // pre-0.3 shell-format hooks
111
+ ];
112
+ exports.LEGACY_HOOK_PREFIXES = LEGACY_HOOK_PREFIXES;
113
+ const HOOK_DEFS = [
114
+ {
115
+ hookName: 'post-commit',
116
+ script: POST_COMMIT_SCRIPT,
117
+ startMarker: '// astria-hook-start',
118
+ endMarker: '// astria-hook-end',
119
+ legacyMarkers: [
120
+ { startMarker: '// nodesify-graphify-hook-start', endMarker: '// nodesify-graphify-hook-end' },
121
+ { startMarker: '# nodesify-graphify-hook-start', endMarker: '# nodesify-graphify-hook-end' },
122
+ ],
123
+ },
124
+ {
125
+ hookName: 'post-checkout',
126
+ script: POST_CHECKOUT_SCRIPT,
127
+ startMarker: '// astria-checkout-hook-start',
128
+ endMarker: '// astria-checkout-hook-end',
129
+ legacyMarkers: [
130
+ { startMarker: '// nodesify-graphify-checkout-hook-start', endMarker: '// nodesify-graphify-checkout-hook-end' },
131
+ { startMarker: '# nodesify-graphify-checkout-hook-start', endMarker: '# nodesify-graphify-checkout-hook-end' },
132
+ ],
133
+ },
134
+ ];
135
+ const SHEBANGS = ['#!/bin/sh', '#!/bin/bash', '#!/usr/bin/env node'];
136
+ function escapeRegExp(s) {
137
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
138
+ }
139
+ function stripMarkerSection(content, startMarker, endMarker) {
140
+ const regex = new RegExp('\\n*' + escapeRegExp(startMarker) + '[\\s\\S]*?' + escapeRegExp(endMarker) + '\\n*', 'g');
141
+ return content.replace(regex, '\n');
142
+ }
143
+ function stripAllLegacySections(content, def) {
144
+ for (const legacy of def.legacyMarkers) {
145
+ content = stripMarkerSection(content, legacy.startMarker, legacy.endMarker);
146
+ }
147
+ return content;
148
+ }
149
+ function hasLegacyMarker(content, def) {
150
+ return def.legacyMarkers.some((l) => content.includes(l.startMarker));
151
+ }
152
+ function isOwnShebangOnly(content) {
153
+ const trimmed = content.trim();
154
+ return trimmed === '' || SHEBANGS.includes(trimmed);
155
+ }
156
+ function getGitRoot(projectDir) {
157
+ try {
158
+ const result = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
159
+ cwd: projectDir,
160
+ encoding: 'utf-8',
161
+ }).trim();
162
+ return result;
163
+ }
164
+ catch {
165
+ return null;
166
+ }
167
+ }
168
+ function getHooksDir(gitRoot) {
169
+ try {
170
+ const customPath = (0, child_process_1.execSync)('git config core.hooksPath', {
171
+ cwd: gitRoot,
172
+ encoding: 'utf-8',
173
+ }).trim();
174
+ if (customPath) {
175
+ return path.isAbsolute(customPath) ? customPath : path.join(gitRoot, customPath);
176
+ }
177
+ }
178
+ catch {
179
+ // no custom hooks path
180
+ }
181
+ return path.join(gitRoot, '.git', 'hooks');
182
+ }
183
+ /// Git hook names come from the fixed HOOK_DEFS allowlist; guard the join
184
+ /// anyway so no runtime-influenced name can reach the hooks directory path.
185
+ function safeHookName(name) {
186
+ return /^[a-zA-Z0-9._-]+$/.test(name);
187
+ }
188
+ function hookPathOrNull(hooksDir, hookName) {
189
+ if (!safeHookName(hookName))
190
+ return null;
191
+ return path.join(hooksDir, hookName);
192
+ }
193
+ function installHook(hooksDir, def) {
194
+ const hookPath = hookPathOrNull(hooksDir, def.hookName);
195
+ if (!hookPath)
196
+ return `${def.hookName}: skipped (unsafe hook name)`;
197
+ if (!fs.existsSync(hooksDir)) {
198
+ fs.mkdirSync(hooksDir, { recursive: true });
199
+ }
200
+ if (fs.existsSync(hookPath)) {
201
+ let content = fs.readFileSync(hookPath, 'utf-8');
202
+ const hadLegacy = hasLegacyMarker(content, def);
203
+ if (hadLegacy) {
204
+ content = stripAllLegacySections(content, def);
205
+ }
206
+ if (isOwnShebangOnly(content)) {
207
+ // File contained only our legacy section - rewrite fresh in current format
208
+ fs.writeFileSync(hookPath, '#!/usr/bin/env node\n\n' + def.script, 'utf-8');
209
+ return hadLegacy
210
+ ? `${def.hookName}: migrated legacy hook to current format`
211
+ : `${def.hookName}: installed`;
212
+ }
213
+ if (content.includes(def.startMarker)) {
214
+ // Refresh the script body when it predates the current template
215
+ // (sentinel: ASTRIA_HOOK_VERSION resolver). Without this, fixed
216
+ // templates would never reach already-installed hooks.
217
+ if (!content.includes("ASTRIA_HOOK_VERSION = '3'")) {
218
+ content = stripMarkerSection(content, def.startMarker, def.endMarker);
219
+ const refreshed = content.trim() === '' || SHEBANGS.includes(content.trim())
220
+ ? '#!/usr/bin/env node\n\n' + def.script
221
+ : content.trimEnd() + '\n\n' + def.script;
222
+ fs.writeFileSync(hookPath, refreshed, 'utf-8');
223
+ return `${def.hookName}: updated script to current version`;
224
+ }
225
+ if (hadLegacy) {
226
+ fs.writeFileSync(hookPath, content, 'utf-8');
227
+ return `${def.hookName}: already installed (stale legacy section removed)`;
228
+ }
229
+ return `${def.hookName}: already installed`;
230
+ }
231
+ fs.writeFileSync(hookPath, content.trimEnd() + '\n\n' + def.script, 'utf-8');
232
+ return hadLegacy
233
+ ? `${def.hookName}: appended to existing hook (replaced legacy section)`
234
+ : `${def.hookName}: appended to existing hook`;
235
+ }
236
+ fs.writeFileSync(hookPath, '#!/usr/bin/env node\n\n' + def.script, 'utf-8');
237
+ try {
238
+ fs.chmodSync(hookPath, 0o755);
239
+ }
240
+ catch { /* Windows */ }
241
+ return `${def.hookName}: installed`;
242
+ }
243
+ function uninstallHook(hooksDir, def) {
244
+ const hookPath = hookPathOrNull(hooksDir, def.hookName);
245
+ if (!hookPath)
246
+ return `${def.hookName}: skipped (unsafe hook name)`;
247
+ if (!fs.existsSync(hookPath)) {
248
+ return `${def.hookName}: not found`;
249
+ }
250
+ let content = fs.readFileSync(hookPath, 'utf-8');
251
+ if (!content.includes(def.startMarker) && !hasLegacyMarker(content, def)) {
252
+ return `${def.hookName}: not installed`;
253
+ }
254
+ content = stripMarkerSection(content, def.startMarker, def.endMarker);
255
+ content = stripAllLegacySections(content, def);
256
+ if (isOwnShebangOnly(content)) {
257
+ fs.unlinkSync(hookPath);
258
+ return `${def.hookName}: removed (deleted empty hook)`;
259
+ }
260
+ fs.writeFileSync(hookPath, content, 'utf-8');
261
+ return `${def.hookName}: removed`;
262
+ }
263
+ function installGitHooks(projectDir) {
264
+ const gitRoot = getGitRoot(projectDir);
265
+ if (!gitRoot)
266
+ return ['Not a git repository'];
267
+ const hooksDir = getHooksDir(gitRoot);
268
+ return HOOK_DEFS.map(def => installHook(hooksDir, def));
269
+ }
270
+ function uninstallGitHooks(projectDir) {
271
+ const gitRoot = getGitRoot(projectDir);
272
+ if (!gitRoot)
273
+ return ['Not a git repository'];
274
+ const hooksDir = getHooksDir(gitRoot);
275
+ return HOOK_DEFS.map(def => uninstallHook(hooksDir, def));
276
+ }
277
+ function statusGitHooks(projectDir) {
278
+ const gitRoot = getGitRoot(projectDir);
279
+ if (!gitRoot)
280
+ return ['Not a git repository'];
281
+ const hooksDir = getHooksDir(gitRoot);
282
+ const results = [];
283
+ for (const def of HOOK_DEFS) {
284
+ const hookPath = hookPathOrNull(hooksDir, def.hookName);
285
+ if (!hookPath) {
286
+ results.push(`${def.hookName}: skipped (unsafe hook name)`);
287
+ continue;
288
+ }
289
+ if (fs.existsSync(hookPath)) {
290
+ const content = fs.readFileSync(hookPath, 'utf-8');
291
+ if (content.includes(def.startMarker)) {
292
+ results.push(`${def.hookName}: installed`);
293
+ }
294
+ else if (hasLegacyMarker(content, def)) {
295
+ results.push(`${def.hookName}: installed (legacy format - run hook install to migrate)`);
296
+ }
297
+ else {
298
+ results.push(`${def.hookName}: not installed`);
299
+ }
300
+ }
301
+ else {
302
+ results.push(`${def.hookName}: not installed`);
303
+ }
304
+ }
305
+ return results;
306
+ }
307
307
  //# sourceMappingURL=hooks.js.map
@@ -1,3 +1,3 @@
1
- export declare function installPlatform(platform: string, projectDir: string): string[];
2
- export declare function uninstallPlatform(platform: string, projectDir: string): string[];
1
+ export declare function installPlatform(platform: string, projectDir: string): string[];
2
+ export declare function uninstallPlatform(platform: string, projectDir: string): string[];
3
3
  //# sourceMappingURL=index.d.ts.map