@adityaaria/spark 6.0.3

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 (91) hide show
  1. package/.claude-plugin/marketplace.json +20 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/.codex-plugin/plugin.json +48 -0
  4. package/.cursor-plugin/plugin.json +23 -0
  5. package/.kimi-plugin/plugin.json +38 -0
  6. package/.opencode/INSTALL.md +115 -0
  7. package/.opencode/plugins/spark.js +139 -0
  8. package/.pi/extensions/spark.ts +121 -0
  9. package/.version-bump.json +21 -0
  10. package/CLAUDE.md +115 -0
  11. package/CODE_OF_CONDUCT.md +128 -0
  12. package/GEMINI.md +2 -0
  13. package/LICENSE +21 -0
  14. package/README.md +282 -0
  15. package/RELEASE-NOTES.md +1299 -0
  16. package/assets/app-icon.png +0 -0
  17. package/assets/spark-small.svg +1 -0
  18. package/bin/spark.js +7 -0
  19. package/docs/README.kimi.md +94 -0
  20. package/docs/README.opencode.md +170 -0
  21. package/docs/porting-to-a-new-harness.md +830 -0
  22. package/gemini-extension.json +6 -0
  23. package/hooks/hooks-codex.json +16 -0
  24. package/hooks/hooks-cursor.json +10 -0
  25. package/hooks/hooks.json +16 -0
  26. package/hooks/run-hook.cmd +46 -0
  27. package/hooks/session-start +49 -0
  28. package/hooks/session-start-codex +26 -0
  29. package/package.json +52 -0
  30. package/skills/brainstorming/SKILL.md +159 -0
  31. package/skills/brainstorming/scripts/frame-template.html +213 -0
  32. package/skills/brainstorming/scripts/helper.js +167 -0
  33. package/skills/brainstorming/scripts/server.cjs +722 -0
  34. package/skills/brainstorming/scripts/start-server.sh +209 -0
  35. package/skills/brainstorming/scripts/stop-server.sh +120 -0
  36. package/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  37. package/skills/brainstorming/visual-companion.md +298 -0
  38. package/skills/dispatching-parallel-agents/SKILL.md +185 -0
  39. package/skills/executing-plans/SKILL.md +70 -0
  40. package/skills/finishing-a-development-branch/SKILL.md +241 -0
  41. package/skills/receiving-code-review/SKILL.md +213 -0
  42. package/skills/requesting-code-review/SKILL.md +103 -0
  43. package/skills/requesting-code-review/code-reviewer.md +172 -0
  44. package/skills/subagent-driven-development/SKILL.md +418 -0
  45. package/skills/subagent-driven-development/implementer-prompt.md +139 -0
  46. package/skills/subagent-driven-development/scripts/review-package +44 -0
  47. package/skills/subagent-driven-development/scripts/sdd-workspace +22 -0
  48. package/skills/subagent-driven-development/scripts/task-brief +40 -0
  49. package/skills/subagent-driven-development/task-reviewer-prompt.md +188 -0
  50. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  51. package/skills/systematic-debugging/SKILL.md +296 -0
  52. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  53. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  54. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  55. package/skills/systematic-debugging/find-polluter.sh +63 -0
  56. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  57. package/skills/systematic-debugging/test-academic.md +14 -0
  58. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  59. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  60. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  61. package/skills/test-driven-development/SKILL.md +371 -0
  62. package/skills/test-driven-development/testing-anti-patterns.md +299 -0
  63. package/skills/using-git-worktrees/SKILL.md +202 -0
  64. package/skills/using-spark/SKILL.md +121 -0
  65. package/skills/using-spark/references/antigravity-tools.md +96 -0
  66. package/skills/using-spark/references/claude-code-tools.md +50 -0
  67. package/skills/using-spark/references/codex-tools.md +72 -0
  68. package/skills/using-spark/references/copilot-tools.md +49 -0
  69. package/skills/using-spark/references/gemini-tools.md +63 -0
  70. package/skills/using-spark/references/pi-tools.md +28 -0
  71. package/skills/verification-before-completion/SKILL.md +139 -0
  72. package/skills/writing-plans/SKILL.md +174 -0
  73. package/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  74. package/skills/writing-skills/SKILL.md +689 -0
  75. package/skills/writing-skills/anthropic-best-practices.md +1150 -0
  76. package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  77. package/skills/writing-skills/graphviz-conventions.dot +172 -0
  78. package/skills/writing-skills/persuasion-principles.md +187 -0
  79. package/skills/writing-skills/render-graphs.js +168 -0
  80. package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  81. package/src/cli/index.js +26 -0
  82. package/src/cli/install.js +47 -0
  83. package/src/cli/output.js +11 -0
  84. package/src/cli/parse-args.js +46 -0
  85. package/src/cli/prompt.js +10 -0
  86. package/src/installer/adapters/common.js +59 -0
  87. package/src/installer/adapters/extension-style.js +67 -0
  88. package/src/installer/adapters/shell-hook.js +57 -0
  89. package/src/installer/detect.js +168 -0
  90. package/src/installer/errors.js +7 -0
  91. package/src/installer/registry.js +35 -0
@@ -0,0 +1,59 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { InstallerError } from '../errors.js';
3
+
4
+ export function createAdapter({
5
+ id,
6
+ label,
7
+ kind,
8
+ bootstrap,
9
+ installHint,
10
+ verifyHint,
11
+ command = null,
12
+ envKeys = [],
13
+ binaryNames = [],
14
+ configPaths = [],
15
+ }) {
16
+ return {
17
+ id,
18
+ label,
19
+ envKeys,
20
+ binaryNames,
21
+ configPaths,
22
+ command,
23
+ planInstall() {
24
+ return {
25
+ kind,
26
+ bootstrap,
27
+ installHint,
28
+ verifyHint,
29
+ command,
30
+ };
31
+ },
32
+ async install({ runner = spawnSync, dryRun = false } = {}) {
33
+ if (dryRun) {
34
+ return this.planInstall();
35
+ }
36
+
37
+ if (!command) {
38
+ throw new InstallerError(`${label} install is not fully automatable yet.`);
39
+ }
40
+
41
+ const result = runner(command.file, command.args ?? [], {
42
+ cwd: command.cwd ?? process.cwd(),
43
+ env: command.env ?? process.env,
44
+ encoding: 'utf8',
45
+ });
46
+
47
+ if (result.status !== 0) {
48
+ throw new InstallerError(
49
+ `${label} install failed: ${result.stderr || result.stdout || 'unknown error'}`
50
+ );
51
+ }
52
+
53
+ return this.planInstall();
54
+ },
55
+ async verify() {
56
+ return verifyHint;
57
+ },
58
+ };
59
+ }
@@ -0,0 +1,67 @@
1
+ import { createAdapter } from './common.js';
2
+
3
+ export function createOpenCodeAdapter() {
4
+ return createAdapter({
5
+ id: 'opencode',
6
+ label: 'OpenCode',
7
+ kind: 'extension',
8
+ envKeys: ['OPENCODE_CONFIG_DIR'],
9
+ binaryNames: ['opencode'],
10
+ configPaths: ['opencode.json'],
11
+ bootstrap: 'using-spark -> .opencode/plugins/spark.js -> message transform bootstrap',
12
+ installHint: '.opencode/plugins/spark.js + .opencode/INSTALL.md',
13
+ verifyHint: 'Run opencode logs or a fresh OpenCode session and confirm bootstrap injection.',
14
+ });
15
+ }
16
+
17
+ export function createPiAdapter() {
18
+ return createAdapter({
19
+ id: 'pi',
20
+ label: 'Pi',
21
+ kind: 'extension',
22
+ envKeys: ['PI_HOME'],
23
+ binaryNames: ['pi'],
24
+ bootstrap: 'using-spark -> .pi/extensions/spark.ts -> session_start/session_compact bootstrap',
25
+ installHint: '.pi/extensions/spark.ts + package.json pi fields',
26
+ verifyHint: 'Run a fresh Pi session and confirm using-spark loads at session start.',
27
+ command: {
28
+ file: 'pi',
29
+ args: ['install', 'git:github.com/adityaaria/SPARK'],
30
+ },
31
+ });
32
+ }
33
+
34
+ export function createGeminiAdapter() {
35
+ return createAdapter({
36
+ id: 'gemini',
37
+ label: 'Gemini CLI',
38
+ kind: 'context-file',
39
+ envKeys: ['GEMINI_HOME', 'GOOGLE_GEMINI_CLI'],
40
+ binaryNames: ['gemini'],
41
+ configPaths: ['GEMINI.md', 'gemini-extension.json'],
42
+ bootstrap: 'using-spark -> gemini-extension.json -> GEMINI.md context file',
43
+ installHint: 'gemini-extension.json + GEMINI.md + references/gemini-tools.md',
44
+ verifyHint: 'Run a fresh Gemini CLI session and confirm using-spark loads at session start.',
45
+ command: {
46
+ file: 'gemini',
47
+ args: ['extensions', 'install', 'https://github.com/adityaaria/SPARK'],
48
+ },
49
+ });
50
+ }
51
+
52
+ export function createAntigravityAdapter() {
53
+ return createAdapter({
54
+ id: 'antigravity',
55
+ label: 'Antigravity',
56
+ kind: 'context-file',
57
+ envKeys: ['ANTIGRAVITY_PLUGIN_ROOT'],
58
+ binaryNames: ['agy'],
59
+ bootstrap: 'using-spark -> agy plugin install -> contextFileName bootstrap',
60
+ installHint: 'skills/using-spark/references/antigravity-tools.md + plugin install flow',
61
+ verifyHint: 'Run a fresh Antigravity session and confirm using-spark loads from the plugin install.',
62
+ command: {
63
+ file: 'agy',
64
+ args: ['plugin', 'install', 'https://github.com/adityaaria/SPARK'],
65
+ },
66
+ });
67
+ }
@@ -0,0 +1,57 @@
1
+ import { createAdapter } from './common.js';
2
+
3
+ export function createClaudeCodeAdapter() {
4
+ return createAdapter({
5
+ id: 'claude',
6
+ label: 'Claude Code',
7
+ kind: 'shell-hook',
8
+ envKeys: ['CLAUDE_PLUGIN_ROOT'],
9
+ binaryNames: ['claude'],
10
+ bootstrap: 'shell hook -> hooks/session-start -> using-spark',
11
+ installHint: '.claude-plugin/plugin.json + hooks/hooks.json + hooks/session-start',
12
+ verifyHint: 'Run a fresh Claude Code session and confirm using-spark loads before coding.',
13
+ });
14
+ }
15
+
16
+ export function createCodexAdapter() {
17
+ return createAdapter({
18
+ id: 'codex',
19
+ label: 'Codex',
20
+ kind: 'shell-hook',
21
+ envKeys: ['CLAUDE_PLUGIN_ROOT'],
22
+ binaryNames: ['codex'],
23
+ bootstrap: 'shell hook -> hooks/session-start-codex -> using-spark',
24
+ installHint: '.codex-plugin/plugin.json + hooks/hooks-codex.json + hooks/session-start-codex',
25
+ verifyHint: 'Run a fresh Codex session and confirm using-spark loads before coding.',
26
+ });
27
+ }
28
+
29
+ export function createCursorAdapter() {
30
+ return createAdapter({
31
+ id: 'cursor',
32
+ label: 'Cursor',
33
+ kind: 'shell-hook',
34
+ envKeys: ['CURSOR_PLUGIN_ROOT'],
35
+ binaryNames: ['cursor'],
36
+ bootstrap: 'shell hook -> hooks/session-start -> using-spark',
37
+ installHint: '.cursor-plugin/plugin.json + hooks/hooks-cursor.json + hooks/session-start',
38
+ verifyHint: 'Run a fresh Cursor Agent session and confirm using-spark loads before coding.',
39
+ });
40
+ }
41
+
42
+ export function createCopilotAdapter() {
43
+ return createAdapter({
44
+ id: 'copilot',
45
+ label: 'Copilot CLI',
46
+ kind: 'shell-hook',
47
+ envKeys: ['COPILOT_CLI'],
48
+ binaryNames: ['copilot'],
49
+ bootstrap: 'shell hook -> hooks/session-start -> using-spark',
50
+ installHint: 'skills/using-spark/references/copilot-tools.md + hooks/session-start',
51
+ verifyHint: 'Run a fresh Copilot CLI session and confirm using-spark loads before coding.',
52
+ command: {
53
+ file: 'copilot',
54
+ args: ['plugin', 'install', 'spark@spark-marketplace'],
55
+ },
56
+ });
57
+ }
@@ -0,0 +1,168 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { getAdapterById, listAdapters } from './registry.js';
5
+ import { InstallerError } from './errors.js';
6
+
7
+ const HIGH_CONFIDENCE_SCORE = 80;
8
+ const AMBIGUOUS_MARGIN = 20;
9
+
10
+ export function detectHarnessCandidates({ env = process.env, fsImpl = fs } = {}) {
11
+ const homeDir = os.homedir();
12
+ const adapters = listAdapters();
13
+
14
+ return adapters
15
+ .map((adapter) => scoreAdapter(adapter, env, fsImpl, homeDir))
16
+ .filter((candidate) => candidate.score > 0)
17
+ .sort((a, b) => b.score - a.score || a.label.localeCompare(b.label));
18
+ }
19
+
20
+ export async function chooseHarness({
21
+ forcedHarness = null,
22
+ env = process.env,
23
+ fs = fs,
24
+ prompt = null,
25
+ } = {}) {
26
+ if (forcedHarness) {
27
+ const adapter = getAdapterById(normalizeHarness(forcedHarness));
28
+ if (!adapter) {
29
+ throw new InstallerError(`Unsupported harness: ${forcedHarness}`);
30
+ }
31
+ return { adapter, source: 'forced', candidates: [] };
32
+ }
33
+
34
+ const candidates = detectHarnessCandidates({ env, fsImpl: fs });
35
+ if (candidates.length === 0) {
36
+ throw new InstallerError(
37
+ `Could not detect a supported harness. Supported harnesses: ${listAdapters().map((adapter) => adapter.label).join(', ')}`
38
+ );
39
+ }
40
+
41
+ if (isConfident(candidates)) {
42
+ const adapter = getAdapterById(candidates[0].id);
43
+ return { adapter, source: 'auto', candidates };
44
+ }
45
+
46
+ if (!prompt) {
47
+ throw new InstallerError(`Multiple harnesses look plausible: ${candidates.map((candidate) => candidate.label).join(', ')}`);
48
+ }
49
+
50
+ const answer = await prompt(renderPrompt(candidates));
51
+ const adapter = resolvePromptAnswer(answer, candidates);
52
+ return { adapter, source: 'prompt', candidates };
53
+ }
54
+
55
+ function scoreAdapter(adapter, env, fsImpl, homeDir) {
56
+ let score = 0;
57
+ const reasons = [];
58
+
59
+ for (const key of adapter.envKeys) {
60
+ if (env[key]) {
61
+ score += 100;
62
+ reasons.push(`env:${key}`);
63
+ }
64
+ }
65
+
66
+ for (const binaryName of adapter.binaryNames) {
67
+ if (commandExists(binaryName, env, fsImpl)) {
68
+ score += 70;
69
+ reasons.push(`path:${binaryName}`);
70
+ }
71
+ }
72
+
73
+ for (const configPath of adapter.configPaths) {
74
+ for (const candidatePath of candidateConfigPaths(homeDir, env, configPath)) {
75
+ if (fsImpl.existsSync(candidatePath)) {
76
+ score += 90;
77
+ reasons.push(`config:${candidatePath}`);
78
+ break;
79
+ }
80
+ }
81
+ }
82
+
83
+ return {
84
+ id: adapter.id,
85
+ label: adapter.label,
86
+ score,
87
+ reasons,
88
+ };
89
+ }
90
+
91
+ function candidateConfigPaths(homeDir, env, configPath) {
92
+ const paths = [];
93
+ if (env.OPENCODE_CONFIG_DIR && configPath === 'opencode.json') {
94
+ paths.push(path.join(env.OPENCODE_CONFIG_DIR, 'opencode.json'));
95
+ }
96
+ if (configPath === 'GEMINI.md') {
97
+ paths.push(path.join(homeDir, '.gemini', 'GEMINI.md'));
98
+ paths.push(path.join(homeDir, 'GEMINI.md'));
99
+ }
100
+ if (configPath === 'gemini-extension.json') {
101
+ paths.push(path.join(homeDir, 'gemini-extension.json'));
102
+ paths.push(path.join(homeDir, '.gemini', 'gemini-extension.json'));
103
+ }
104
+ return paths;
105
+ }
106
+
107
+ function commandExists(commandName, env, fsImpl) {
108
+ const paths = String(env.PATH ?? '').split(path.delimiter).filter(Boolean);
109
+ const exts = process.platform === 'win32'
110
+ ? String(env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)
111
+ : [''];
112
+
113
+ for (const dir of paths) {
114
+ for (const ext of exts) {
115
+ const candidate = path.join(dir, `${commandName}${ext}`);
116
+ try {
117
+ fsImpl.accessSync(candidate, fs.constants.X_OK);
118
+ return true;
119
+ } catch {
120
+ try {
121
+ fsImpl.accessSync(candidate, fs.constants.F_OK);
122
+ return true;
123
+ } catch {
124
+ continue;
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ return false;
131
+ }
132
+
133
+ function isConfident(candidates) {
134
+ if (candidates[0].score < HIGH_CONFIDENCE_SCORE) return false;
135
+ if (candidates.length === 1) return true;
136
+ return candidates[0].score - candidates[1].score >= AMBIGUOUS_MARGIN;
137
+ }
138
+
139
+ function renderPrompt(candidates) {
140
+ return [
141
+ 'Choose the harness to install SPARK for:',
142
+ ...candidates.map((candidate, index) => `${index + 1}. ${candidate.label} (${candidate.id})`),
143
+ 'Enter a number or harness name:',
144
+ ].join('\n');
145
+ }
146
+
147
+ function resolvePromptAnswer(answer, candidates) {
148
+ const value = normalizeHarness(answer);
149
+ if (!value) {
150
+ throw new InstallerError('No harness selected.');
151
+ }
152
+
153
+ const numeric = Number(value);
154
+ if (Number.isInteger(numeric) && numeric >= 1 && numeric <= candidates.length) {
155
+ return getAdapterById(candidates[numeric - 1].id);
156
+ }
157
+
158
+ const exact = candidates.find((candidate) => candidate.id === value || candidate.label.toLowerCase() === value);
159
+ if (!exact) {
160
+ throw new InstallerError(`Unsupported harness choice: ${answer}`);
161
+ }
162
+
163
+ return getAdapterById(exact.id);
164
+ }
165
+
166
+ function normalizeHarness(value) {
167
+ return String(value ?? '').trim().toLowerCase();
168
+ }
@@ -0,0 +1,7 @@
1
+ export class InstallerError extends Error {
2
+ constructor(message, code = 'INSTALLER_ERROR') {
3
+ super(message);
4
+ this.name = 'InstallerError';
5
+ this.code = code;
6
+ }
7
+ }
@@ -0,0 +1,35 @@
1
+ import {
2
+ createClaudeCodeAdapter,
3
+ createCodexAdapter,
4
+ createCopilotAdapter,
5
+ createCursorAdapter,
6
+ } from './adapters/shell-hook.js';
7
+ import {
8
+ createAntigravityAdapter,
9
+ createGeminiAdapter,
10
+ createOpenCodeAdapter,
11
+ createPiAdapter,
12
+ } from './adapters/extension-style.js';
13
+
14
+ const ADAPTERS = [
15
+ createClaudeCodeAdapter(),
16
+ createCodexAdapter(),
17
+ createCursorAdapter(),
18
+ createCopilotAdapter(),
19
+ createOpenCodeAdapter(),
20
+ createGeminiAdapter(),
21
+ createPiAdapter(),
22
+ createAntigravityAdapter(),
23
+ ];
24
+
25
+ export function listAdapters() {
26
+ return ADAPTERS.map((adapter) => ({ ...adapter }));
27
+ }
28
+
29
+ export function getAdapterById(id) {
30
+ return ADAPTERS.find((adapter) => adapter.id === id) ?? null;
31
+ }
32
+
33
+ export function getAdapterLabels() {
34
+ return ADAPTERS.map((adapter) => adapter.label);
35
+ }