claude-opencode-viewer 2.6.47 → 2.6.48
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/.agents/skills/code-review-expert/README.md +74 -0
- package/.agents/skills/code-review-expert/SKILL.md +156 -0
- package/.agents/skills/code-review-expert/agents/agent.yaml +7 -0
- package/.agents/skills/code-review-expert/references/code-quality-checklist.md +130 -0
- package/.agents/skills/code-review-expert/references/removal-plan.md +52 -0
- package/.agents/skills/code-review-expert/references/security-checklist.md +118 -0
- package/.agents/skills/code-review-expert/references/solid-checklist.md +65 -0
- package/index-pc.html +54 -11
- package/index.html +14 -5
- package/package.json +1 -1
- package/server.js +20 -0
- package/skills-lock.json +10 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Code Review Expert
|
|
2
|
+
|
|
3
|
+
A comprehensive code review skill for AI agents. Performs structured reviews with a senior engineer lens, covering architecture, security, performance, and code quality.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx skills add sanyuan0704/sanyuan-skills --path skills/code-review-expert
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **SOLID Principles** - Detect SRP, OCP, LSP, ISP, DIP violations
|
|
14
|
+
- **Security Scan** - XSS, injection, SSRF, race conditions, auth gaps, secrets leakage
|
|
15
|
+
- **Performance** - N+1 queries, CPU hotspots, missing cache, memory issues
|
|
16
|
+
- **Error Handling** - Swallowed exceptions, async errors, missing boundaries
|
|
17
|
+
- **Boundary Conditions** - Null handling, empty collections, off-by-one, numeric limits
|
|
18
|
+
- **Removal Planning** - Identify dead code with safe deletion plans
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
After installation, simply run:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
/code-review-expert
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The skill will automatically review your current git changes.
|
|
29
|
+
|
|
30
|
+
## Workflow
|
|
31
|
+
|
|
32
|
+
1. **Preflight** - Scope changes via `git diff`
|
|
33
|
+
2. **SOLID + Architecture** - Check design principles
|
|
34
|
+
3. **Removal Candidates** - Find dead/unused code
|
|
35
|
+
4. **Security Scan** - Vulnerability detection
|
|
36
|
+
5. **Code Quality** - Error handling, performance, boundaries
|
|
37
|
+
6. **Output** - Findings by severity (P0-P3)
|
|
38
|
+
7. **Confirmation** - Ask user before implementing fixes
|
|
39
|
+
|
|
40
|
+
## Severity Levels
|
|
41
|
+
|
|
42
|
+
| Level | Name | Action |
|
|
43
|
+
|-------|------|--------|
|
|
44
|
+
| P0 | Critical | Must block merge |
|
|
45
|
+
| P1 | High | Should fix before merge |
|
|
46
|
+
| P2 | Medium | Fix or create follow-up |
|
|
47
|
+
| P3 | Low | Optional improvement |
|
|
48
|
+
|
|
49
|
+
## Structure
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
code-review-expert/
|
|
53
|
+
├── SKILL.md # Main skill definition
|
|
54
|
+
├── agents/
|
|
55
|
+
│ └── agent.yaml # Agent interface config
|
|
56
|
+
└── references/
|
|
57
|
+
├── solid-checklist.md # SOLID smell prompts
|
|
58
|
+
├── security-checklist.md # Security & reliability
|
|
59
|
+
├── code-quality-checklist.md # Error, perf, boundaries
|
|
60
|
+
└── removal-plan.md # Deletion planning template
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## References
|
|
64
|
+
|
|
65
|
+
Each checklist provides detailed prompts and anti-patterns:
|
|
66
|
+
|
|
67
|
+
- **solid-checklist.md** - SOLID violations + common code smells
|
|
68
|
+
- **security-checklist.md** - OWASP risks, race conditions, crypto, supply chain
|
|
69
|
+
- **code-quality-checklist.md** - Error handling, caching, N+1, null safety
|
|
70
|
+
- **removal-plan.md** - Safe vs deferred deletion with rollback plans
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review-expert
|
|
3
|
+
description: "Expert code review of current git changes with a senior engineer lens. Detects SOLID violations, security risks, and proposes actionable improvements."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Review Expert
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Perform a structured review of the current git changes with focus on SOLID, architecture, removal candidates, and security risks. Default to review-only output unless the user asks to implement changes.
|
|
11
|
+
|
|
12
|
+
## Severity Levels
|
|
13
|
+
|
|
14
|
+
| Level | Name | Description | Action |
|
|
15
|
+
|-------|------|-------------|--------|
|
|
16
|
+
| **P0** | Critical | Security vulnerability, data loss risk, correctness bug | Must block merge |
|
|
17
|
+
| **P1** | High | Logic error, significant SOLID violation, performance regression | Should fix before merge |
|
|
18
|
+
| **P2** | Medium | Code smell, maintainability concern, minor SOLID violation | Fix in this PR or create follow-up |
|
|
19
|
+
| **P3** | Low | Style, naming, minor suggestion | Optional improvement |
|
|
20
|
+
|
|
21
|
+
## Workflow
|
|
22
|
+
|
|
23
|
+
### 1) Preflight context
|
|
24
|
+
|
|
25
|
+
- Use `git status -sb`, `git diff --stat`, and `git diff` to scope changes.
|
|
26
|
+
- If needed, use `rg` or `grep` to find related modules, usages, and contracts.
|
|
27
|
+
- Identify entry points, ownership boundaries, and critical paths (auth, payments, data writes, network).
|
|
28
|
+
|
|
29
|
+
**Edge cases:**
|
|
30
|
+
- **No changes**: If `git diff` is empty, inform user and ask if they want to review staged changes or a specific commit range.
|
|
31
|
+
- **Large diff (>500 lines)**: Summarize by file first, then review in batches by module/feature area.
|
|
32
|
+
- **Mixed concerns**: Group findings by logical feature, not just file order.
|
|
33
|
+
|
|
34
|
+
### 2) SOLID + architecture smells
|
|
35
|
+
|
|
36
|
+
- Load `references/solid-checklist.md` for specific prompts.
|
|
37
|
+
- Look for:
|
|
38
|
+
- **SRP**: Overloaded modules with unrelated responsibilities.
|
|
39
|
+
- **OCP**: Frequent edits to add behavior instead of extension points.
|
|
40
|
+
- **LSP**: Subclasses that break expectations or require type checks.
|
|
41
|
+
- **ISP**: Wide interfaces with unused methods.
|
|
42
|
+
- **DIP**: High-level logic tied to low-level implementations.
|
|
43
|
+
- When you propose a refactor, explain *why* it improves cohesion/coupling and outline a minimal, safe split.
|
|
44
|
+
- If refactor is non-trivial, propose an incremental plan instead of a large rewrite.
|
|
45
|
+
|
|
46
|
+
### 3) Removal candidates + iteration plan
|
|
47
|
+
|
|
48
|
+
- Load `references/removal-plan.md` for template.
|
|
49
|
+
- Identify code that is unused, redundant, or feature-flagged off.
|
|
50
|
+
- Distinguish **safe delete now** vs **defer with plan**.
|
|
51
|
+
- Provide a follow-up plan with concrete steps and checkpoints (tests/metrics).
|
|
52
|
+
|
|
53
|
+
### 4) Security and reliability scan
|
|
54
|
+
|
|
55
|
+
- Load `references/security-checklist.md` for coverage.
|
|
56
|
+
- Check for:
|
|
57
|
+
- XSS, injection (SQL/NoSQL/command), SSRF, path traversal
|
|
58
|
+
- AuthZ/AuthN gaps, missing tenancy checks
|
|
59
|
+
- Secret leakage or API keys in logs/env/files
|
|
60
|
+
- Rate limits, unbounded loops, CPU/memory hotspots
|
|
61
|
+
- Unsafe deserialization, weak crypto, insecure defaults
|
|
62
|
+
- **Race conditions**: concurrent access, check-then-act, TOCTOU, missing locks
|
|
63
|
+
- Call out both **exploitability** and **impact**.
|
|
64
|
+
|
|
65
|
+
### 5) Code quality scan
|
|
66
|
+
|
|
67
|
+
- Load `references/code-quality-checklist.md` for coverage.
|
|
68
|
+
- Check for:
|
|
69
|
+
- **Error handling**: swallowed exceptions, overly broad catch, missing error handling, async errors
|
|
70
|
+
- **Performance**: N+1 queries, CPU-intensive ops in hot paths, missing cache, unbounded memory
|
|
71
|
+
- **Boundary conditions**: null/undefined handling, empty collections, numeric boundaries, off-by-one
|
|
72
|
+
- Flag issues that may cause silent failures or production incidents.
|
|
73
|
+
|
|
74
|
+
### 6) Output format
|
|
75
|
+
|
|
76
|
+
Structure your review as follows:
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
## Code Review Summary
|
|
80
|
+
|
|
81
|
+
**Files reviewed**: X files, Y lines changed
|
|
82
|
+
**Overall assessment**: [APPROVE / REQUEST_CHANGES / COMMENT]
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Findings
|
|
87
|
+
|
|
88
|
+
### P0 - Critical
|
|
89
|
+
(none or list)
|
|
90
|
+
|
|
91
|
+
### P1 - High
|
|
92
|
+
1. **[file:line]** Brief title
|
|
93
|
+
- Description of issue
|
|
94
|
+
- Suggested fix
|
|
95
|
+
|
|
96
|
+
### P2 - Medium
|
|
97
|
+
2. (continue numbering across sections)
|
|
98
|
+
- ...
|
|
99
|
+
|
|
100
|
+
### P3 - Low
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Removal/Iteration Plan
|
|
106
|
+
(if applicable)
|
|
107
|
+
|
|
108
|
+
## Additional Suggestions
|
|
109
|
+
(optional improvements, not blocking)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Inline comments**: Use this format for file-specific findings:
|
|
113
|
+
```
|
|
114
|
+
::code-comment{file="path/to/file.ts" line="42" severity="P1"}
|
|
115
|
+
Description of the issue and suggested fix.
|
|
116
|
+
::
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Clean review**: If no issues found, explicitly state:
|
|
120
|
+
- What was checked
|
|
121
|
+
- Any areas not covered (e.g., "Did not verify database migrations")
|
|
122
|
+
- Residual risks or recommended follow-up tests
|
|
123
|
+
|
|
124
|
+
### 7) Next steps confirmation
|
|
125
|
+
|
|
126
|
+
After presenting findings, ask user how to proceed:
|
|
127
|
+
|
|
128
|
+
```markdown
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Next Steps
|
|
132
|
+
|
|
133
|
+
I found X issues (P0: _, P1: _, P2: _, P3: _).
|
|
134
|
+
|
|
135
|
+
**How would you like to proceed?**
|
|
136
|
+
|
|
137
|
+
1. **Fix all** - I'll implement all suggested fixes
|
|
138
|
+
2. **Fix P0/P1 only** - Address critical and high priority issues
|
|
139
|
+
3. **Fix specific items** - Tell me which issues to fix
|
|
140
|
+
4. **No changes** - Review complete, no implementation needed
|
|
141
|
+
|
|
142
|
+
Please choose an option or provide specific instructions.
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Important**: Do NOT implement any changes until user explicitly confirms. This is a review-first workflow.
|
|
146
|
+
|
|
147
|
+
## Resources
|
|
148
|
+
|
|
149
|
+
### references/
|
|
150
|
+
|
|
151
|
+
| File | Purpose |
|
|
152
|
+
|------|---------|
|
|
153
|
+
| `solid-checklist.md` | SOLID smell prompts and refactor heuristics |
|
|
154
|
+
| `security-checklist.md` | Web/app security and runtime risk checklist |
|
|
155
|
+
| `code-quality-checklist.md` | Error handling, performance, boundary conditions |
|
|
156
|
+
| `removal-plan.md` | Template for deletion candidates and follow-up plan |
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Code Review Expert"
|
|
3
|
+
short_description: "Senior engineer code review: SOLID, security, performance, error handling"
|
|
4
|
+
default_prompt: "Review current git changes for SOLID violations, security risks, race conditions, error handling issues, performance problems, and boundary condition bugs."
|
|
5
|
+
|
|
6
|
+
# Agent-agnostic skill - works with any LLM provider.
|
|
7
|
+
# No provider-specific configuration required.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Code Quality Checklist
|
|
2
|
+
|
|
3
|
+
## Error Handling
|
|
4
|
+
|
|
5
|
+
### Anti-patterns to Flag
|
|
6
|
+
|
|
7
|
+
- **Swallowed exceptions**: Empty catch blocks or catch with only logging
|
|
8
|
+
```javascript
|
|
9
|
+
try { ... } catch (e) { } // Silent failure
|
|
10
|
+
try { ... } catch (e) { console.log(e) } // Log and forget
|
|
11
|
+
```
|
|
12
|
+
- **Overly broad catch**: Catching `Exception`/`Error` base class instead of specific types
|
|
13
|
+
- **Error information leakage**: Stack traces or internal details exposed to users
|
|
14
|
+
- **Missing error handling**: No try-catch around fallible operations (I/O, network, parsing)
|
|
15
|
+
- **Async error handling**: Unhandled promise rejections, missing `.catch()`, no error boundary
|
|
16
|
+
|
|
17
|
+
### Best Practices to Check
|
|
18
|
+
|
|
19
|
+
- [ ] Errors are caught at appropriate boundaries
|
|
20
|
+
- [ ] Error messages are user-friendly (no internal details exposed)
|
|
21
|
+
- [ ] Errors are logged with sufficient context for debugging
|
|
22
|
+
- [ ] Async errors are properly propagated or handled
|
|
23
|
+
- [ ] Fallback behavior is defined for recoverable errors
|
|
24
|
+
- [ ] Critical errors trigger alerts/monitoring
|
|
25
|
+
|
|
26
|
+
### Questions to Ask
|
|
27
|
+
- "What happens when this operation fails?"
|
|
28
|
+
- "Will the caller know something went wrong?"
|
|
29
|
+
- "Is there enough context to debug this error?"
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Performance & Caching
|
|
34
|
+
|
|
35
|
+
### CPU-Intensive Operations
|
|
36
|
+
|
|
37
|
+
- **Expensive operations in hot paths**: Regex compilation, JSON parsing, crypto in loops
|
|
38
|
+
- **Blocking main thread**: Sync I/O, heavy computation without worker/async
|
|
39
|
+
- **Unnecessary recomputation**: Same calculation done multiple times
|
|
40
|
+
- **Missing memoization**: Pure functions called repeatedly with same inputs
|
|
41
|
+
|
|
42
|
+
### Database & I/O
|
|
43
|
+
|
|
44
|
+
- **N+1 queries**: Loop that makes a query per item instead of batch
|
|
45
|
+
```javascript
|
|
46
|
+
// Bad: N+1
|
|
47
|
+
for (const id of ids) {
|
|
48
|
+
const user = await db.query(`SELECT * FROM users WHERE id = ?`, id)
|
|
49
|
+
}
|
|
50
|
+
// Good: Batch
|
|
51
|
+
const users = await db.query(`SELECT * FROM users WHERE id IN (?)`, ids)
|
|
52
|
+
```
|
|
53
|
+
- **Missing indexes**: Queries on unindexed columns
|
|
54
|
+
- **Over-fetching**: SELECT * when only few columns needed
|
|
55
|
+
- **No pagination**: Loading entire dataset into memory
|
|
56
|
+
|
|
57
|
+
### Caching Issues
|
|
58
|
+
|
|
59
|
+
- **Missing cache for expensive operations**: Repeated API calls, DB queries, computations
|
|
60
|
+
- **Cache without TTL**: Stale data served indefinitely
|
|
61
|
+
- **Cache without invalidation strategy**: Data updated but cache not cleared
|
|
62
|
+
- **Cache key collisions**: Insufficient key uniqueness
|
|
63
|
+
- **Caching user-specific data globally**: Security/privacy issue
|
|
64
|
+
|
|
65
|
+
### Memory
|
|
66
|
+
|
|
67
|
+
- **Unbounded collections**: Arrays/maps that grow without limit
|
|
68
|
+
- **Large object retention**: Holding references preventing GC
|
|
69
|
+
- **String concatenation in loops**: Use StringBuilder/join instead
|
|
70
|
+
- **Loading large files entirely**: Use streaming instead
|
|
71
|
+
|
|
72
|
+
### Questions to Ask
|
|
73
|
+
- "What's the time complexity of this operation?"
|
|
74
|
+
- "How does this behave with 10x/100x data?"
|
|
75
|
+
- "Is this result cacheable? Should it be?"
|
|
76
|
+
- "Can this be batched instead of one-by-one?"
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Boundary Conditions
|
|
81
|
+
|
|
82
|
+
### Null/Undefined Handling
|
|
83
|
+
|
|
84
|
+
- **Missing null checks**: Accessing properties on potentially null objects
|
|
85
|
+
- **Truthy/falsy confusion**: `if (value)` when `0` or `""` are valid
|
|
86
|
+
- **Optional chaining overuse**: `a?.b?.c?.d` hiding structural issues
|
|
87
|
+
- **Null vs undefined inconsistency**: Mixed usage without clear convention
|
|
88
|
+
|
|
89
|
+
### Empty Collections
|
|
90
|
+
|
|
91
|
+
- **Empty array not handled**: Code assumes array has items
|
|
92
|
+
- **Empty object edge case**: `for...in` or `Object.keys` on empty object
|
|
93
|
+
- **First/last element access**: `arr[0]` or `arr[arr.length-1]` without length check
|
|
94
|
+
|
|
95
|
+
### Numeric Boundaries
|
|
96
|
+
|
|
97
|
+
- **Division by zero**: Missing check before division
|
|
98
|
+
- **Integer overflow**: Large numbers exceeding safe integer range
|
|
99
|
+
- **Floating point comparison**: Using `===` instead of epsilon comparison
|
|
100
|
+
- **Negative values**: Index or count that shouldn't be negative
|
|
101
|
+
- **Off-by-one errors**: Loop bounds, array slicing, pagination
|
|
102
|
+
|
|
103
|
+
### String Boundaries
|
|
104
|
+
|
|
105
|
+
- **Empty string**: Not handled as edge case
|
|
106
|
+
- **Whitespace-only string**: Passes truthy check but is effectively empty
|
|
107
|
+
- **Very long strings**: No length limits causing memory/display issues
|
|
108
|
+
- **Unicode edge cases**: Emoji, RTL text, combining characters
|
|
109
|
+
|
|
110
|
+
### Common Patterns to Flag
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// Dangerous: no null check
|
|
114
|
+
const name = user.profile.name
|
|
115
|
+
|
|
116
|
+
// Dangerous: array access without check
|
|
117
|
+
const first = items[0]
|
|
118
|
+
|
|
119
|
+
// Dangerous: division without check
|
|
120
|
+
const avg = total / count
|
|
121
|
+
|
|
122
|
+
// Dangerous: truthy check excludes valid values
|
|
123
|
+
if (value) { ... } // fails for 0, "", false
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Questions to Ask
|
|
127
|
+
- "What if this is null/undefined?"
|
|
128
|
+
- "What if this collection is empty?"
|
|
129
|
+
- "What's the valid range for this number?"
|
|
130
|
+
- "What happens at the boundaries (0, -1, MAX_INT)?"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Removal and Iteration Plan Template
|
|
2
|
+
|
|
3
|
+
## Priority Levels
|
|
4
|
+
|
|
5
|
+
- [ ] **P0**: Immediate removal needed (security risk, significant cost, blocking other work)
|
|
6
|
+
- [ ] **P1**: Remove in current sprint
|
|
7
|
+
- [ ] **P2**: Backlog / next iteration
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Safe to Remove Now
|
|
12
|
+
|
|
13
|
+
### Item: [Name/Description]
|
|
14
|
+
|
|
15
|
+
| Field | Details |
|
|
16
|
+
|-------|---------|
|
|
17
|
+
| **Location** | `path/to/file.ts:line` |
|
|
18
|
+
| **Rationale** | Why this should be removed |
|
|
19
|
+
| **Evidence** | Unused (no references), dead feature flag, deprecated API |
|
|
20
|
+
| **Impact** | None / Low - no active consumers |
|
|
21
|
+
| **Deletion steps** | 1. Remove code 2. Remove tests 3. Remove config |
|
|
22
|
+
| **Verification** | Run tests, check no runtime errors, monitor logs |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Defer Removal (Plan Required)
|
|
27
|
+
|
|
28
|
+
### Item: [Name/Description]
|
|
29
|
+
|
|
30
|
+
| Field | Details |
|
|
31
|
+
|-------|---------|
|
|
32
|
+
| **Location** | `path/to/file.ts:line` |
|
|
33
|
+
| **Why defer** | Active consumers, needs migration, stakeholder sign-off |
|
|
34
|
+
| **Preconditions** | Feature flag off for 2 weeks, telemetry shows 0 usage |
|
|
35
|
+
| **Breaking changes** | List any API/contract changes |
|
|
36
|
+
| **Migration plan** | Steps for consumers to migrate |
|
|
37
|
+
| **Timeline** | Target date or sprint |
|
|
38
|
+
| **Owner** | Person/team responsible |
|
|
39
|
+
| **Validation** | Metrics to confirm safe removal (error rates, usage counts) |
|
|
40
|
+
| **Rollback plan** | How to restore if issues found |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Checklist Before Removal
|
|
45
|
+
|
|
46
|
+
- [ ] Searched codebase for all references (`rg`, `grep`)
|
|
47
|
+
- [ ] Checked for dynamic/reflection-based usage
|
|
48
|
+
- [ ] Verified no external consumers (APIs, SDKs, docs)
|
|
49
|
+
- [ ] Feature flag telemetry reviewed (if applicable)
|
|
50
|
+
- [ ] Tests updated/removed
|
|
51
|
+
- [ ] Documentation updated
|
|
52
|
+
- [ ] Team notified (if shared code)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Security and Reliability Checklist
|
|
2
|
+
|
|
3
|
+
## Input/Output Safety
|
|
4
|
+
|
|
5
|
+
- **XSS**: Unsafe HTML injection, `dangerouslySetInnerHTML`, unescaped templates, innerHTML assignments
|
|
6
|
+
- **Injection**: SQL/NoSQL/command/GraphQL injection via string concatenation or template literals
|
|
7
|
+
- **SSRF**: User-controlled URLs reaching internal services without allowlist validation
|
|
8
|
+
- **Path traversal**: User input in file paths without sanitization (`../` attacks)
|
|
9
|
+
- **Prototype pollution**: Unsafe object merging in JavaScript (`Object.assign`, spread with user input)
|
|
10
|
+
|
|
11
|
+
## AuthN/AuthZ
|
|
12
|
+
|
|
13
|
+
- Missing tenant or ownership checks for read/write operations
|
|
14
|
+
- New endpoints without auth guards or RBAC enforcement
|
|
15
|
+
- Trusting client-provided roles/flags/IDs
|
|
16
|
+
- Broken access control (IDOR - Insecure Direct Object Reference)
|
|
17
|
+
- Session fixation or weak session management
|
|
18
|
+
|
|
19
|
+
## JWT & Token Security
|
|
20
|
+
|
|
21
|
+
- Algorithm confusion attacks (accepting `none` or `HS256` when expecting `RS256`)
|
|
22
|
+
- Weak or hardcoded secrets
|
|
23
|
+
- Missing expiration (`exp`) or not validating it
|
|
24
|
+
- Sensitive data in JWT payload (tokens are base64, not encrypted)
|
|
25
|
+
- Not validating `iss` (issuer) or `aud` (audience)
|
|
26
|
+
|
|
27
|
+
## Secrets and PII
|
|
28
|
+
|
|
29
|
+
- API keys, tokens, or credentials in code/config/logs
|
|
30
|
+
- Secrets in git history or environment variables exposed to client
|
|
31
|
+
- Excessive logging of PII or sensitive payloads
|
|
32
|
+
- Missing data masking in error messages
|
|
33
|
+
|
|
34
|
+
## Supply Chain & Dependencies
|
|
35
|
+
|
|
36
|
+
- Unpinned dependencies allowing malicious updates
|
|
37
|
+
- Dependency confusion (private package name collision)
|
|
38
|
+
- Importing from untrusted sources or CDNs without integrity checks
|
|
39
|
+
- Outdated dependencies with known CVEs
|
|
40
|
+
|
|
41
|
+
## CORS & Headers
|
|
42
|
+
|
|
43
|
+
- Overly permissive CORS (`Access-Control-Allow-Origin: *` with credentials)
|
|
44
|
+
- Missing security headers (CSP, X-Frame-Options, X-Content-Type-Options)
|
|
45
|
+
- Exposed internal headers or stack traces
|
|
46
|
+
|
|
47
|
+
## Runtime Risks
|
|
48
|
+
|
|
49
|
+
- Unbounded loops, recursive calls, or large in-memory buffers
|
|
50
|
+
- Missing timeouts, retries, or rate limiting on external calls
|
|
51
|
+
- Blocking operations on request path (sync I/O in async context)
|
|
52
|
+
- Resource exhaustion (file handles, connections, memory)
|
|
53
|
+
- ReDoS (Regular Expression Denial of Service)
|
|
54
|
+
|
|
55
|
+
## Cryptography
|
|
56
|
+
|
|
57
|
+
- Weak algorithms (MD5, SHA1 for security purposes)
|
|
58
|
+
- Hardcoded IVs or salts
|
|
59
|
+
- Using encryption without authentication (ECB mode, no HMAC)
|
|
60
|
+
- Insufficient key length
|
|
61
|
+
|
|
62
|
+
## Race Conditions
|
|
63
|
+
|
|
64
|
+
Race conditions are subtle bugs that cause intermittent failures and security vulnerabilities. Pay special attention to:
|
|
65
|
+
|
|
66
|
+
### Shared State Access
|
|
67
|
+
- Multiple threads/goroutines/async tasks accessing shared variables without synchronization
|
|
68
|
+
- Global state or singletons modified concurrently
|
|
69
|
+
- Lazy initialization without proper locking (double-checked locking issues)
|
|
70
|
+
- Non-thread-safe collections used in concurrent context
|
|
71
|
+
|
|
72
|
+
### Check-Then-Act (TOCTOU)
|
|
73
|
+
- `if (exists) then use` patterns without atomic operations
|
|
74
|
+
- `if (authorized) then perform` where authorization can change
|
|
75
|
+
- File existence check followed by file operation
|
|
76
|
+
- Balance check followed by deduction (financial operations)
|
|
77
|
+
- Inventory check followed by order placement
|
|
78
|
+
|
|
79
|
+
### Database Concurrency
|
|
80
|
+
- Missing optimistic locking (`version` column, `updated_at` checks)
|
|
81
|
+
- Missing pessimistic locking (`SELECT FOR UPDATE`)
|
|
82
|
+
- Read-modify-write without transaction isolation
|
|
83
|
+
- Counter increments without atomic operations (`UPDATE SET count = count + 1`)
|
|
84
|
+
- Unique constraint violations in concurrent inserts
|
|
85
|
+
|
|
86
|
+
### Distributed Systems
|
|
87
|
+
- Missing distributed locks for shared resources
|
|
88
|
+
- Leader election race conditions
|
|
89
|
+
- Cache invalidation races (stale reads after writes)
|
|
90
|
+
- Event ordering dependencies without proper sequencing
|
|
91
|
+
- Split-brain scenarios in cluster operations
|
|
92
|
+
|
|
93
|
+
### Common Patterns to Flag
|
|
94
|
+
```
|
|
95
|
+
# Dangerous patterns:
|
|
96
|
+
if not exists(key): # TOCTOU
|
|
97
|
+
create(key)
|
|
98
|
+
|
|
99
|
+
value = get(key) # Read-modify-write
|
|
100
|
+
value += 1
|
|
101
|
+
set(key, value)
|
|
102
|
+
|
|
103
|
+
if user.balance >= amount: # Check-then-act
|
|
104
|
+
user.balance -= amount
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Questions to Ask
|
|
108
|
+
- "What happens if two requests hit this code simultaneously?"
|
|
109
|
+
- "Is this operation atomic or can it be interrupted?"
|
|
110
|
+
- "What shared state does this code access?"
|
|
111
|
+
- "How does this behave under high concurrency?"
|
|
112
|
+
|
|
113
|
+
## Data Integrity
|
|
114
|
+
|
|
115
|
+
- Missing transactions, partial writes, or inconsistent state updates
|
|
116
|
+
- Weak validation before persistence (type coercion issues)
|
|
117
|
+
- Missing idempotency for retryable operations
|
|
118
|
+
- Lost updates due to concurrent modifications
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# SOLID Smell Prompts
|
|
2
|
+
|
|
3
|
+
## SRP (Single Responsibility)
|
|
4
|
+
|
|
5
|
+
- File owns unrelated concerns (e.g., HTTP + DB + domain rules in one file)
|
|
6
|
+
- Large class/module with low cohesion or multiple reasons to change
|
|
7
|
+
- Functions that orchestrate many unrelated steps
|
|
8
|
+
- God objects that know too much about the system
|
|
9
|
+
- **Ask**: "What is the single reason this module would change?"
|
|
10
|
+
|
|
11
|
+
## OCP (Open/Closed)
|
|
12
|
+
|
|
13
|
+
- Adding a new behavior requires editing many switch/if blocks
|
|
14
|
+
- Feature growth requires modifying core logic rather than extending
|
|
15
|
+
- No plugin/strategy/hook points for variation
|
|
16
|
+
- **Ask**: "Can I add a new variant without touching existing code?"
|
|
17
|
+
|
|
18
|
+
## LSP (Liskov Substitution)
|
|
19
|
+
|
|
20
|
+
- Subclass checks for concrete type or throws for base method
|
|
21
|
+
- Overridden methods weaken preconditions or strengthen postconditions
|
|
22
|
+
- Subclass ignores or no-ops parent behavior
|
|
23
|
+
- **Ask**: "Can I substitute any subclass without the caller knowing?"
|
|
24
|
+
|
|
25
|
+
## ISP (Interface Segregation)
|
|
26
|
+
|
|
27
|
+
- Interfaces with many methods, most unused by implementers
|
|
28
|
+
- Callers depend on broad interfaces for narrow needs
|
|
29
|
+
- Empty/stub implementations of interface methods
|
|
30
|
+
- **Ask**: "Do all implementers use all methods?"
|
|
31
|
+
|
|
32
|
+
## DIP (Dependency Inversion)
|
|
33
|
+
|
|
34
|
+
- High-level logic depends on concrete IO, storage, or network types
|
|
35
|
+
- Hard-coded implementations instead of abstractions or injection
|
|
36
|
+
- Import chains that couple business logic to infrastructure
|
|
37
|
+
- **Ask**: "Can I swap the implementation without changing business logic?"
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Common Code Smells (Beyond SOLID)
|
|
42
|
+
|
|
43
|
+
| Smell | Signs |
|
|
44
|
+
|-------|-------|
|
|
45
|
+
| **Long method** | Function > 30 lines, multiple levels of nesting |
|
|
46
|
+
| **Feature envy** | Method uses more data from another class than its own |
|
|
47
|
+
| **Data clumps** | Same group of parameters passed together repeatedly |
|
|
48
|
+
| **Primitive obsession** | Using strings/numbers instead of domain types |
|
|
49
|
+
| **Shotgun surgery** | One change requires edits across many files |
|
|
50
|
+
| **Divergent change** | One file changes for many unrelated reasons |
|
|
51
|
+
| **Dead code** | Unreachable or never-called code |
|
|
52
|
+
| **Speculative generality** | Abstractions for hypothetical future needs |
|
|
53
|
+
| **Magic numbers/strings** | Hardcoded values without named constants |
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Refactor Heuristics
|
|
58
|
+
|
|
59
|
+
1. **Split by responsibility, not by size** - A small file can still violate SRP
|
|
60
|
+
2. **Introduce abstraction only when needed** - Wait for the second use case
|
|
61
|
+
3. **Keep refactors incremental** - Isolate behavior before moving
|
|
62
|
+
4. **Preserve behavior first** - Add tests before restructuring
|
|
63
|
+
5. **Name things by intent** - If naming is hard, the abstraction might be wrong
|
|
64
|
+
6. **Prefer composition over inheritance** - Inheritance creates tight coupling
|
|
65
|
+
7. **Make illegal states unrepresentable** - Use types to enforce invariants
|
package/index-pc.html
CHANGED
|
@@ -410,6 +410,25 @@
|
|
|
410
410
|
animation: loading-dots 1.2s steps(4, end) infinite;
|
|
411
411
|
}
|
|
412
412
|
#init-overlay.visible { display: flex; }
|
|
413
|
+
#reconnect-overlay {
|
|
414
|
+
display: none;
|
|
415
|
+
position: absolute;
|
|
416
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
417
|
+
background: rgba(10, 10, 10, 0.85);
|
|
418
|
+
color: #f59e0b;
|
|
419
|
+
align-items: center;
|
|
420
|
+
justify-content: center;
|
|
421
|
+
font-size: 16px;
|
|
422
|
+
font-weight: 600;
|
|
423
|
+
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
424
|
+
letter-spacing: 1px;
|
|
425
|
+
z-index: 10;
|
|
426
|
+
}
|
|
427
|
+
#reconnect-overlay::after {
|
|
428
|
+
content: '';
|
|
429
|
+
animation: loading-dots 1.2s steps(4, end) infinite;
|
|
430
|
+
}
|
|
431
|
+
#reconnect-overlay.visible { display: flex; }
|
|
413
432
|
|
|
414
433
|
|
|
415
434
|
/* 选择模式:原位文本层 */
|
|
@@ -1038,6 +1057,7 @@
|
|
|
1038
1057
|
<div id="terminal" style="position:relative;">
|
|
1039
1058
|
<div id="switch-overlay">正在切换</div>
|
|
1040
1059
|
<div id="init-overlay"></div>
|
|
1060
|
+
<div id="reconnect-overlay">连接断开,正在重连</div>
|
|
1041
1061
|
<div id="select-text-layer">
|
|
1042
1062
|
<div id="select-hint">长按选择文本 · 点右上角 ✕ 返回终端</div>
|
|
1043
1063
|
<pre id="select-text-pre"></pre>
|
|
@@ -1059,6 +1079,9 @@
|
|
|
1059
1079
|
var fontSize = isMobile ? 11 : 13;
|
|
1060
1080
|
var currentMode = 'claude';
|
|
1061
1081
|
var isTransitioning = false;
|
|
1082
|
+
var transitionEndTimer = null;
|
|
1083
|
+
var waitingInitData = false;
|
|
1084
|
+
var initDataTimer = null;
|
|
1062
1085
|
var isBufferReplay = true; // 初始缓冲区回放中,不弹 toast
|
|
1063
1086
|
var startupDialogShown = false;
|
|
1064
1087
|
|
|
@@ -1640,16 +1663,16 @@
|
|
|
1640
1663
|
|
|
1641
1664
|
ws.onopen = function() {
|
|
1642
1665
|
isBufferReplay = true;
|
|
1666
|
+
document.getElementById('reconnect-overlay').classList.remove('visible');
|
|
1643
1667
|
resize();
|
|
1644
1668
|
rebindTouchScroll();
|
|
1645
|
-
setTimeout(function() { isBufferReplay = false; },
|
|
1669
|
+
setTimeout(function() { isBufferReplay = false; }, 2000);
|
|
1646
1670
|
// 不在这里初始化,等 state 消息判断是否需要弹对话框
|
|
1647
1671
|
};
|
|
1648
1672
|
|
|
1649
1673
|
ws.onclose = function() {
|
|
1650
1674
|
ws = null;
|
|
1651
|
-
|
|
1652
|
-
term.write('\r\n \x1b[33m连接断开,正在重连...\x1b[0m\r\n');
|
|
1675
|
+
document.getElementById('reconnect-overlay').classList.add('visible');
|
|
1653
1676
|
setTimeout(connect, 2000);
|
|
1654
1677
|
};
|
|
1655
1678
|
|
|
@@ -1657,7 +1680,23 @@
|
|
|
1657
1680
|
try {
|
|
1658
1681
|
var msg = JSON.parse(e.data);
|
|
1659
1682
|
if (msg.type === 'data') {
|
|
1660
|
-
if (
|
|
1683
|
+
if (isTransitioning) {
|
|
1684
|
+
// 模式切换:TUI 渲染会发送一连串 data,debounce 等稳定后移除覆盖层
|
|
1685
|
+
term.write(msg.data);
|
|
1686
|
+
clearTimeout(transitionEndTimer);
|
|
1687
|
+
transitionEndTimer = setTimeout(function() {
|
|
1688
|
+
terminalEl.classList.remove('transitioning');
|
|
1689
|
+
isTransitioning = false;
|
|
1690
|
+
}, 2000);
|
|
1691
|
+
} else if (waitingInitData) {
|
|
1692
|
+
// 启动/恢复:同样 debounce 等 TUI 渲染完再隐藏启动覆盖层
|
|
1693
|
+
throttledWrite(msg.data);
|
|
1694
|
+
clearTimeout(initDataTimer);
|
|
1695
|
+
initDataTimer = setTimeout(function() {
|
|
1696
|
+
hideInitOverlay();
|
|
1697
|
+
waitingInitData = false;
|
|
1698
|
+
}, 2000);
|
|
1699
|
+
} else if (!isCreatingNewSession) {
|
|
1661
1700
|
throttledWrite(msg.data);
|
|
1662
1701
|
}
|
|
1663
1702
|
}
|
|
@@ -1671,7 +1710,9 @@
|
|
|
1671
1710
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1672
1711
|
writeBuffer = '';
|
|
1673
1712
|
term.reset();
|
|
1674
|
-
|
|
1713
|
+
// 更新模式但保留覆盖层,等首条 data 到达后再移除
|
|
1714
|
+
currentMode = msg.mode;
|
|
1715
|
+
modeSelect.value = msg.mode;
|
|
1675
1716
|
if (msg.buffer) {
|
|
1676
1717
|
term.write(msg.buffer);
|
|
1677
1718
|
}
|
|
@@ -1698,7 +1739,8 @@
|
|
|
1698
1739
|
}
|
|
1699
1740
|
}
|
|
1700
1741
|
else if (msg.type === 'restored') {
|
|
1701
|
-
|
|
1742
|
+
// 不立即隐藏覆盖层,等 data debounce 后隐藏
|
|
1743
|
+
waitingInitData = true;
|
|
1702
1744
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1703
1745
|
writeBuffer = '';
|
|
1704
1746
|
term.reset();
|
|
@@ -1711,12 +1753,13 @@
|
|
|
1711
1753
|
term.write('恢复失败: ' + msg.error + '\r\n');
|
|
1712
1754
|
}
|
|
1713
1755
|
else if (msg.type === 'started') {
|
|
1714
|
-
|
|
1756
|
+
// 不立即隐藏覆盖层,等 data debounce 后隐藏
|
|
1757
|
+
waitingInitData = true;
|
|
1715
1758
|
rebindTouchScroll();
|
|
1716
1759
|
preloadData();
|
|
1717
1760
|
}
|
|
1718
1761
|
else if (msg.type === 'new-session-ok') {
|
|
1719
|
-
|
|
1762
|
+
waitingInitData = true;
|
|
1720
1763
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1721
1764
|
writeBuffer = '';
|
|
1722
1765
|
term.reset();
|
|
@@ -1739,7 +1782,7 @@
|
|
|
1739
1782
|
} else {
|
|
1740
1783
|
cacheRestored = true;
|
|
1741
1784
|
}
|
|
1742
|
-
},
|
|
1785
|
+
}, 2000);
|
|
1743
1786
|
}
|
|
1744
1787
|
|
|
1745
1788
|
window.addEventListener('resize', resize);
|
|
@@ -1908,7 +1951,7 @@
|
|
|
1908
1951
|
setTimeout(function() {
|
|
1909
1952
|
sessions = sessions.filter(function(s) { return s.id !== sessionId; });
|
|
1910
1953
|
renderSessions();
|
|
1911
|
-
},
|
|
1954
|
+
}, 2000);
|
|
1912
1955
|
} else {
|
|
1913
1956
|
itemEl.style.opacity = '';
|
|
1914
1957
|
itemEl.style.pointerEvents = '';
|
|
@@ -2108,7 +2151,7 @@
|
|
|
2108
2151
|
itemEl.style.padding = '0 12px';
|
|
2109
2152
|
itemEl.style.margin = '0';
|
|
2110
2153
|
itemEl.style.opacity = '0';
|
|
2111
|
-
setTimeout(function() { itemEl.remove(); },
|
|
2154
|
+
setTimeout(function() { itemEl.remove(); }, 2000);
|
|
2112
2155
|
} else {
|
|
2113
2156
|
itemEl.style.opacity = '';
|
|
2114
2157
|
itemEl.style.pointerEvents = '';
|
package/index.html
CHANGED
|
@@ -1120,6 +1120,7 @@
|
|
|
1120
1120
|
var fontSize = isMobile ? 11 : 13;
|
|
1121
1121
|
var currentMode = 'claude';
|
|
1122
1122
|
var isTransitioning = false;
|
|
1123
|
+
var transitionEndTimer = null;
|
|
1123
1124
|
var mobileInitSent = false;
|
|
1124
1125
|
|
|
1125
1126
|
var term = new Terminal({
|
|
@@ -1681,7 +1682,14 @@
|
|
|
1681
1682
|
var msg = JSON.parse(e.data);
|
|
1682
1683
|
if (msg.type === 'data') {
|
|
1683
1684
|
hideLoading();
|
|
1684
|
-
if (
|
|
1685
|
+
if (isTransitioning) {
|
|
1686
|
+
term.write(msg.data);
|
|
1687
|
+
clearTimeout(transitionEndTimer);
|
|
1688
|
+
transitionEndTimer = setTimeout(function() {
|
|
1689
|
+
terminalEl.classList.remove('transitioning');
|
|
1690
|
+
isTransitioning = false;
|
|
1691
|
+
}, 2000);
|
|
1692
|
+
} else if (!isCreatingNewSession) {
|
|
1685
1693
|
throttledWrite(msg.data);
|
|
1686
1694
|
}
|
|
1687
1695
|
}
|
|
@@ -1737,7 +1745,8 @@
|
|
|
1737
1745
|
if (writeTimer) { cancelAnimationFrame(writeTimer); writeTimer = null; }
|
|
1738
1746
|
writeBuffer = '';
|
|
1739
1747
|
term.reset();
|
|
1740
|
-
|
|
1748
|
+
currentMode = msg.mode;
|
|
1749
|
+
modeSelect.value = msg.mode;
|
|
1741
1750
|
if (msg.buffer) {
|
|
1742
1751
|
term.write(msg.buffer);
|
|
1743
1752
|
}
|
|
@@ -1792,7 +1801,7 @@
|
|
|
1792
1801
|
} else {
|
|
1793
1802
|
cacheRestored = true;
|
|
1794
1803
|
}
|
|
1795
|
-
},
|
|
1804
|
+
}, 2000);
|
|
1796
1805
|
}
|
|
1797
1806
|
|
|
1798
1807
|
window.addEventListener('resize', resize);
|
|
@@ -2031,7 +2040,7 @@
|
|
|
2031
2040
|
setTimeout(function() {
|
|
2032
2041
|
sessions = sessions.filter(function(s) { return s.id !== sessionId; });
|
|
2033
2042
|
renderSessions();
|
|
2034
|
-
},
|
|
2043
|
+
}, 2000);
|
|
2035
2044
|
} else {
|
|
2036
2045
|
itemEl.style.opacity = '';
|
|
2037
2046
|
itemEl.style.pointerEvents = '';
|
|
@@ -2234,7 +2243,7 @@
|
|
|
2234
2243
|
itemEl.style.padding = '0 12px';
|
|
2235
2244
|
itemEl.style.margin = '0';
|
|
2236
2245
|
itemEl.style.opacity = '0';
|
|
2237
|
-
setTimeout(function() { itemEl.remove(); },
|
|
2246
|
+
setTimeout(function() { itemEl.remove(); }, 2000);
|
|
2238
2247
|
} else {
|
|
2239
2248
|
itemEl.style.opacity = '';
|
|
2240
2249
|
itemEl.style.pointerEvents = '';
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -201,6 +201,7 @@ function killProcessTree(proc) {
|
|
|
201
201
|
|
|
202
202
|
// 清理孤儿进程(PPID=1 的 opencode/claude)
|
|
203
203
|
function cleanupOrphanProcesses() {
|
|
204
|
+
const tc = Date.now();
|
|
204
205
|
try {
|
|
205
206
|
const myPid = process.pid;
|
|
206
207
|
const orphans = execSync(
|
|
@@ -212,15 +213,23 @@ function cleanupOrphanProcesses() {
|
|
|
212
213
|
}
|
|
213
214
|
if (orphans.length > 0) LOG(`[cleanup] 清理孤儿进程: ${orphans.join(', ')}`);
|
|
214
215
|
} catch {}
|
|
216
|
+
console.log(`[perf] cleanupOrphanProcesses: ${Date.now() - tc}ms`);
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
async function spawnProcess(mode, sessionId = null) {
|
|
220
|
+
const t0 = Date.now();
|
|
218
221
|
const pty = await getPty();
|
|
222
|
+
console.log(`[perf] getPty: ${Date.now() - t0}ms`);
|
|
223
|
+
|
|
224
|
+
const t1 = Date.now();
|
|
219
225
|
fixSpawnHelperPermissions();
|
|
226
|
+
console.log(`[perf] fixSpawnHelperPermissions: ${Date.now() - t1}ms`);
|
|
220
227
|
|
|
221
228
|
let command, args = [];
|
|
222
229
|
if (mode === 'claude') {
|
|
230
|
+
const t2 = Date.now();
|
|
223
231
|
const claudePath = findCommand('claude');
|
|
232
|
+
console.log(`[perf] findCommand(claude): ${Date.now() - t2}ms`);
|
|
224
233
|
if (claudePath.endsWith('.js')) {
|
|
225
234
|
command = process.execPath;
|
|
226
235
|
args = [claudePath];
|
|
@@ -233,7 +242,9 @@ async function spawnProcess(mode, sessionId = null) {
|
|
|
233
242
|
LOG(`[claude] 恢复会话: ${sessionId}`);
|
|
234
243
|
}
|
|
235
244
|
} else {
|
|
245
|
+
const t2 = Date.now();
|
|
236
246
|
command = findCommand('opencode');
|
|
247
|
+
console.log(`[perf] findCommand(opencode): ${Date.now() - t2}ms`);
|
|
237
248
|
// 如果提供了 sessionId,添加 --session 参数
|
|
238
249
|
if (sessionId) {
|
|
239
250
|
args = ['--session', sessionId];
|
|
@@ -244,6 +255,7 @@ async function spawnProcess(mode, sessionId = null) {
|
|
|
244
255
|
const spawnEnv = { ...process.env };
|
|
245
256
|
|
|
246
257
|
// 恢复会话时,使用会话记录的工作目录
|
|
258
|
+
const t3 = Date.now();
|
|
247
259
|
let spawnCwd = process.cwd();
|
|
248
260
|
if (sessionId && mode === 'opencode') {
|
|
249
261
|
try {
|
|
@@ -286,6 +298,9 @@ async function spawnProcess(mode, sessionId = null) {
|
|
|
286
298
|
}
|
|
287
299
|
}
|
|
288
300
|
|
|
301
|
+
console.log(`[perf] cwdLookup: ${Date.now() - t3}ms`);
|
|
302
|
+
|
|
303
|
+
const t4 = Date.now();
|
|
289
304
|
const proc = pty.spawn(command, args, {
|
|
290
305
|
name: 'xterm-256color',
|
|
291
306
|
cols: lastPtyCols,
|
|
@@ -294,6 +309,9 @@ async function spawnProcess(mode, sessionId = null) {
|
|
|
294
309
|
env: spawnEnv,
|
|
295
310
|
});
|
|
296
311
|
|
|
312
|
+
console.log(`[perf] pty.spawn: ${Date.now() - t4}ms`);
|
|
313
|
+
console.log(`[perf] spawnProcess total: ${Date.now() - t0}ms`);
|
|
314
|
+
|
|
297
315
|
proc.onData((data) => {
|
|
298
316
|
// 忽略已被替换的旧进程输出
|
|
299
317
|
if (currentProcess !== proc) return;
|
|
@@ -786,6 +804,7 @@ const requestHandler = async (req, res) => {
|
|
|
786
804
|
|
|
787
805
|
// API: 获取最近的 OpenCode 和 Claude 会话(用于启动对话框)
|
|
788
806
|
if (req.url === '/api/last-sessions') {
|
|
807
|
+
const tls = Date.now();
|
|
789
808
|
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
790
809
|
let opencode = null, claude = null;
|
|
791
810
|
// OpenCode: 从 SQLite 查最近会话
|
|
@@ -861,6 +880,7 @@ const requestHandler = async (req, res) => {
|
|
|
861
880
|
}
|
|
862
881
|
}
|
|
863
882
|
} catch {}
|
|
883
|
+
console.log(`[perf] last-sessions: ${Date.now() - tls}ms`);
|
|
864
884
|
res.end(JSON.stringify({ opencode, claude }));
|
|
865
885
|
return;
|
|
866
886
|
}
|