tsic-ainode 1.0.0 → 1.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 (2) hide show
  1. package/bin/index.js +59 -61
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -54,36 +54,36 @@ function detectLocale() {
54
54
  // ---------------------------------------------------------------------------
55
55
  const TOOLS = [
56
56
  {
57
- id: 'claude',
58
- nameKey: 'tool_claude',
59
- detect: () => fs.existsSync(path.join(HOME, '.claude')),
60
- install: installClaude,
57
+ id: 'claude',
58
+ nameKey: 'tool_claude',
59
+ detect: () => fs.existsSync(path.join(HOME, '.claude')),
60
+ install: installClaude,
61
61
  },
62
62
  {
63
- id: 'cursor',
64
- nameKey: 'tool_cursor',
65
- detect: () => fs.existsSync(path.join(HOME, '.cursor')) ||
66
- fs.existsSync(path.join(CWD, '.cursor')),
67
- install: installCursor,
63
+ id: 'cursor',
64
+ nameKey: 'tool_cursor',
65
+ detect: () => cmdExists('cursor') ||
66
+ fs.existsSync(path.join(HOME, '.cursor')) ||
67
+ fs.existsSync(path.join(CWD, '.cursor')),
68
+ install: installCursor,
68
69
  },
69
70
  {
70
- id: 'codex',
71
- nameKey: 'tool_codex',
72
- detect: () => cmdExists('codex') || fs.existsSync(path.join(HOME, '.codex')),
73
- install: installCodex,
71
+ id: 'codex',
72
+ nameKey: 'tool_codex',
73
+ detect: () => cmdExists('codex'),
74
+ install: installCodex,
74
75
  },
75
76
  {
76
- id: 'gemini',
77
- nameKey: 'tool_gemini',
78
- detect: () => cmdExists('gemini') || fs.existsSync(path.join(HOME, '.gemini')),
79
- install: installGemini,
77
+ id: 'gemini',
78
+ nameKey: 'tool_gemini',
79
+ detect: () => cmdExists('gemini') || cmdExists('gemini-cli'),
80
+ install: installGemini,
80
81
  },
81
82
  {
82
- id: 'antigravity',
83
- nameKey: 'tool_antigravity',
84
- detect: () => cmdExists('antigravity') ||
85
- fs.existsSync(path.join(HOME, '.antigravity')),
86
- install: installAntigravity,
83
+ id: 'antigravity',
84
+ nameKey: 'tool_antigravity',
85
+ detect: () => cmdExists('antigravity'),
86
+ install: installAntigravity,
87
87
  },
88
88
  ];
89
89
 
@@ -105,7 +105,7 @@ function copyTemplate(src, dest) {
105
105
  }
106
106
 
107
107
  function appendToFile(src, dest) {
108
- const tag = '<!-- tsic-ainode -->';
108
+ const tag = '<!-- tsic-ainode -->';
109
109
  const content = fs.readFileSync(src, 'utf8');
110
110
  if (fs.existsSync(dest)) {
111
111
  const existing = fs.readFileSync(dest, 'utf8');
@@ -164,47 +164,48 @@ function createRL() {
164
164
  }
165
165
 
166
166
  function ask(rl, question) {
167
- return new Promise(resolve => rl.question(question, resolve));
167
+ return new Promise(resolve => rl.question(question, ans => resolve(ans.trim())));
168
168
  }
169
169
 
170
- function askChoice(rl, question, choices) {
171
- return new Promise(resolve => {
172
- const opts = choices.map((c, i) => ` ${i + 1}. ${c}`).join('\n');
173
- rl.question(`${question}\n${opts}\n> `, ans => {
174
- const idx = parseInt(ans.trim(), 10) - 1;
175
- resolve(choices[Math.max(0, Math.min(idx, choices.length - 1))]);
176
- });
177
- });
170
+ // Single-prompt choice: shows numbered list, returns chosen item (default: first)
171
+ async function askChoice(rl, question, choices) {
172
+ const opts = choices.map((c, i) => ` ${i + 1}. ${c}`).join('\n');
173
+ const ans = await ask(rl, `${question}\n${opts}\n> `);
174
+ const idx = parseInt(ans, 10) - 1;
175
+ if (isNaN(idx) || idx < 0 || idx >= choices.length) return choices[0];
176
+ return choices[idx];
178
177
  }
179
178
 
179
+ // Single-prompt checkbox: shows numbered list with detected pre-selected.
180
+ // User types space-separated numbers to select, or Enter to accept detected defaults.
180
181
  async function askCheckbox(rl, question, items) {
182
+ const defaultNums = items
183
+ .map((item, i) => (item.detected ? String(i + 1) : null))
184
+ .filter(Boolean);
185
+
181
186
  console.log(`\n${question}`);
182
- const selected = new Set(items.filter(i => i.detected).map(i => i.id));
183
187
  items.forEach((item, i) => {
184
- const check = selected.has(item.id) ? '' : '';
185
- const hint = item.detected ? ' (detected)' : '';
186
- console.log(` ${i + 1}. ${check} ${item.label}${hint}`);
188
+ const mark = item.detected ? '[✓]' : '[ ]';
189
+ console.log(` ${i + 1}. ${mark} ${item.label}`);
187
190
  });
188
- console.log(' (Enter numbers to toggle, e.g. "1 3", or Enter to confirm)');
189
-
190
- while (true) {
191
- const ans = await ask(rl, '> ');
192
- if (ans.trim() === '') break;
193
- ans.trim().split(/\s+/).forEach(n => {
194
- const idx = parseInt(n, 10) - 1;
195
- if (idx >= 0 && idx < items.length) {
196
- const id = items[idx].id;
197
- selected.has(id) ? selected.delete(id) : selected.add(id);
198
- }
199
- });
200
- // redraw
201
- items.forEach((item, i) => {
202
- const check = selected.has(item.id) ? '◉' : '○';
203
- process.stdout.write(` ${i + 1}. ${check} ${item.label}\n`);
204
- });
205
- console.log(' (Enter to confirm, or toggle more)');
191
+
192
+ const hint = defaultNums.length > 0
193
+ ? `(Enter 確認選取 ${defaultNums.join(' ')},或輸入編號如 "1 3")`
194
+ : '(輸入編號如 "1 3",多選用空白分隔)';
195
+ console.log(` ${hint}`);
196
+
197
+ const ans = await ask(rl, '> ');
198
+
199
+ if (ans === '' && defaultNums.length > 0) {
200
+ return items.filter(item => item.detected);
206
201
  }
207
- return items.filter(i => selected.has(i.id));
202
+
203
+ const selected = ans.split(/\s+/)
204
+ .map(n => parseInt(n, 10) - 1)
205
+ .filter(i => i >= 0 && i < items.length)
206
+ .map(i => items[i]);
207
+
208
+ return selected;
208
209
  }
209
210
 
210
211
  // ---------------------------------------------------------------------------
@@ -232,11 +233,11 @@ async function main() {
232
233
  loadLocale(locale);
233
234
 
234
235
  // Detect tools
235
- const detectedIds = TOOLS.filter(t => t.detect()).map(t => t.id);
236
+ const detectedIds = TOOLS.filter(tool => tool.detect()).map(tool => tool.id);
236
237
  if (detectedIds.length > 0) {
237
238
  console.log(`\n${T.detect_title}`);
238
239
  detectedIds.forEach(id => {
239
- const tool = TOOLS.find(t => t.id === id);
240
+ const tool = TOOLS.find(tool => tool.id === id);
240
241
  console.log(` ✓ ${T[tool.nameKey] || tool.id}`);
241
242
  });
242
243
  } else {
@@ -267,16 +268,13 @@ async function main() {
267
268
 
268
269
  // Install
269
270
  console.log(`\n${T.installing}`);
270
- const results = [];
271
271
  for (const tool of chosen) {
272
272
  try {
273
273
  const dest = tool.install(scope);
274
- console.log(` ${T.installed_ok}: ${tool.label}`);
274
+ console.log(` ${T.installed_ok}: ${tool.label}`);
275
275
  console.log(` → ${dest}`);
276
- results.push({ tool, dest, ok: true });
277
276
  } catch (err) {
278
277
  console.error(` ✗ ${T.err_write}: ${tool.label} — ${err.message}`);
279
- results.push({ tool, ok: false });
280
278
  }
281
279
  }
282
280
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsic-ainode",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "TSIC AINode Skill installer — adds AINode SSH Dongle support to Claude Code, Cursor, Codex, Gemini CLI, and Antigravity",
5
5
  "bin": {
6
6
  "tsic-ainode": "bin/index.js"