@shipsafe/cli 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 (166) hide show
  1. package/README.md +167 -0
  2. package/dist/bin/shipsafe.d.ts +3 -0
  3. package/dist/bin/shipsafe.d.ts.map +1 -0
  4. package/dist/bin/shipsafe.js +33 -0
  5. package/dist/bin/shipsafe.js.map +1 -0
  6. package/dist/src/autofix/pr-generator.d.ts +48 -0
  7. package/dist/src/autofix/pr-generator.d.ts.map +1 -0
  8. package/dist/src/autofix/pr-generator.js +359 -0
  9. package/dist/src/autofix/pr-generator.js.map +1 -0
  10. package/dist/src/autofix/scaffolding.d.ts +26 -0
  11. package/dist/src/autofix/scaffolding.d.ts.map +1 -0
  12. package/dist/src/autofix/scaffolding.js +249 -0
  13. package/dist/src/autofix/scaffolding.js.map +1 -0
  14. package/dist/src/autofix/secret-fixer.d.ts +27 -0
  15. package/dist/src/autofix/secret-fixer.d.ts.map +1 -0
  16. package/dist/src/autofix/secret-fixer.js +138 -0
  17. package/dist/src/autofix/secret-fixer.js.map +1 -0
  18. package/dist/src/claude-md/manager.d.ts +17 -0
  19. package/dist/src/claude-md/manager.d.ts.map +1 -0
  20. package/dist/src/claude-md/manager.js +143 -0
  21. package/dist/src/claude-md/manager.js.map +1 -0
  22. package/dist/src/cli/activate.d.ts +4 -0
  23. package/dist/src/cli/activate.d.ts.map +1 -0
  24. package/dist/src/cli/activate.js +53 -0
  25. package/dist/src/cli/activate.js.map +1 -0
  26. package/dist/src/cli/config.d.ts +21 -0
  27. package/dist/src/cli/config.d.ts.map +1 -0
  28. package/dist/src/cli/config.js +128 -0
  29. package/dist/src/cli/config.js.map +1 -0
  30. package/dist/src/cli/connect.d.ts +36 -0
  31. package/dist/src/cli/connect.d.ts.map +1 -0
  32. package/dist/src/cli/connect.js +107 -0
  33. package/dist/src/cli/connect.js.map +1 -0
  34. package/dist/src/cli/init.d.ts +12 -0
  35. package/dist/src/cli/init.d.ts.map +1 -0
  36. package/dist/src/cli/init.js +45 -0
  37. package/dist/src/cli/init.js.map +1 -0
  38. package/dist/src/cli/license-check.d.ts +7 -0
  39. package/dist/src/cli/license-check.d.ts.map +1 -0
  40. package/dist/src/cli/license-check.js +69 -0
  41. package/dist/src/cli/license-check.js.map +1 -0
  42. package/dist/src/cli/license-gate.d.ts +9 -0
  43. package/dist/src/cli/license-gate.d.ts.map +1 -0
  44. package/dist/src/cli/license-gate.js +25 -0
  45. package/dist/src/cli/license-gate.js.map +1 -0
  46. package/dist/src/cli/scan.d.ts +9 -0
  47. package/dist/src/cli/scan.d.ts.map +1 -0
  48. package/dist/src/cli/scan.js +75 -0
  49. package/dist/src/cli/scan.js.map +1 -0
  50. package/dist/src/cli/setup.d.ts +27 -0
  51. package/dist/src/cli/setup.d.ts.map +1 -0
  52. package/dist/src/cli/setup.js +134 -0
  53. package/dist/src/cli/setup.js.map +1 -0
  54. package/dist/src/cli/status.d.ts +4 -0
  55. package/dist/src/cli/status.d.ts.map +1 -0
  56. package/dist/src/cli/status.js +52 -0
  57. package/dist/src/cli/status.js.map +1 -0
  58. package/dist/src/cli/upload-sourcemaps.d.ts +13 -0
  59. package/dist/src/cli/upload-sourcemaps.d.ts.map +1 -0
  60. package/dist/src/cli/upload-sourcemaps.js +157 -0
  61. package/dist/src/cli/upload-sourcemaps.js.map +1 -0
  62. package/dist/src/config/manager.d.ts +37 -0
  63. package/dist/src/config/manager.d.ts.map +1 -0
  64. package/dist/src/config/manager.js +131 -0
  65. package/dist/src/config/manager.js.map +1 -0
  66. package/dist/src/constants.d.ts +28 -0
  67. package/dist/src/constants.d.ts.map +1 -0
  68. package/dist/src/constants.js +34 -0
  69. package/dist/src/constants.js.map +1 -0
  70. package/dist/src/engines/graph/data-flow.d.ts +36 -0
  71. package/dist/src/engines/graph/data-flow.d.ts.map +1 -0
  72. package/dist/src/engines/graph/data-flow.js +189 -0
  73. package/dist/src/engines/graph/data-flow.js.map +1 -0
  74. package/dist/src/engines/graph/index.d.ts +20 -0
  75. package/dist/src/engines/graph/index.d.ts.map +1 -0
  76. package/dist/src/engines/graph/index.js +100 -0
  77. package/dist/src/engines/graph/index.js.map +1 -0
  78. package/dist/src/engines/graph/parser.d.ts +13 -0
  79. package/dist/src/engines/graph/parser.d.ts.map +1 -0
  80. package/dist/src/engines/graph/parser.js +620 -0
  81. package/dist/src/engines/graph/parser.js.map +1 -0
  82. package/dist/src/engines/graph/queries.d.ts +11 -0
  83. package/dist/src/engines/graph/queries.d.ts.map +1 -0
  84. package/dist/src/engines/graph/queries.js +196 -0
  85. package/dist/src/engines/graph/queries.js.map +1 -0
  86. package/dist/src/engines/graph/store.d.ts +35 -0
  87. package/dist/src/engines/graph/store.d.ts.map +1 -0
  88. package/dist/src/engines/graph/store.js +284 -0
  89. package/dist/src/engines/graph/store.js.map +1 -0
  90. package/dist/src/engines/pattern/gitleaks.d.ts +4 -0
  91. package/dist/src/engines/pattern/gitleaks.d.ts.map +1 -0
  92. package/dist/src/engines/pattern/gitleaks.js +78 -0
  93. package/dist/src/engines/pattern/gitleaks.js.map +1 -0
  94. package/dist/src/engines/pattern/index.d.ts +11 -0
  95. package/dist/src/engines/pattern/index.d.ts.map +1 -0
  96. package/dist/src/engines/pattern/index.js +111 -0
  97. package/dist/src/engines/pattern/index.js.map +1 -0
  98. package/dist/src/engines/pattern/semgrep.d.ts +4 -0
  99. package/dist/src/engines/pattern/semgrep.d.ts.map +1 -0
  100. package/dist/src/engines/pattern/semgrep.js +83 -0
  101. package/dist/src/engines/pattern/semgrep.js.map +1 -0
  102. package/dist/src/engines/pattern/trivy.d.ts +4 -0
  103. package/dist/src/engines/pattern/trivy.d.ts.map +1 -0
  104. package/dist/src/engines/pattern/trivy.js +90 -0
  105. package/dist/src/engines/pattern/trivy.js.map +1 -0
  106. package/dist/src/github/api.d.ts +19 -0
  107. package/dist/src/github/api.d.ts.map +1 -0
  108. package/dist/src/github/api.js +75 -0
  109. package/dist/src/github/api.js.map +1 -0
  110. package/dist/src/github/app-manifest.d.ts +28 -0
  111. package/dist/src/github/app-manifest.d.ts.map +1 -0
  112. package/dist/src/github/app-manifest.js +27 -0
  113. package/dist/src/github/app-manifest.js.map +1 -0
  114. package/dist/src/github/checks.d.ts +36 -0
  115. package/dist/src/github/checks.d.ts.map +1 -0
  116. package/dist/src/github/checks.js +90 -0
  117. package/dist/src/github/checks.js.map +1 -0
  118. package/dist/src/github/scanner.d.ts +20 -0
  119. package/dist/src/github/scanner.d.ts.map +1 -0
  120. package/dist/src/github/scanner.js +78 -0
  121. package/dist/src/github/scanner.js.map +1 -0
  122. package/dist/src/github/webhook.d.ts +39 -0
  123. package/dist/src/github/webhook.d.ts.map +1 -0
  124. package/dist/src/github/webhook.js +80 -0
  125. package/dist/src/github/webhook.js.map +1 -0
  126. package/dist/src/hooks/installer.d.ts +4 -0
  127. package/dist/src/hooks/installer.d.ts.map +1 -0
  128. package/dist/src/hooks/installer.js +146 -0
  129. package/dist/src/hooks/installer.js.map +1 -0
  130. package/dist/src/mcp/server.d.ts +2 -0
  131. package/dist/src/mcp/server.d.ts.map +1 -0
  132. package/dist/src/mcp/server.js +96 -0
  133. package/dist/src/mcp/server.js.map +1 -0
  134. package/dist/src/mcp/tools/check-package.d.ts +30 -0
  135. package/dist/src/mcp/tools/check-package.d.ts.map +1 -0
  136. package/dist/src/mcp/tools/check-package.js +196 -0
  137. package/dist/src/mcp/tools/check-package.js.map +1 -0
  138. package/dist/src/mcp/tools/fix.d.ts +41 -0
  139. package/dist/src/mcp/tools/fix.d.ts.map +1 -0
  140. package/dist/src/mcp/tools/fix.js +98 -0
  141. package/dist/src/mcp/tools/fix.js.map +1 -0
  142. package/dist/src/mcp/tools/graph-query.d.ts +7 -0
  143. package/dist/src/mcp/tools/graph-query.d.ts.map +1 -0
  144. package/dist/src/mcp/tools/graph-query.js +139 -0
  145. package/dist/src/mcp/tools/graph-query.js.map +1 -0
  146. package/dist/src/mcp/tools/production-errors.d.ts +23 -0
  147. package/dist/src/mcp/tools/production-errors.d.ts.map +1 -0
  148. package/dist/src/mcp/tools/production-errors.js +46 -0
  149. package/dist/src/mcp/tools/production-errors.js.map +1 -0
  150. package/dist/src/mcp/tools/scan.d.ts +7 -0
  151. package/dist/src/mcp/tools/scan.d.ts.map +1 -0
  152. package/dist/src/mcp/tools/scan.js +9 -0
  153. package/dist/src/mcp/tools/scan.js.map +1 -0
  154. package/dist/src/mcp/tools/status.d.ts +9 -0
  155. package/dist/src/mcp/tools/status.d.ts.map +1 -0
  156. package/dist/src/mcp/tools/status.js +18 -0
  157. package/dist/src/mcp/tools/status.js.map +1 -0
  158. package/dist/src/mcp/tools/verify-resolution.d.ts +12 -0
  159. package/dist/src/mcp/tools/verify-resolution.d.ts.map +1 -0
  160. package/dist/src/mcp/tools/verify-resolution.js +45 -0
  161. package/dist/src/mcp/tools/verify-resolution.js.map +1 -0
  162. package/dist/src/types.d.ts +136 -0
  163. package/dist/src/types.d.ts.map +1 -0
  164. package/dist/src/types.js +2 -0
  165. package/dist/src/types.js.map +1 -0
  166. package/package.json +53 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffolding.d.ts","sourceRoot":"","sources":["../../../src/autofix/scaffolding.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,sBAAsB,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EACA,gBAAgB,GAChB,cAAc,GACd,oBAAoB,GACpB,aAAa,GACb,oBAAoB,GACpB,iBAAiB,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACrC;AAED,UAAU,WAAW;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAavE;AA4MD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAmDjC"}
@@ -0,0 +1,249 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ /**
4
+ * Detect the framework from package.json dependencies.
5
+ */
6
+ export function detectFramework(packageJson) {
7
+ const allDeps = {
8
+ ...packageJson.dependencies,
9
+ ...packageJson.devDependencies,
10
+ };
11
+ // Order matters — more specific frameworks first
12
+ if (allDeps['next'])
13
+ return 'nextjs';
14
+ if (allDeps['hono'])
15
+ return 'hono';
16
+ if (allDeps['fastify'])
17
+ return 'fastify';
18
+ if (allDeps['express'])
19
+ return 'express';
20
+ return null;
21
+ }
22
+ /**
23
+ * Check if a package is present in the project dependencies.
24
+ */
25
+ function hasDep(packageJson, name) {
26
+ return !!(packageJson.dependencies?.[name] || packageJson.devDependencies?.[name]);
27
+ }
28
+ /**
29
+ * Check if .env is listed in .gitignore.
30
+ */
31
+ async function checkEnvInGitignore(projectDir) {
32
+ try {
33
+ const content = await fs.readFile(path.join(projectDir, '.gitignore'), 'utf-8');
34
+ return content.split('\n').some((line) => {
35
+ const trimmed = line.trim();
36
+ return trimmed === '.env' || trimmed === '.env*' || trimmed === '.env.*';
37
+ });
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ /**
44
+ * Generate Express-specific security recommendations.
45
+ */
46
+ function getExpressRecommendations(packageJson) {
47
+ const recs = [];
48
+ if (!hasDep(packageJson, 'helmet')) {
49
+ recs.push({
50
+ type: 'missing_helmet',
51
+ description: 'Express app is missing Helmet — HTTP headers are not secured against common attacks (XSS, clickjacking, MIME sniffing).',
52
+ fix: `// npm install helmet
53
+ import helmet from 'helmet';
54
+ app.use(helmet());`,
55
+ file: 'src/app.ts',
56
+ priority: 'high',
57
+ });
58
+ }
59
+ if (!hasDep(packageJson, 'cors')) {
60
+ recs.push({
61
+ type: 'missing_cors',
62
+ description: 'No CORS middleware detected. Without explicit CORS configuration, the API may reject legitimate cross-origin requests or allow unintended origins.',
63
+ fix: `// npm install cors
64
+ import cors from 'cors';
65
+ app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') ?? [] }));`,
66
+ file: 'src/app.ts',
67
+ priority: 'medium',
68
+ });
69
+ }
70
+ if (!hasDep(packageJson, 'express-rate-limit') && !hasDep(packageJson, 'rate-limiter-flexible')) {
71
+ recs.push({
72
+ type: 'missing_rate_limit',
73
+ description: 'No rate limiting middleware detected. API endpoints are vulnerable to brute-force and denial-of-service attacks.',
74
+ fix: `// npm install express-rate-limit
75
+ import rateLimit from 'express-rate-limit';
76
+ app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));`,
77
+ file: 'src/app.ts',
78
+ priority: 'high',
79
+ });
80
+ }
81
+ return recs;
82
+ }
83
+ /**
84
+ * Generate Next.js-specific security recommendations.
85
+ */
86
+ function getNextjsRecommendations(packageJson) {
87
+ const recs = [];
88
+ // Next.js CSP headers
89
+ recs.push({
90
+ type: 'missing_csp',
91
+ description: 'No Content Security Policy headers detected. CSP prevents XSS, data injection, and other code-injection attacks.',
92
+ fix: `// In next.config.js or middleware.ts:
93
+ const cspHeader = \`
94
+ default-src 'self';
95
+ script-src 'self';
96
+ style-src 'self';
97
+ img-src 'self' blob: data:;
98
+ font-src 'self';
99
+ connect-src 'self';
100
+ frame-ancestors 'none';
101
+ \`;
102
+
103
+ // next.config.js
104
+ const nextConfig = {
105
+ async headers() {
106
+ return [{
107
+ source: '/(.*)',
108
+ headers: [{ key: 'Content-Security-Policy', value: cspHeader.replace(/\\n/g, '') }],
109
+ }];
110
+ },
111
+ };`,
112
+ file: 'next.config.js',
113
+ priority: 'high',
114
+ });
115
+ if (!hasDep(packageJson, 'rate-limiter-flexible') && !hasDep(packageJson, '@upstash/ratelimit')) {
116
+ recs.push({
117
+ type: 'missing_rate_limit',
118
+ description: 'No rate limiting detected for API routes. Next.js API routes are vulnerable to abuse without rate limiting.',
119
+ fix: `// npm install @upstash/ratelimit @upstash/redis
120
+ // In your API route or middleware:
121
+ import { Ratelimit } from '@upstash/ratelimit';
122
+ import { Redis } from '@upstash/redis';
123
+
124
+ const ratelimit = new Ratelimit({
125
+ redis: Redis.fromEnv(),
126
+ limiter: Ratelimit.slidingWindow(10, '10 s'),
127
+ });`,
128
+ file: 'src/middleware.ts',
129
+ priority: 'medium',
130
+ });
131
+ }
132
+ return recs;
133
+ }
134
+ /**
135
+ * Generate Fastify-specific security recommendations.
136
+ */
137
+ function getFastifyRecommendations(packageJson) {
138
+ const recs = [];
139
+ if (!hasDep(packageJson, '@fastify/helmet')) {
140
+ recs.push({
141
+ type: 'missing_helmet',
142
+ description: 'Fastify app is missing @fastify/helmet — HTTP security headers are not configured.',
143
+ fix: `// npm install @fastify/helmet
144
+ import helmet from '@fastify/helmet';
145
+ await app.register(helmet);`,
146
+ file: 'src/app.ts',
147
+ priority: 'high',
148
+ });
149
+ }
150
+ if (!hasDep(packageJson, '@fastify/cors')) {
151
+ recs.push({
152
+ type: 'missing_cors',
153
+ description: 'No CORS plugin registered. Cross-origin requests may not be handled correctly.',
154
+ fix: `// npm install @fastify/cors
155
+ import cors from '@fastify/cors';
156
+ await app.register(cors, { origin: process.env.ALLOWED_ORIGINS?.split(',') ?? [] });`,
157
+ file: 'src/app.ts',
158
+ priority: 'medium',
159
+ });
160
+ }
161
+ if (!hasDep(packageJson, '@fastify/rate-limit')) {
162
+ recs.push({
163
+ type: 'missing_rate_limit',
164
+ description: 'No rate limiting plugin detected. API endpoints are vulnerable to brute-force attacks.',
165
+ fix: `// npm install @fastify/rate-limit
166
+ import rateLimit from '@fastify/rate-limit';
167
+ await app.register(rateLimit, { max: 100, timeWindow: '15 minutes' });`,
168
+ file: 'src/app.ts',
169
+ priority: 'high',
170
+ });
171
+ }
172
+ return recs;
173
+ }
174
+ /**
175
+ * Generate Hono-specific security recommendations.
176
+ */
177
+ function getHonoRecommendations(packageJson) {
178
+ const recs = [];
179
+ recs.push({
180
+ type: 'missing_cors',
181
+ description: 'Ensure CORS middleware is configured for your Hono app to control cross-origin access.',
182
+ fix: `import { cors } from 'hono/cors';
183
+ app.use('*', cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') ?? [] }));`,
184
+ file: 'src/index.ts',
185
+ priority: 'medium',
186
+ });
187
+ recs.push({
188
+ type: 'missing_csp',
189
+ description: 'Add security headers middleware to protect against common web attacks.',
190
+ fix: `import { secureHeaders } from 'hono/secure-headers';
191
+ app.use('*', secureHeaders());`,
192
+ file: 'src/index.ts',
193
+ priority: 'high',
194
+ });
195
+ return recs;
196
+ }
197
+ /**
198
+ * Detect the project framework and generate security recommendations.
199
+ * Returns null if the framework cannot be detected.
200
+ */
201
+ export async function detectAndRecommend(projectDir) {
202
+ const dir = projectDir ?? process.cwd();
203
+ const packageJsonPath = path.join(dir, 'package.json');
204
+ let packageJson;
205
+ try {
206
+ const raw = await fs.readFile(packageJsonPath, 'utf-8');
207
+ packageJson = JSON.parse(raw);
208
+ }
209
+ catch {
210
+ return null; // No package.json — can't detect framework
211
+ }
212
+ const framework = detectFramework(packageJson);
213
+ if (!framework)
214
+ return null;
215
+ // Get framework-specific recommendations
216
+ let recommendations;
217
+ switch (framework) {
218
+ case 'express':
219
+ recommendations = getExpressRecommendations(packageJson);
220
+ break;
221
+ case 'nextjs':
222
+ recommendations = getNextjsRecommendations(packageJson);
223
+ break;
224
+ case 'fastify':
225
+ recommendations = getFastifyRecommendations(packageJson);
226
+ break;
227
+ case 'hono':
228
+ recommendations = getHonoRecommendations(packageJson);
229
+ break;
230
+ default:
231
+ recommendations = [];
232
+ }
233
+ // Common check: .env in .gitignore
234
+ const hasEnvInGitignore = await checkEnvInGitignore(dir);
235
+ if (!hasEnvInGitignore) {
236
+ recommendations.push({
237
+ type: 'missing_env_config',
238
+ description: '.env file is not listed in .gitignore. Environment variables containing secrets may be committed to version control.',
239
+ fix: `# Add to .gitignore:
240
+ .env
241
+ .env.*
242
+ .env.local`,
243
+ file: '.gitignore',
244
+ priority: 'high',
245
+ });
246
+ }
247
+ return { framework, recommendations };
248
+ }
249
+ //# sourceMappingURL=scaffolding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffolding.js","sourceRoot":"","sources":["../../../src/autofix/scaffolding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AA0BlC;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,WAAwB;IACtD,MAAM,OAAO,GAAG;QACd,GAAG,WAAW,CAAC,YAAY;QAC3B,GAAG,WAAW,CAAC,eAAe;KAC/B,CAAC;IAEF,iDAAiD;IACjD,IAAI,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,QAAQ,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACnC,IAAI,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAEzC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,MAAM,CAAC,WAAwB,EAAE,IAAY;IACpD,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AACrF,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,mBAAmB,CAAC,UAAkB;IACnD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QAChF,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,OAAO,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,QAAQ,CAAC;QAC3E,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,yBAAyB,CAAC,WAAwB;IACzD,MAAM,IAAI,GAA6B,EAAE,CAAC;IAE1C,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,gBAAgB;YACtB,WAAW,EACT,yHAAyH;YAC3H,GAAG,EAAE;;mBAEQ;YACb,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,cAAc;YACpB,WAAW,EACT,oJAAoJ;YACtJ,GAAG,EAAE;;0EAE+D;YACpE,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,uBAAuB,CAAC,EAAE,CAAC;QAChG,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EACT,kHAAkH;YACpH,GAAG,EAAE;;4DAEiD;YACtD,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,WAAwB;IACxD,MAAM,IAAI,GAA6B,EAAE,CAAC;IAE1C,sBAAsB;IACtB,IAAI,CAAC,IAAI,CAAC;QACR,IAAI,EAAE,aAAa;QACnB,WAAW,EACT,kHAAkH;QACpH,GAAG,EAAE;;;;;;;;;;;;;;;;;;;GAmBN;QACC,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,oBAAoB,CAAC,EAAE,CAAC;QAChG,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EACT,6GAA6G;YAC/G,GAAG,EAAE;;;;;;;;IAQP;YACE,IAAI,EAAE,mBAAmB;YACzB,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,yBAAyB,CAAC,WAAwB;IACzD,MAAM,IAAI,GAA6B,EAAE,CAAC;IAE1C,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,gBAAgB;YACtB,WAAW,EACT,oFAAoF;YACtF,GAAG,EAAE;;4BAEiB;YACtB,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,cAAc;YACpB,WAAW,EAAE,gFAAgF;YAC7F,GAAG,EAAE;;qFAE0E;YAC/E,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,QAAQ;SACnB,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,qBAAqB,CAAC,EAAE,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EACT,wFAAwF;YAC1F,GAAG,EAAE;;uEAE4D;YACjE,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,WAAwB;IACtD,MAAM,IAAI,GAA6B,EAAE,CAAC;IAE1C,IAAI,CAAC,IAAI,CAAC;QACR,IAAI,EAAE,cAAc;QACpB,WAAW,EACT,wFAAwF;QAC1F,GAAG,EAAE;+EACsE;QAC3E,IAAI,EAAE,cAAc;QACpB,QAAQ,EAAE,QAAQ;KACnB,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC;QACR,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,wEAAwE;QACrF,GAAG,EAAE;+BACsB;QAC3B,IAAI,EAAE,cAAc;QACpB,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,UAAmB;IAEnB,MAAM,GAAG,GAAG,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACxC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAEvD,IAAI,WAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QACxD,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,2CAA2C;IAC1D,CAAC;IAED,MAAM,SAAS,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAC/C,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,yCAAyC;IACzC,IAAI,eAAyC,CAAC;IAC9C,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,SAAS;YACZ,eAAe,GAAG,yBAAyB,CAAC,WAAW,CAAC,CAAC;YACzD,MAAM;QACR,KAAK,QAAQ;YACX,eAAe,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;YACxD,MAAM;QACR,KAAK,SAAS;YACZ,eAAe,GAAG,yBAAyB,CAAC,WAAW,CAAC,CAAC;YACzD,MAAM;QACR,KAAK,MAAM;YACT,eAAe,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;YACtD,MAAM;QACR;YACE,eAAe,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,mCAAmC;IACnC,MAAM,iBAAiB,GAAG,MAAM,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACzD,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,eAAe,CAAC,IAAI,CAAC;YACnB,IAAI,EAAE,oBAAoB;YAC1B,WAAW,EACT,sHAAsH;YACxH,GAAG,EAAE;;;WAGA;YACL,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { Finding } from '../types.js';
2
+ export interface SecretFix {
3
+ file: string;
4
+ line: number;
5
+ secretType: string;
6
+ envVarName: string;
7
+ filesModified: string[];
8
+ }
9
+ /**
10
+ * Generate an appropriate env var name from the secret type and surrounding context.
11
+ */
12
+ export declare function generateEnvVarName(secretType: string, context: string): string;
13
+ /**
14
+ * Detect the type of secret from the value or context.
15
+ */
16
+ export declare function detectSecretType(value: string, context: string): string;
17
+ /**
18
+ * Ensure .env is in .gitignore. Creates .gitignore if it doesn't exist.
19
+ * Returns true if .gitignore was modified, false if .env was already present.
20
+ */
21
+ export declare function ensureGitignoreHasEnv(projectDir?: string): Promise<boolean>;
22
+ /**
23
+ * Detect and fix a hardcoded secret by moving it to .env and replacing
24
+ * the inline value with a process.env reference.
25
+ */
26
+ export declare function fixHardcodedSecret(finding: Finding, projectDir?: string): Promise<SecretFix>;
27
+ //# sourceMappingURL=secret-fixer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-fixer.d.ts","sourceRoot":"","sources":["../../../src/autofix/secret-fixer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAwB9E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAgBvE;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBjF;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,SAAS,CAAC,CAkEpB"}
@@ -0,0 +1,138 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ /**
4
+ * Generate an appropriate env var name from the secret type and surrounding context.
5
+ */
6
+ export function generateEnvVarName(secretType, context) {
7
+ // Combine type and context, remove non-alphanumeric chars, convert to UPPER_SNAKE_CASE
8
+ let raw = context || secretType;
9
+ // Remove common prefixes like 'const', 'let', 'var'
10
+ raw = raw.replace(/^(?:const|let|var)\s+/, '');
11
+ // Take just the first identifier if there's an assignment (include hyphens for kebab-case)
12
+ const identMatch = raw.match(/^([\w-]+)/);
13
+ if (identMatch) {
14
+ raw = identMatch[1];
15
+ }
16
+ // Convert camelCase / PascalCase to UPPER_SNAKE_CASE
17
+ const snaked = raw
18
+ .replace(/([a-z])([A-Z])/g, '$1_$2')
19
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
20
+ .replace(/[^a-zA-Z0-9]/g, '_')
21
+ .toUpperCase();
22
+ // Remove leading/trailing underscores
23
+ const cleaned = snaked.replace(/^_+|_+$/g, '').replace(/_+/g, '_');
24
+ return cleaned || 'SECRET_VALUE';
25
+ }
26
+ /**
27
+ * Detect the type of secret from the value or context.
28
+ */
29
+ export function detectSecretType(value, context) {
30
+ const lower = (value + ' ' + context).toLowerCase();
31
+ if (/AKIA[0-9A-Z]{16}/.test(value))
32
+ return 'aws_access_key';
33
+ if (lower.includes('database') || lower.includes('postgres') || lower.includes('mysql'))
34
+ return 'database_url';
35
+ if (lower.includes('stripe') && lower.includes('sk_'))
36
+ return 'stripe_secret_key';
37
+ if (lower.includes('supabase'))
38
+ return 'supabase_key';
39
+ if (lower.includes('firebase'))
40
+ return 'firebase_key';
41
+ if (lower.includes('jwt') || lower.includes('token'))
42
+ return 'token';
43
+ if (lower.includes('password') || lower.includes('passwd'))
44
+ return 'password';
45
+ if (lower.includes('key') || lower.includes('apikey') || lower.includes('api_key'))
46
+ return 'api_key';
47
+ if (lower.includes('secret'))
48
+ return 'secret';
49
+ return 'api_key';
50
+ }
51
+ /**
52
+ * Ensure .env is in .gitignore. Creates .gitignore if it doesn't exist.
53
+ * Returns true if .gitignore was modified, false if .env was already present.
54
+ */
55
+ export async function ensureGitignoreHasEnv(projectDir) {
56
+ const dir = projectDir ?? process.cwd();
57
+ const gitignorePath = path.join(dir, '.gitignore');
58
+ let content = '';
59
+ try {
60
+ content = await fs.readFile(gitignorePath, 'utf-8');
61
+ }
62
+ catch {
63
+ // .gitignore doesn't exist — we'll create it
64
+ }
65
+ // Check if .env is already covered
66
+ const lines = content.split('\n');
67
+ const hasEnv = lines.some((line) => {
68
+ const trimmed = line.trim();
69
+ return trimmed === '.env' || trimmed === '.env*' || trimmed === '.env.*';
70
+ });
71
+ if (hasEnv)
72
+ return false;
73
+ // Append .env entry
74
+ const separator = content.length > 0 && !content.endsWith('\n') ? '\n' : '';
75
+ await fs.writeFile(gitignorePath, content + separator + '.env\n', 'utf-8');
76
+ return true;
77
+ }
78
+ /**
79
+ * Detect and fix a hardcoded secret by moving it to .env and replacing
80
+ * the inline value with a process.env reference.
81
+ */
82
+ export async function fixHardcodedSecret(finding, projectDir) {
83
+ const dir = projectDir ?? process.cwd();
84
+ const filePath = path.resolve(dir, finding.file);
85
+ const filesModified = [];
86
+ // 1. Read the file containing the hardcoded secret
87
+ const content = await fs.readFile(filePath, 'utf-8');
88
+ const lines = content.split('\n');
89
+ const targetLine = lines[finding.line - 1];
90
+ if (!targetLine) {
91
+ throw new Error(`Line ${finding.line} not found in ${finding.file}`);
92
+ }
93
+ // 2. Extract the secret value
94
+ const secretMatch = targetLine.match(/['"`]([A-Za-z0-9_\-./+=:@]{8,})['"`]/);
95
+ if (!secretMatch) {
96
+ throw new Error(`Could not extract secret value from line ${finding.line} in ${finding.file}`);
97
+ }
98
+ const secretValue = secretMatch[1];
99
+ // 3. Determine variable context
100
+ const varNameMatch = targetLine.match(/(?:const|let|var)\s+(\w+)|(\w+)\s*[:=]/);
101
+ const context = varNameMatch?.[1] ?? varNameMatch?.[2] ?? '';
102
+ // 4. Detect secret type
103
+ const secretType = detectSecretType(secretValue, context);
104
+ // 5. Generate env var name
105
+ const envVarName = generateEnvVarName(secretType, context);
106
+ // 6. Replace hardcoded value with process.env reference
107
+ const newLine = targetLine.replace(secretMatch[0], `process.env.${envVarName}`);
108
+ lines[finding.line - 1] = newLine;
109
+ await fs.writeFile(filePath, lines.join('\n'), 'utf-8');
110
+ filesModified.push(finding.file);
111
+ // 7. Append to .env
112
+ const envPath = path.join(dir, '.env');
113
+ let envContent = '';
114
+ try {
115
+ envContent = await fs.readFile(envPath, 'utf-8');
116
+ }
117
+ catch {
118
+ // .env doesn't exist yet — will be created
119
+ }
120
+ if (!envContent.includes(`${envVarName}=`)) {
121
+ const separator = envContent.length > 0 && !envContent.endsWith('\n') ? '\n' : '';
122
+ await fs.writeFile(envPath, envContent + separator + `${envVarName}=${secretValue}\n`, 'utf-8');
123
+ filesModified.push('.env');
124
+ }
125
+ // 8. Ensure .env is in .gitignore
126
+ const gitignoreModified = await ensureGitignoreHasEnv(dir);
127
+ if (gitignoreModified) {
128
+ filesModified.push('.gitignore');
129
+ }
130
+ return {
131
+ file: finding.file,
132
+ line: finding.line,
133
+ secretType,
134
+ envVarName,
135
+ filesModified,
136
+ };
137
+ }
138
+ //# sourceMappingURL=secret-fixer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-fixer.js","sourceRoot":"","sources":["../../../src/autofix/secret-fixer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Injects or updates the ShipSafe instruction block in a project's CLAUDE.md file.
3
+ * - If CLAUDE.md doesn't exist, creates it with just the ShipSafe block.
4
+ * - If it exists but has no ShipSafe block, appends the block.
5
+ * - If it exists and has a ShipSafe block, replaces it with the latest template.
6
+ */
7
+ export declare function injectClaudeMd(projectDir?: string): Promise<void>;
8
+ /**
9
+ * Removes the ShipSafe block from CLAUDE.md, preserving the rest.
10
+ * Deletes the file if it would be empty after removal.
11
+ */
12
+ export declare function removeClaudeMd(projectDir?: string): Promise<void>;
13
+ /**
14
+ * Checks whether CLAUDE.md contains a ShipSafe instruction block.
15
+ */
16
+ export declare function hasClaudeMdBlock(projectDir?: string): Promise<boolean>;
17
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/claude-md/manager.ts"],"names":[],"mappings":"AA0CA;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCvE;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA+CvE;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU5E"}
@@ -0,0 +1,143 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { CLAUDE_MD_START, CLAUDE_MD_END } from '../constants.js';
4
+ const SHIPSAFE_TEMPLATE = `${CLAUDE_MD_START}
5
+ # ShipSafe Security & Monitoring Agent
6
+
7
+ ## Session Start (MANDATORY)
8
+ At the start of EVERY session, do the following:
9
+ 1. Ask the user: "ShipSafe is active on this project. Keep security protection enabled? (yes/no)"
10
+ 2. If yes: call the \`shipsafe_status\` tool to check project state and queued production errors
11
+ 3. If there are queued production errors, present them to the user and offer to fix them BEFORE starting new work
12
+ 4. Print the one-line status: "ShipSafe: [score] | [issue count] issues | [error count] production errors"
13
+
14
+ ## Rules (Active Copilot Mode - user said yes)
15
+
16
+ ### Pre-Commit
17
+ - Before EVERY git commit, call \`shipsafe_scan\` and wait for results
18
+ - If critical or high issues are found, fix them before committing. Do NOT commit with unresolved critical/high findings.
19
+ - If medium/low issues are found, inform the user and let them decide
20
+
21
+ ### Security Practices
22
+ - NEVER hardcode API keys, tokens, secrets, or credentials. Always use environment variables.
23
+ - If you detect a hardcoded secret, move it to .env, add .env to .gitignore, and replace with process.env reference
24
+ - When installing packages, call \`shipsafe_check_package\` to verify safety before installing
25
+ - Use parameterized queries. Never concatenate user input into SQL strings.
26
+
27
+ ### Monitoring
28
+ - If this project does not have the ShipSafe monitoring snippet installed and it is a deployable web application, add it
29
+ - Do NOT remove or modify the ShipSafe monitoring snippet or git hooks
30
+
31
+ ### Production Errors
32
+ - When production errors are delivered at session start, address them BEFORE working on new features
33
+ - Use the full context provided (stack trace, root cause, suggested fix)
34
+ - After fixing a production error, inform the user what was fixed and why
35
+
36
+ ## Rules (Silent Guardian Mode - user said no)
37
+ - ShipSafe tools are available but do not proactively use them
38
+ - Git hooks will still run on commit/push
39
+ - Do NOT ask the user about ShipSafe again during this session
40
+ ${CLAUDE_MD_END}`;
41
+ /**
42
+ * Injects or updates the ShipSafe instruction block in a project's CLAUDE.md file.
43
+ * - If CLAUDE.md doesn't exist, creates it with just the ShipSafe block.
44
+ * - If it exists but has no ShipSafe block, appends the block.
45
+ * - If it exists and has a ShipSafe block, replaces it with the latest template.
46
+ */
47
+ export async function injectClaudeMd(projectDir) {
48
+ const dir = projectDir ?? process.cwd();
49
+ const filePath = path.join(dir, 'CLAUDE.md');
50
+ let existing = null;
51
+ try {
52
+ existing = await fs.readFile(filePath, 'utf-8');
53
+ }
54
+ catch {
55
+ // File doesn't exist
56
+ }
57
+ if (existing === null) {
58
+ // No file — create with just the block
59
+ await fs.writeFile(filePath, SHIPSAFE_TEMPLATE + '\n', 'utf-8');
60
+ return;
61
+ }
62
+ if (existing.includes(CLAUDE_MD_START)) {
63
+ // Replace existing block
64
+ const startIdx = existing.indexOf(CLAUDE_MD_START);
65
+ const endIdx = existing.indexOf(CLAUDE_MD_END);
66
+ if (endIdx === -1) {
67
+ // Malformed — has start but no end. Replace from start to EOF.
68
+ const before = existing.substring(0, startIdx);
69
+ await fs.writeFile(filePath, before + SHIPSAFE_TEMPLATE + '\n', 'utf-8');
70
+ return;
71
+ }
72
+ const before = existing.substring(0, startIdx);
73
+ const after = existing.substring(endIdx + CLAUDE_MD_END.length);
74
+ await fs.writeFile(filePath, before + SHIPSAFE_TEMPLATE + after, 'utf-8');
75
+ }
76
+ else {
77
+ // Append with blank line separator
78
+ const separator = existing.endsWith('\n') ? '\n' : '\n\n';
79
+ await fs.writeFile(filePath, existing + separator + SHIPSAFE_TEMPLATE + '\n', 'utf-8');
80
+ }
81
+ }
82
+ /**
83
+ * Removes the ShipSafe block from CLAUDE.md, preserving the rest.
84
+ * Deletes the file if it would be empty after removal.
85
+ */
86
+ export async function removeClaudeMd(projectDir) {
87
+ const dir = projectDir ?? process.cwd();
88
+ const filePath = path.join(dir, 'CLAUDE.md');
89
+ let content;
90
+ try {
91
+ content = await fs.readFile(filePath, 'utf-8');
92
+ }
93
+ catch {
94
+ // File doesn't exist — nothing to do
95
+ return;
96
+ }
97
+ if (!content.includes(CLAUDE_MD_START)) {
98
+ // No ShipSafe block — nothing to do
99
+ return;
100
+ }
101
+ const startIdx = content.indexOf(CLAUDE_MD_START);
102
+ const endIdx = content.indexOf(CLAUDE_MD_END);
103
+ if (endIdx === -1) {
104
+ // Malformed — has start but no end. Remove from start to EOF.
105
+ const before = content.substring(0, startIdx);
106
+ const trimmed = before.replace(/\n+$/, '');
107
+ if (trimmed.length === 0) {
108
+ await fs.unlink(filePath);
109
+ }
110
+ else {
111
+ await fs.writeFile(filePath, trimmed + '\n', 'utf-8');
112
+ }
113
+ return;
114
+ }
115
+ const before = content.substring(0, startIdx);
116
+ const after = content.substring(endIdx + CLAUDE_MD_END.length);
117
+ let result = before + after;
118
+ // Clean up multiple consecutive blank lines (3+ newlines -> 2 newlines)
119
+ result = result.replace(/\n{3,}/g, '\n\n');
120
+ // Trim trailing whitespace
121
+ result = result.replace(/\s+$/, '');
122
+ if (result.length === 0) {
123
+ await fs.unlink(filePath);
124
+ }
125
+ else {
126
+ await fs.writeFile(filePath, result + '\n', 'utf-8');
127
+ }
128
+ }
129
+ /**
130
+ * Checks whether CLAUDE.md contains a ShipSafe instruction block.
131
+ */
132
+ export async function hasClaudeMdBlock(projectDir) {
133
+ const dir = projectDir ?? process.cwd();
134
+ const filePath = path.join(dir, 'CLAUDE.md');
135
+ try {
136
+ const content = await fs.readFile(filePath, 'utf-8');
137
+ return content.includes(CLAUDE_MD_START);
138
+ }
139
+ catch {
140
+ return false;
141
+ }
142
+ }
143
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/claude-md/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ export declare function handleActivateAction(licenseKey: string): Promise<void>;
3
+ export declare function registerActivateCommand(program: Command): void;
4
+ //# sourceMappingURL=activate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activate.d.ts","sourceRoot":"","sources":["../../../src/cli/activate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAO9D"}