@adityaaria/spark 6.0.8 → 6.0.9

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": "@adityaaria/spark",
3
- "version": "6.0.8",
3
+ "version": "6.0.9",
4
4
  "description": "SPARK skills and runtime bootstrap for coding agents",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -66,6 +66,18 @@ export function createAdapter({
66
66
  encoding: 'utf8',
67
67
  });
68
68
 
69
+ if (result.error) {
70
+ if (result.error.code === 'ENOENT') {
71
+ throw new InstallerError(
72
+ `\n\u2715 ${label} install failed: Command '${entry.file}' not found.\n` +
73
+ `SPARK requires ${label} to be installed first.\n` +
74
+ `Please install ${label} from its official source (e.g. npm install -g <package-name>)\n` +
75
+ `and ensure '${entry.file}' is in your PATH before retrying.\n`
76
+ );
77
+ }
78
+ throw new InstallerError(`${label} install failed: ${result.error.message}`);
79
+ }
80
+
69
81
  if (result.status !== 0) {
70
82
  throw new InstallerError(
71
83
  `${label} install failed: ${result.stderr || result.stdout || 'unknown error'}`
@@ -33,8 +33,9 @@ export async function chooseHarness({
33
33
  throw new InstallerError('No harness selected. Re-run with --harness or use an interactive terminal.');
34
34
  }
35
35
 
36
+ const homeDir = os.homedir();
36
37
  const detectedCandidates = detectHarnessCandidates({ env, fsImpl: fs });
37
- const candidates = buildPromptCandidates(detectedCandidates);
38
+ const candidates = buildPromptCandidates(detectedCandidates, env, fs, homeDir);
38
39
 
39
40
  let validationMessage = null;
40
41
  while (true) {
@@ -51,6 +52,8 @@ function scoreAdapter(adapter, env, fsImpl, homeDir) {
51
52
  let score = 0;
52
53
  const reasons = [];
53
54
 
55
+ let missingBinaries = false;
56
+
54
57
  for (const key of adapter.envKeys) {
55
58
  if (env[key]) {
56
59
  score += 100;
@@ -58,10 +61,15 @@ function scoreAdapter(adapter, env, fsImpl, homeDir) {
58
61
  }
59
62
  }
60
63
 
61
- for (const binaryName of adapter.binaryNames) {
62
- if (commandExists(binaryName, env, fsImpl)) {
63
- score += 70;
64
- reasons.push(`path:${binaryName}`);
64
+ if (adapter.binaryNames && adapter.binaryNames.length > 0) {
65
+ missingBinaries = true;
66
+ for (const binaryName of adapter.binaryNames) {
67
+ if (commandExists(binaryName, env, fsImpl)) {
68
+ score += 70;
69
+ reasons.push(`path:${binaryName}`);
70
+ missingBinaries = false;
71
+ break;
72
+ }
65
73
  }
66
74
  }
67
75
 
@@ -80,6 +88,7 @@ function scoreAdapter(adapter, env, fsImpl, homeDir) {
80
88
  label: adapter.label,
81
89
  score,
82
90
  reasons,
91
+ missingBinaries,
83
92
  };
84
93
  }
85
94
 
@@ -129,7 +138,7 @@ function commandExists(commandName, env, fsImpl) {
129
138
  return false;
130
139
  }
131
140
 
132
- function buildPromptCandidates(detectedCandidates) {
141
+ function buildPromptCandidates(detectedCandidates, env, fsImpl, homeDir) {
133
142
  const allAdapters = listAdapters();
134
143
  const detectedIds = new Set(detectedCandidates.map((candidate) => candidate.id));
135
144
 
@@ -137,12 +146,12 @@ function buildPromptCandidates(detectedCandidates) {
137
146
  ...detectedCandidates,
138
147
  ...allAdapters
139
148
  .filter((adapter) => !detectedIds.has(adapter.id))
140
- .map((adapter) => ({
141
- id: adapter.id,
142
- label: adapter.label,
143
- score: 0,
144
- reasons: [],
145
- })),
149
+ .map((adapter) => {
150
+ const result = scoreAdapter(adapter, env, fsImpl, homeDir);
151
+ result.score = 0;
152
+ result.reasons = [];
153
+ return result;
154
+ }),
146
155
  ];
147
156
  }
148
157
 
@@ -163,14 +172,19 @@ function renderPrompt(candidates, validationMessage = null) {
163
172
  }
164
173
 
165
174
  for (const [index, candidate] of candidates.entries()) {
175
+ let label = candidate.label;
176
+ if (candidate.missingBinaries) {
177
+ label += ' [Missing CLI]';
178
+ }
179
+
166
180
  const hint = recommendedIds.has(candidate.id) ? 'recommended' : null;
167
181
  lines.push(
168
182
  promptOption(
169
183
  index + 1,
170
- candidate.label,
184
+ label,
171
185
  candidate.id,
172
186
  hint,
173
- describeHarness(candidate.id)
187
+ describeHarness(candidate.id, candidate.missingBinaries)
174
188
  )
175
189
  );
176
190
  }
@@ -197,7 +211,7 @@ function resolvePromptAnswer(answer, candidates) {
197
211
  return getAdapterById(exact.id);
198
212
  }
199
213
 
200
- function describeHarness(id) {
214
+ function describeHarness(id, missingBinaries = false) {
201
215
  const descriptions = {
202
216
  claude: 'Best for Claude Code sessions and project-local plugin workflows.',
203
217
  codex: 'For the Codex CLI plugin marketplace flow in terminal sessions.',
@@ -210,7 +224,12 @@ function describeHarness(id) {
210
224
  antigravity: 'Installs the Antigravity plugin and loads SPARK on new sessions.',
211
225
  };
212
226
 
213
- return descriptions[id] ?? null;
227
+ let description = descriptions[id] ?? null;
228
+ if (description && missingBinaries) {
229
+ description += '\n ⚠️ Requires its official CLI to be installed first.';
230
+ }
231
+
232
+ return description;
214
233
  }
215
234
 
216
235
  function normalizeHarness(value) {