@bastani/atomic 0.5.0-1 → 0.5.0-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/.atomic/workflows/hello/claude/index.ts +44 -0
- package/.atomic/workflows/hello/copilot/index.ts +58 -0
- package/.atomic/workflows/hello/opencode/index.ts +58 -0
- package/.atomic/workflows/hello-parallel/claude/index.ts +76 -0
- package/.atomic/workflows/hello-parallel/copilot/index.ts +105 -0
- package/.atomic/workflows/hello-parallel/opencode/index.ts +115 -0
- package/.atomic/workflows/ralph/claude/index.ts +149 -0
- package/.atomic/workflows/ralph/copilot/index.ts +162 -0
- package/.atomic/workflows/ralph/helpers/git.ts +34 -0
- package/.atomic/workflows/ralph/helpers/prompts.ts +538 -0
- package/.atomic/workflows/ralph/helpers/review.ts +32 -0
- package/.atomic/workflows/ralph/opencode/index.ts +164 -0
- package/.atomic/workflows/tsconfig.json +22 -0
- package/.claude/agents/code-simplifier.md +52 -0
- package/.claude/agents/codebase-analyzer.md +166 -0
- package/.claude/agents/codebase-locator.md +122 -0
- package/.claude/agents/codebase-online-researcher.md +148 -0
- package/.claude/agents/codebase-pattern-finder.md +247 -0
- package/.claude/agents/codebase-research-analyzer.md +179 -0
- package/.claude/agents/codebase-research-locator.md +145 -0
- package/.claude/agents/debugger.md +91 -0
- package/.claude/agents/orchestrator.md +19 -0
- package/.claude/agents/planner.md +106 -0
- package/.claude/agents/reviewer.md +97 -0
- package/.claude/agents/worker.md +165 -0
- package/.github/agents/code-simplifier.md +52 -0
- package/.github/agents/codebase-analyzer.md +166 -0
- package/.github/agents/codebase-locator.md +122 -0
- package/.github/agents/codebase-online-researcher.md +146 -0
- package/.github/agents/codebase-pattern-finder.md +247 -0
- package/.github/agents/codebase-research-analyzer.md +179 -0
- package/.github/agents/codebase-research-locator.md +145 -0
- package/.github/agents/debugger.md +98 -0
- package/.github/agents/orchestrator.md +27 -0
- package/.github/agents/planner.md +131 -0
- package/.github/agents/reviewer.md +94 -0
- package/.github/agents/worker.md +237 -0
- package/.github/lsp.json +93 -0
- package/.opencode/agents/code-simplifier.md +62 -0
- package/.opencode/agents/codebase-analyzer.md +171 -0
- package/.opencode/agents/codebase-locator.md +127 -0
- package/.opencode/agents/codebase-online-researcher.md +152 -0
- package/.opencode/agents/codebase-pattern-finder.md +252 -0
- package/.opencode/agents/codebase-research-analyzer.md +183 -0
- package/.opencode/agents/codebase-research-locator.md +149 -0
- package/.opencode/agents/debugger.md +99 -0
- package/.opencode/agents/orchestrator.md +27 -0
- package/.opencode/agents/planner.md +146 -0
- package/.opencode/agents/reviewer.md +102 -0
- package/.opencode/agents/worker.md +165 -0
- package/README.md +355 -299
- package/assets/settings.schema.json +0 -5
- package/package.json +7 -2
- package/src/cli.ts +16 -8
- package/src/commands/cli/workflow.ts +209 -15
- package/src/lib/spawn.ts +106 -31
- package/src/sdk/runtime/loader.ts +1 -1
- package/src/services/config/config-path.ts +1 -1
- package/src/services/config/settings.ts +0 -9
- package/src/services/system/agents.ts +94 -0
- package/src/services/system/auto-sync.ts +131 -0
- package/src/services/system/install-ui.ts +158 -0
- package/src/services/system/skills.ts +26 -17
- package/src/services/system/workflows.ts +105 -0
- package/src/theme/colors.ts +2 -0
- package/src/commands/cli/update.ts +0 -46
- package/src/services/system/download.ts +0 -325
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub release and download utilities for atomic updates
|
|
3
|
-
*
|
|
4
|
-
* Provides functions to:
|
|
5
|
-
* - Fetch latest release information from GitHub API
|
|
6
|
-
* - Download files with progress reporting
|
|
7
|
-
* - Verify SHA256 checksums
|
|
8
|
-
* - Get platform-specific filenames for binaries and config archives
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Error thrown when a file's checksum does not match the expected value.
|
|
15
|
-
* Indicates the file may be corrupted or tampered with.
|
|
16
|
-
*/
|
|
17
|
-
export class ChecksumMismatchError extends Error {
|
|
18
|
-
constructor(filename: string) {
|
|
19
|
-
super(`Checksum verification failed for ${filename}`);
|
|
20
|
-
this.name = "ChecksumMismatchError";
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** GitHub repository for atomic */
|
|
25
|
-
export const GITHUB_REPO = "flora131/atomic";
|
|
26
|
-
|
|
27
|
-
/** Information about a GitHub release */
|
|
28
|
-
export interface ReleaseInfo {
|
|
29
|
-
/** Version string without 'v' prefix (e.g., "0.2.0") */
|
|
30
|
-
version: string;
|
|
31
|
-
/** Full tag name (e.g., "v0.2.0") */
|
|
32
|
-
tagName: string;
|
|
33
|
-
/** ISO date when the release was published */
|
|
34
|
-
publishedAt: string;
|
|
35
|
-
/** Release notes in markdown format */
|
|
36
|
-
body: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** GitHub API release response structure */
|
|
40
|
-
interface GitHubReleaseResponse {
|
|
41
|
-
tag_name: string;
|
|
42
|
-
published_at: string;
|
|
43
|
-
body: string | null;
|
|
44
|
-
prerelease: boolean;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Fetch the latest release info from GitHub API.
|
|
49
|
-
*
|
|
50
|
-
* @returns Release information for the latest version
|
|
51
|
-
* @throws Error if the API request fails or rate limit is exceeded
|
|
52
|
-
*/
|
|
53
|
-
export async function getLatestRelease(): Promise<ReleaseInfo> {
|
|
54
|
-
const url = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
55
|
-
|
|
56
|
-
const headers: Record<string, string> = {
|
|
57
|
-
Accept: "application/vnd.github.v3+json",
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// Include token if available to avoid rate limits
|
|
61
|
-
if (process.env.GITHUB_TOKEN) {
|
|
62
|
-
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const response = await fetch(url, { headers });
|
|
66
|
-
|
|
67
|
-
if (!response.ok) {
|
|
68
|
-
if (response.status === 403) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
"GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable to increase limit."
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
if (response.status === 404) {
|
|
74
|
-
throw new Error("No releases found for this repository.");
|
|
75
|
-
}
|
|
76
|
-
throw new Error(`Failed to fetch release info: ${response.status} ${response.statusText}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const data = (await response.json()) as GitHubReleaseResponse;
|
|
80
|
-
return {
|
|
81
|
-
version: data.tag_name.replace(/^v/, ""),
|
|
82
|
-
tagName: data.tag_name,
|
|
83
|
-
publishedAt: data.published_at,
|
|
84
|
-
body: data.body || "",
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Fetch the latest prerelease info from GitHub API.
|
|
90
|
-
*
|
|
91
|
-
* Queries the releases list and returns the first release marked as a prerelease.
|
|
92
|
-
*
|
|
93
|
-
* @returns Release information for the latest prerelease version
|
|
94
|
-
* @throws Error if no prerelease is found or the API request fails
|
|
95
|
-
*/
|
|
96
|
-
export async function getLatestPrerelease(): Promise<ReleaseInfo> {
|
|
97
|
-
const url = `https://api.github.com/repos/${GITHUB_REPO}/releases`;
|
|
98
|
-
|
|
99
|
-
const headers: Record<string, string> = {
|
|
100
|
-
Accept: "application/vnd.github.v3+json",
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
if (process.env.GITHUB_TOKEN) {
|
|
104
|
-
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const response = await fetch(url, { headers });
|
|
108
|
-
|
|
109
|
-
if (!response.ok) {
|
|
110
|
-
if (response.status === 403) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
"GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable to increase limit."
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
throw new Error(`Failed to fetch releases: ${response.status} ${response.statusText}`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const releases = (await response.json()) as GitHubReleaseResponse[];
|
|
119
|
-
const prerelease = releases.find((r) => r.prerelease);
|
|
120
|
-
|
|
121
|
-
if (!prerelease) {
|
|
122
|
-
throw new Error("No prerelease found for this repository.");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
version: prerelease.tag_name.replace(/^v/, ""),
|
|
127
|
-
tagName: prerelease.tag_name,
|
|
128
|
-
publishedAt: prerelease.published_at,
|
|
129
|
-
body: prerelease.body || "",
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Fetch release info for a specific version from GitHub API.
|
|
135
|
-
*
|
|
136
|
-
* @param version - Version string with or without 'v' prefix (e.g., "0.2.0" or "v0.2.0")
|
|
137
|
-
* @returns Release information for the specified version
|
|
138
|
-
* @throws Error if the version is not found or API request fails
|
|
139
|
-
*/
|
|
140
|
-
export async function getReleaseByVersion(version: string): Promise<ReleaseInfo> {
|
|
141
|
-
// Ensure version has 'v' prefix for tag lookup
|
|
142
|
-
const tagName = version.startsWith("v") ? version : `v${version}`;
|
|
143
|
-
const url = `https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${tagName}`;
|
|
144
|
-
|
|
145
|
-
const headers: Record<string, string> = {
|
|
146
|
-
Accept: "application/vnd.github.v3+json",
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
if (process.env.GITHUB_TOKEN) {
|
|
150
|
-
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const response = await fetch(url, { headers });
|
|
154
|
-
|
|
155
|
-
if (!response.ok) {
|
|
156
|
-
if (response.status === 403) {
|
|
157
|
-
throw new Error(
|
|
158
|
-
"GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable to increase limit."
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
if (response.status === 404) {
|
|
162
|
-
throw new Error(`Version ${tagName} not found.`);
|
|
163
|
-
}
|
|
164
|
-
throw new Error(`Failed to fetch release info: ${response.status} ${response.statusText}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const data = (await response.json()) as GitHubReleaseResponse;
|
|
168
|
-
return {
|
|
169
|
-
version: data.tag_name.replace(/^v/, ""),
|
|
170
|
-
tagName: data.tag_name,
|
|
171
|
-
publishedAt: data.published_at,
|
|
172
|
-
body: data.body || "",
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Progress callback type for download operations.
|
|
178
|
-
* Called periodically with the download percentage (0-100).
|
|
179
|
-
*/
|
|
180
|
-
export type ProgressCallback = (percent: number) => void;
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Download a file from a URL to a local path with optional progress reporting.
|
|
184
|
-
*
|
|
185
|
-
* @param url - The URL to download from
|
|
186
|
-
* @param destPath - The local path to save the file to
|
|
187
|
-
* @param onProgress - Optional callback for progress updates (percentage 0-100)
|
|
188
|
-
* @throws Error if the download fails
|
|
189
|
-
*/
|
|
190
|
-
export async function downloadFile(
|
|
191
|
-
url: string,
|
|
192
|
-
destPath: string,
|
|
193
|
-
onProgress?: ProgressCallback
|
|
194
|
-
): Promise<void> {
|
|
195
|
-
const headers: Record<string, string> = {};
|
|
196
|
-
|
|
197
|
-
// Include token if available to avoid rate limits on download URLs
|
|
198
|
-
if (process.env.GITHUB_TOKEN) {
|
|
199
|
-
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const response = await fetch(url, {
|
|
203
|
-
redirect: "follow",
|
|
204
|
-
headers,
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
if (!response.ok) {
|
|
208
|
-
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const contentLength = response.headers.get("content-length");
|
|
212
|
-
const total = contentLength ? parseInt(contentLength, 10) : 0;
|
|
213
|
-
let loaded = 0;
|
|
214
|
-
|
|
215
|
-
const reader = response.body?.getReader();
|
|
216
|
-
if (!reader) {
|
|
217
|
-
throw new Error("Failed to read response body");
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const chunks: Uint8Array[] = [];
|
|
221
|
-
|
|
222
|
-
while (true) {
|
|
223
|
-
const { done, value } = await reader.read();
|
|
224
|
-
if (done) break;
|
|
225
|
-
|
|
226
|
-
chunks.push(value);
|
|
227
|
-
loaded += value.length;
|
|
228
|
-
|
|
229
|
-
if (onProgress && total > 0) {
|
|
230
|
-
onProgress(Math.round((loaded / total) * 100));
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Combine chunks and write to file
|
|
235
|
-
const data = new Uint8Array(loaded);
|
|
236
|
-
let position = 0;
|
|
237
|
-
for (const chunk of chunks) {
|
|
238
|
-
data.set(chunk, position);
|
|
239
|
-
position += chunk.length;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
await Bun.write(destPath, data);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Parse a checksums.txt file content into a map of filename to hash.
|
|
247
|
-
*
|
|
248
|
-
* The format is: "<hash> <filename>" (two spaces between hash and filename)
|
|
249
|
-
* This is the standard format used by sha256sum command.
|
|
250
|
-
*
|
|
251
|
-
* @param checksumsTxt - Content of the checksums.txt file
|
|
252
|
-
* @returns Map of filename to lowercase hex hash
|
|
253
|
-
*/
|
|
254
|
-
export function parseChecksums(checksumsTxt: string): Map<string, string> {
|
|
255
|
-
const checksums = new Map<string, string>();
|
|
256
|
-
const lines = checksumsTxt.trim().split("\n");
|
|
257
|
-
|
|
258
|
-
for (const line of lines) {
|
|
259
|
-
// Format: "<hash> <filename>" (two spaces between)
|
|
260
|
-
const match = line.match(/^([a-fA-F0-9]{64})\s{2}(.+)$/);
|
|
261
|
-
if (match) {
|
|
262
|
-
const hash = match[1];
|
|
263
|
-
const filename = match[2];
|
|
264
|
-
if (hash && filename) {
|
|
265
|
-
checksums.set(filename, hash.toLowerCase());
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return checksums;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Verify SHA256 checksum of a file against checksums.txt content.
|
|
275
|
-
*
|
|
276
|
-
* @param filePath - Path to the file to verify
|
|
277
|
-
* @param checksumsTxt - Content of checksums.txt file
|
|
278
|
-
* @param expectedFilename - The filename to look up in checksums.txt
|
|
279
|
-
* @returns True if the checksum matches, false otherwise
|
|
280
|
-
* @throws Error if the filename is not found in checksums.txt
|
|
281
|
-
*/
|
|
282
|
-
export async function verifyChecksum(
|
|
283
|
-
filePath: string,
|
|
284
|
-
checksumsTxt: string,
|
|
285
|
-
expectedFilename: string
|
|
286
|
-
): Promise<boolean> {
|
|
287
|
-
const checksums = parseChecksums(checksumsTxt);
|
|
288
|
-
const expectedHash = checksums.get(expectedFilename);
|
|
289
|
-
|
|
290
|
-
if (!expectedHash) {
|
|
291
|
-
throw new Error(`No checksum found for ${expectedFilename}`);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Calculate actual hash using Bun's crypto
|
|
295
|
-
const file = Bun.file(filePath);
|
|
296
|
-
const data = await file.arrayBuffer();
|
|
297
|
-
const hasher = new Bun.CryptoHasher("sha256");
|
|
298
|
-
hasher.update(data);
|
|
299
|
-
const actualHash = hasher.digest("hex");
|
|
300
|
-
|
|
301
|
-
return actualHash === expectedHash;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
declare const __ATOMIC_BASELINE__: boolean | undefined;
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Get platform-specific binary filename for download.
|
|
308
|
-
*
|
|
309
|
-
* Returns the filename used in GitHub releases, e.g.:
|
|
310
|
-
* @param packageName - Full package name including scope (e.g. "atomic")
|
|
311
|
-
* @param version - Semver version without 'v' prefix (e.g. "0.4.30")
|
|
312
|
-
* @returns true if the package version exists, false otherwise
|
|
313
|
-
*/
|
|
314
|
-
export async function checkNpmPackageExists(
|
|
315
|
-
packageName: string,
|
|
316
|
-
version: string,
|
|
317
|
-
): Promise<boolean> {
|
|
318
|
-
const url = `https://registry.npmjs.org/${packageName}/${version}`;
|
|
319
|
-
try {
|
|
320
|
-
const response = await fetch(url);
|
|
321
|
-
return response.ok;
|
|
322
|
-
} catch {
|
|
323
|
-
return false;
|
|
324
|
-
}
|
|
325
|
-
}
|