ineedcodes 1.7.1 → 1.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ineedcodes",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "Your terminal, now autonomous. Just say what you want, ineed does the rest.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/cli.js CHANGED
@@ -3,6 +3,9 @@
3
3
  // First open asks for provider setup. After that, just say what you want.
4
4
 
5
5
  import { spawn } from 'node:child_process';
6
+ import * as fs from 'node:fs';
7
+ import * as os from 'node:os';
8
+ import * as path from 'node:path';
6
9
 
7
10
  const [MAJOR] = process.versions.node.split('.').map(Number);
8
11
  if (!(MAJOR >= 20)) {
@@ -27,6 +30,7 @@ ${bold('ineed')} ${dim(`v${VERSION}`)} - your terminal, now autonomous
27
30
 
28
31
  ${green('ineed')} interactive session (first open: setup)
29
32
  ${green('ineed "fix the build errors"')} one-shot task
33
+ ${green('ineed unlock')} enable gated skills on this machine
30
34
  ${green('ineed --reset')} redo provider setup
31
35
  ${green('ineed --version')} show version
32
36
 
@@ -35,6 +39,34 @@ Inside a session, ${dim('/help')} lists the shortcuts. Or just talk to it.
35
39
  process.exit(0);
36
40
  }
37
41
 
42
+ // unlock: store the developer keyword locally so gated security skills activate here
43
+ if (args[0] === 'unlock') {
44
+ const readline = await import('node:readline');
45
+ const { makeInput, dim, gray } = await import('./ui.js');
46
+ const keywordDir = process.env.INEED_CONFIG_DIR || path.join(os.homedir(), '.ineedcodes');
47
+ const keywordFile = path.join(keywordDir, 'keyword');
48
+ const given = args[1];
49
+ let keyword = '';
50
+ if (given) {
51
+ keyword = given.trim();
52
+ } else {
53
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
54
+ const ask = makeInput(rl);
55
+ keyword = (await ask('Developer keyword: ', { secret: true })).trim();
56
+ rl.close();
57
+ }
58
+ if (!keyword) {
59
+ console.error(red('No keyword given. Nothing saved.'));
60
+ process.exit(1);
61
+ }
62
+ fs.mkdirSync(keywordDir, { recursive: true });
63
+ fs.writeFileSync(keywordFile, keyword, { mode: 0o600 });
64
+ try { fs.chmodSync(keywordFile, 0o600); } catch {}
65
+ console.log(green('Gated skills unlocked on this machine.'));
66
+ console.log(dim('Saved to ' + keywordFile + ' (0600). Use them by mentioning the keyword in a task, e.g. /skills or "use the sqli skill".'));
67
+ process.exit(0);
68
+ }
69
+
38
70
  // internal: one-shot worker. Spawned by the branch below, never run by users.
39
71
  if (args[0] === '--child') {
40
72
  const task = args.slice(1).join(' ');
package/src/session.js CHANGED
@@ -12,7 +12,7 @@ import { saveSession, listSessions, loadSession } from './sessions.js';
12
12
  import * as boost from './boost.js';
13
13
  import { spawnSync } from 'node:child_process';
14
14
  import { mcpConfigured } from './mcp.js';
15
- import { listSkills, findSkill } from './skills.js';
15
+ import { listSkills, findSkill, devKeyword } from './skills.js';
16
16
 
17
17
  function currentBranch(cwd) {
18
18
  const r = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, encoding: 'utf8' });
@@ -483,10 +483,12 @@ export async function startSession(cfg, { fresh = false, resume = null } = {}) {
483
483
  else say(red(`No skill named ${arg}.`));
484
484
  return;
485
485
  }
486
- const all = listSkills(process.cwd(), input);
487
- const gated = all.filter(s => s.gated).length;
486
+ const machineUnlocked = devKeyword() !== '';
487
+ const all = listSkills(process.cwd(), machineUnlocked ? devKeyword() : input);
488
+ const gated = machineUnlocked ? all.filter(x => x.gated).length : -1;
488
489
  say(all.length ? all.map(s => ` ${cyan(s.name)} ${dim('(' + s.scope + ')')} ${s.description}`).join('\n') : yellow('No skills installed.'));
489
- if (gated) say(dim(` (+${gated} gated security skills)`));
490
+ if (gated > 0) say(dim(` (+${gated} gated security skills unlocked on this machine)`));
491
+ else if (!machineUnlocked) say(dim(` gated security skills are locked on this machine - run ${bold('ineed unlock')} to enable`));
490
492
  return;
491
493
  }
492
494
  if (input === '/new') {
package/src/skills.js CHANGED
@@ -34,9 +34,23 @@ function parseFrontmatter(raw) {
34
34
  const m = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
35
35
  if (!m) return null;
36
36
  const meta = {};
37
- for (const line of m[1].split('\n')) {
38
- const kv = line.match(/^(\w+):\s*(.+)$/);
39
- if (kv) meta[kv[1]] = kv[2].trim();
37
+ const lines = m[1].split('\n');
38
+ for (let i = 0; i < lines.length; i++) {
39
+ const kv = lines[i].match(/^(\w+):\s*(.*)$/);
40
+ if (!kv) continue;
41
+ // folded scalar (>-, >): join the indented continuation lines into one paragraph
42
+ if (/^(description|summary)$/.test(kv[1]) && ['', '>-', '>', '|', '|-'].includes(kv[2])) {
43
+ const parts = [];
44
+ let j = i + 1;
45
+ for (; j < lines.length; j++) {
46
+ if (/^\S/.test(lines[j])) break;
47
+ parts.push(lines[j].trim());
48
+ }
49
+ meta[kv[1]] = parts.join(' ').trim();
50
+ i = j - 1;
51
+ continue;
52
+ }
53
+ meta[kv[1]] = kv[2].trim();
40
54
  }
41
55
  if (!meta.name) return null;
42
56
  return {
package/src/ui.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // ui.js: terminal helpers. No dependencies, respects NO_COLOR and non-TTY.
2
2
 
3
- export const VERSION = '1.7.1';
3
+ export const VERSION = '1.7.2';
4
4
 
5
5
  const USE_COLOR = process.stdout.isTTY && !process.env.NO_COLOR;
6
6
  const wrap = (code, t) => USE_COLOR ? `\x1b[${code}m${t}\x1b[0m` : String(t);