Improve `sentry init` runtime harness with Dirac-inspired patterns
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 25/100
- issue の種類
- 機能追加
- 明瞭さ
- おおむね明確
- 活発さ
- 静か
- 技術スタック
- bun, typescript
調査の方向性
推奨されているビルド順序から始め、src/lib/init/local-ops.ts と src/lib/init/types.ts から着手し、Verification に記載されたフェーズ固有のテストを実行します。後のフェーズに取りかかる前に、指定された AST ファイルと wizard-runner.ts を確認します。フェーズテスト、統合コマンド、build、typecheck、lint、フルスイートがすべてパスすれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Summary
Adopt three patterns from the Dirac coding agent to strengthen the sentry init local runtime:
- Content hashing (from
line-hashing.ts) — FNV-1a file hashing for stale-read detection - Atomic patchsets with rollback (simplified from
CheckpointTracker) — in-memory backup before writes - Tree-sitter AST infrastructure (from
services/tree-sitter/) — WASM-based code parsing for local framework detection, entry point discovery, and post-patch syntax validation
What We're NOT Picking
- Hash-anchored line edits: Requires Mastra protocol changes (server must produce hash-referenced diffs). Out of scope.
- React Ink TUI: We use Stricli + @clack/prompts. Different paradigm.
- Full eval harness: The Dirac eval system is benchmark-focused, not applicable.
Phase 1: Content Hashing & Staleness Detection (no new deps)
Goal: Detect when files change between read-files and apply-patchset operations.
New file: src/lib/init/content-hash.ts
fnv1a(content: string): string— FNV-1a 32-bit hash → 8-char hex (5 lines, from Dirac'sline-hashing.ts)FileSnapshottype —Map<string, string>of path→hashcreateSnapshot(),recordFile(),isStale()helpers
Modify: src/lib/init/local-ops.ts
- Add module-scoped
FileSnapshotinstance, reset per wizard run - In
readFiles(): record content hash for each file after reading - In
applyEdits(): before applying edits, re-read the file and check staleness. If stale,log.warn()but continue (the fuzzy replacer may still succeed) - Return
contentHashesin theread-filesresult data so the Mastra workflow can optionally use it later
Modify: src/lib/init/types.ts
- Add optional
contentHashes?: Record<string, string>to read-files result shape
New tests
test/lib/init/content-hash.test.ts— basic hash consistencytest/lib/init/content-hash.property.test.ts— determinism, collision resistance
Phase 2: Atomic Patchsets with Rollback (no new deps)
Goal: If any patch in a patchset fails, restore all previously-applied files.
Modify: src/lib/init/local-ops.ts
Restructure applyPatchset() with in-memory backup:
type FileBackup =
| { type: 'existed'; path: string; absPath: string; content: string }
| { type: 'created'; path: string; absPath: string };
- Before each patch: snapshot the file's current content (or record it as nonexistent)
- On failure: restore all backups in reverse order (write-back for modify/delete, unlink for create)
- On success: discard backups
Why in-memory, not git stash?
git stashhas side effects on user's stash stack- The wizard already checks for clean git state in
git.ts— users cangit checkout .for ultimate recovery - Patchsets are small (typically <10 files, <100KB total)
Modify: src/lib/init/types.ts
- Add
rolledBack?: booleanandrollbackErrors?: string[]toLocalOpResult
New test: test/lib/init/local-ops-rollback.test.ts
- 3-patch patchset where patch #3 fails → verify patches #1 and #2 rolled back
- Modify + rollback restores original content
- Create + rollback deletes the file
Phase 3: Tree-sitter AST Infrastructure
Goal: Add WASM-based tree-sitter parsing, lazy-loaded and cached.
New dependency
bun add -d web-tree-sitter
NOT adding tree-sitter-wasms as a dependency. Grammar WASMs downloaded on demand (~250-810KB each, cached in ~/.sentry/grammars/). The web-tree-sitter JS runtime (~60KB) bundles via esbuild. Its WASM runtime (~2.5MB) is also downloaded on demand alongside grammars.
New file: src/lib/ast/parser.ts
Adapted from Dirac's languageParser.ts:
EXTENSION_TO_LANGUAGEmap —.js→javascript,.ts→typescript,.tsx→tsx,.py→python,.go→go,.rb→ruby,.php→php,.java→javagetParser(language)— lazy init of web-tree-sitter, loads grammar WASM on demandparseFile(content, filePath)— returnsParser.Tree | nullisAstSupported(filePath)— extension check- Two-level cache: language→Parser cache + grammar→WASM cache (same as Dirac)
New file: src/lib/ast/grammar-loader.ts
ensureGrammar(language)— checks~/.sentry/grammars/<version>/, downloads from CDN if missingensureRuntime()— same for thetree-sitter.wasmruntime- CDN source: unpkg.com or GitHub-hosted (configurable)
- Downloads use
fetch()with timeout, write viaBun.write()
New file: src/lib/ast/queries.ts
S-expression queries per language, inspired by Dirac's queries/ directory:
type FrameworkSignal = {
framework: string; // 'nextjs', 'express', 'django', etc.
confidence: number; // 0.0–1.0
evidence: string; // human-readable
file: string;
line: number;
};
type EntryPoint = {
file: string;
line: number;
kind: 'server-start' | 'app-export' | 'main-function' | 'sdk-init';
pattern: string;
};
Functions: detectFramework(), findEntryPoints(), findSentryConfig(), generateOutline()
New file: src/lib/ast/errors.ts
AstError extends CliError— for grammar load failures, parse errors
New file: src/lib/ast/index.ts
Barrel re-export.
Build impact
web-tree-sitterJS: ~60KB bundled (esbuild handles it)- WASM runtime: NOT bundled, downloaded on demand (~2.5MB, cached)
- Grammar WASMs: NOT bundled, downloaded on demand (~250-810KB each, cached)
- Net binary size increase: ~60KB (just the JS loader)
New test: test/lib/ast/parser.test.ts
- Mock grammar download, parse a JS fixture, verify tree exists
- Unsupported extension returns null
Phase 4: AST-Based Intelligence for Init
Goal: Use tree-sitter to send richer context to the Mastra workflow.
New file: src/lib/init/ast-context.ts
type ProjectAstContext = {
frameworks: FrameworkSignal[];
entryPoints: EntryPoint[];
existingSentry: SentryConfig[];
fileOutlines: Record<string, string>;
};
async function buildProjectAstContext(
cwd: string,
dirListing: DirEntry[]
): Promise<ProjectAstContext | null>
File selection heuristic (scan a targeted subset, not the whole tree):
- Known entry point filenames:
src/index.ts,src/app.ts,app.py,manage.py,main.go,pages/_app.tsx,app/layout.tsx,next.config.js, etc. - Files matching
*sentry* - Fallback: first 5 source files in
dirListing
Modify: src/lib/init/wizard-runner.ts
After precomputeDirListing(), before run.startAsync():
let astContext = null;
try {
const { buildProjectAstContext } = await import("./ast-context.js");
astContext = await buildProjectAstContext(directory, dirListing);
} catch {
// AST unavailable — continue without it
}
Pass astContext in inputData to the workflow.
New local-op: parse-files
Register as a new operation type in local-ops.ts for the Mastra workflow to request on-demand AST analysis.
Modify: src/lib/init/types.ts
- Add
ParseFilesPayloadtype - Add to
LocalOpPayloadunion
Phase 5: AST Validation of Applied Patches
Goal: Verify modified files are syntactically valid after patching.
New file: src/lib/init/ast-validation.ts
type ValidationResult = {
valid: boolean;
errors: Array<{ line: number; column: number; message: string }>;
};
async function validateSyntax(content: string, filePath: string): Promise<ValidationResult>
Implementation: parse with tree-sitter, walk CST for ERROR/MISSING nodes.
Modify: src/lib/init/local-ops.ts
In applySinglePatch() after writing a modify patch:
const result = await validateSyntax(content, patch.path);
if (!result.valid) {
log.warn(`Syntax issues in ${patch.path}: ${result.errors.length} error(s)`);
}
Advisory only (warn, don't fail). Validation errors are included in the patchset result metadata so the Mastra workflow can decide whether to retry.
Recommended Build Order
Phase 1 (content hashing) ─── 1 day, no deps, immediate value
↓
Phase 2 (atomic rollback) ─── 1.5 days, no deps, immediate value
↓
Phase 3 (tree-sitter infra) ─── 3-4 days, adds web-tree-sitter
↓
Phase 4 (AST intelligence) ─── 3-4 days, uses Phase 3
↓
Phase 5 (AST validation) ─── 2 days, uses Phase 3
Phases 1-2 are dependency-free quick wins. Phase 3 is the risky foundation. Phases 4-5 build on it.
Total: ~11-13 days
Key Files Modified
| File | Phase | Change |
|---|---|---|
src/lib/init/local-ops.ts |
1,2,4 | Content hashing, atomic rollback, parse-files handler |
src/lib/init/types.ts |
1,2,4 | New payload types, result fields |
src/lib/init/wizard-runner.ts |
4 | Pass astContext to workflow |
package.json |
3 | Add web-tree-sitter devDependency |
Key Files Created
| File | Phase |
|---|---|
src/lib/init/content-hash.ts |
1 |
src/lib/ast/parser.ts |
3 |
src/lib/ast/grammar-loader.ts |
3 |
src/lib/ast/queries.ts |
3 |
src/lib/ast/errors.ts |
3 |
src/lib/ast/index.ts |
3 |
src/lib/init/ast-context.ts |
4 |
src/lib/init/ast-validation.ts |
5 |
Risks
| Risk | Level | Mitigation |
|---|---|---|
web-tree-sitter WASM loading in Bun compiled binary |
High | Download WASM on demand (not embedded). Test early in Phase 3. |
| Grammar CDN unreliable | Medium | Host grammars on GitHub Releases or bundle JS/TS grammars as fallback (~2MB) |
| Tree-sitter adds complexity for limited init-specific value | Medium | Phases 1-2 deliver value without tree-sitter. AST phases are additive, not critical path. |
Mastra workflow ignores astContext initially |
Low | Context is advisory — server can adopt it incrementally. Local value (validation) is standalone. |
Verification
- Phase 1:
bun test test/lib/init/content-hash— property tests pass - Phase 2:
bun test test/lib/init/local-ops-rollback— rollback tests pass - Phase 3:
bun test test/lib/ast/parser— JS/TS parsing works - Phase 4:
bun test test/lib/init/ast-context— framework detection returns signals - Phase 5:
bun test test/lib/init/ast-validation— catches broken syntax - Integration:
bun run dev -- init ./test-project --dry-run— wizard completes with AST context - Build:
bun run build— binary size increase < 100KB (only JS loader bundled) - Full suite:
bun run typecheck && bun run lint && bun test
- 主要言語
- TypeScript
- スター
- 123
- フォーク
- 14
- 平均マージ
- 21時間 48分
- マージ済み PR(30日)
- 64
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
getsentry/cli のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
Evaluate Twinkleplop for terminal code highlighting対応中かも @MathurAditya724 が 13 日前に担当しました。 オープンjared
getsentry/cli#1633 · コメント 1 件 · リアクション 2 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
似ている issue
-
Bump Firebase JS SDK (12.19.0 → 13.0.0)対応中かも @SelaseKay が今日担当しました。 オープンNeeds Attention type: enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
invertase/react-native-firebase#9364 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 4 日以内に返信
-
e2e-failure ready-to-code
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
redhat-developer/rhdh-plugin-export-overlays#4261 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
[Bug] 官网文档的图片挂了オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
メンテナーはふだん 1 日以内に返信
-
area:cli bug triage:in-progress
難易度 1/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信