@jikida/init 0.3.0 → 0.4.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 (2) hide show
  1. package/bin/init.js +80 -9
  2. package/package.json +1 -1
package/bin/init.js CHANGED
@@ -117,13 +117,42 @@ function installSkill() {
117
117
  // Master skill (full body) + one thin bootstrap per assistant, so "install into
118
118
  // every editor you already use" actually holds — Cursor, Windsurf, Cline,
119
119
  // Copilot, Aider, Continue, Claude, Gemini, plus the generic AGENTS.md.
120
+ // The Continue.dev config is YAML with a `rules:` block, not free prose — wrap
121
+ // the bootstrap so it drops in as a valid rule instead of breaking the parser.
122
+ const continueYaml =
123
+ 'name: Jikida security\n' +
124
+ 'version: 0.0.1\n' +
125
+ 'rules:\n' +
126
+ ' - >\n' +
127
+ bootstrap.split('\n').map((l) => ' ' + l).join('\n') + '\n';
128
+
129
+ // A Claude Code slash command: `/jikida-scan` in the editor kicks off the
130
+ // guided repo scan without the user remembering the phrasing.
131
+ const scanCommand =
132
+ '---\n' +
133
+ 'description: Scan this repo for leaked secrets, exposed rules, and vulnerabilities (Jikida)\n' +
134
+ '---\n\n' +
135
+ 'Read `.claude/skills/jikida/SKILL.md`, then run a full security pass on this\n' +
136
+ 'repository: committed secrets (.env, serviceAccountKey.json, *.pem, API keys),\n' +
137
+ 'open Supabase/Firebase rules, missing auth checks, injection, and out-of-date\n' +
138
+ 'dependencies with known CVEs. Walk the 25-category catalog. Report findings\n' +
139
+ 'worst-first with a one-line fix each, and apply fixes at the lowest safe\n' +
140
+ 'intervention level. Ask before anything destructive. Record what you find in\n' +
141
+ '`memory-security.md`.\n';
142
+
143
+ // `managed: true` = a Jikida-owned file we fully (re)write on every run so
144
+ // updates land and re-running never appends a duplicate (SKILL.md, our own
145
+ // .mdc/command/continue files). The rest are shared assistant files we only
146
+ // append-if-missing so we never clobber a user's own rules.
120
147
  const targets = [
121
- [join(cwd, '.claude', 'skills', 'jikida', 'SKILL.md'), body],
148
+ [join(cwd, '.claude', 'skills', 'jikida', 'SKILL.md'), body, true],
149
+ [join(cwd, '.claude', 'commands', 'jikida-scan.md'), scanCommand, true],
150
+ [join(cwd, '.cursor', 'rules', 'jikida.mdc'), mdc, true],
151
+ [join(cwd, '.continue', 'config.yaml'), continueYaml, true],
122
152
  [join(cwd, 'AGENTS.md'), bootstrap],
123
153
  [join(cwd, 'CLAUDE.md'), bootstrap],
124
154
  [join(cwd, 'GEMINI.md'), bootstrap],
125
155
  [join(cwd, '.cursorrules'), bootstrap],
126
- [join(cwd, '.cursor', 'rules', 'jikida.mdc'), mdc],
127
156
  [join(cwd, '.windsurfrules'), bootstrap],
128
157
  [join(cwd, '.clinerules'), bootstrap],
129
158
  [join(cwd, '.aider.conf.yml'), bootstrap],
@@ -134,14 +163,14 @@ function installSkill() {
134
163
  log(' ----------------------------');
135
164
  const MARK = 'Jikida security skill';
136
165
  let wrote = 0, skipped = 0;
137
- for (const [target, content] of targets) {
166
+ for (const [target, content, managed] of targets) {
138
167
  const rel = target.replace(cwd + '/', '');
139
168
  try {
140
169
  mkdirSync(dirname(target), { recursive: true });
141
- // The master SKILL.md is always (re)written so updates land. The per-
142
- // assistant files are APPEND-if-present, skip-if-already-ours — never clobber
143
- // a user's existing rules file, never double-add.
144
- if (target.endsWith('SKILL.md')) {
170
+ // Managed (Jikida-owned) files are always (re)written so updates land and
171
+ // re-runs never duplicate. Shared assistant files are APPEND-if-present,
172
+ // skip-if-already-ours — never clobber a user's own rules, never double-add.
173
+ if (managed) {
145
174
  writeFileSync(target, content);
146
175
  done(rel); wrote++;
147
176
  } else if (!existsSync(target)) {
@@ -171,11 +200,53 @@ function installSkill() {
171
200
  }
172
201
  } catch (e) { warn(`Could not copy checklists: ${e.message}`); }
173
202
 
203
+ // Harden .gitignore: a security skill install should also stop the classic
204
+ // leaks it scans for from ever being committed. Append only the lines that are
205
+ // missing, under one clearly-marked block — never duplicate, never reorder the
206
+ // user's file.
207
+ try {
208
+ const giPath = join(cwd, '.gitignore');
209
+ const current = existsSync(giPath) ? readFileSync(giPath, 'utf8') : '';
210
+ const wanted = [
211
+ '.env', '.env.*', '!.env.example',
212
+ '*.pem', '*.key', '*.p8', '*.p12', '*.keystore',
213
+ 'serviceAccountKey.json', '*-firebase-adminsdk-*.json', 'google-services.json',
214
+ '*.mobileprovision', 'secrets.*', '.jikida/',
215
+ ];
216
+ const missing = wanted.filter((line) => {
217
+ const re = new RegExp('^' + line.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\s*$', 'm');
218
+ return !re.test(current);
219
+ });
220
+ if (missing.length) {
221
+ const block = (current && !current.endsWith('\n') ? '\n' : '')
222
+ + '\n# Jikida security — do not commit secrets\n' + missing.join('\n') + '\n';
223
+ appendFileSync(giPath, block);
224
+ done(`.gitignore (+${missing.length} security entr${missing.length === 1 ? 'y' : 'ies'})`); wrote++;
225
+ }
226
+ } catch (e) { warn(`Could not update .gitignore: ${e.message}`); }
227
+
228
+ // A persistent findings log the assistant appends to across sessions, so a
229
+ // scan's results and the "still to fix" list survive context resets.
230
+ try {
231
+ const memPath = join(cwd, 'memory-security.md');
232
+ if (!existsSync(memPath)) {
233
+ writeFileSync(memPath,
234
+ '# Security memory (Jikida)\n\n' +
235
+ 'The assistant appends findings and their status here as it scans and fixes.\n' +
236
+ 'Read this at the start of a security pass so nothing is re-flagged or lost.\n\n' +
237
+ '## Open findings\n\n_none yet — run `/jikida-scan` or ask your AI to scan the repo._\n\n' +
238
+ '## Fixed\n\n');
239
+ done('memory-security.md'); wrote++;
240
+ }
241
+ } catch (e) { warn(`Could not write memory-security.md: ${e.message}`); }
242
+
174
243
  if (wrote > 0 || skipped > 0) {
175
244
  log('');
176
245
  if (skipped > 0) { log(` ${skipped} file(s) already had the skill — left untouched.`); }
177
- log(' Done. Your AI editors now know Jikida restart them, then ask them to');
178
- log(' "add Jikida security to this app" or "scan my repo for leaked secrets".');
246
+ log(' Done. Your AI editors now know Jikida. Restart them, then:');
247
+ log('');
248
+ log(' ⚡ Run /jikida-scan in Claude Code — or ask any AI to');
249
+ log(' "scan my repo for leaked secrets and open rules".');
179
250
  log('');
180
251
  }
181
252
  process.exit((wrote > 0 || skipped > 0) ? 0 : 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jikida/init",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "One command to install a production Web Application Firewall. Detects Node (Next.js / Express / Fastify), PHP (Laravel / Symfony), and Python (Django / FastAPI / Flask), installs the right Jikida SDK, wires the middleware, writes JIKIDA_TOKEN to .env. $0 to start. Fails open. Playground + MCP for Claude Code / Cursor / Windsurf included.",
5
5
  "type": "module",
6
6
  "bin": {