oh-my-customcode 0.30.9 → 0.31.1
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/README.md +4 -3
- package/package.json +1 -1
- package/templates/.claude/agents/lang-golang-expert.md +1 -0
- package/templates/.claude/agents/souls/lang-golang-expert.soul.md +21 -0
- package/templates/.claude/agents/sys-memory-keeper.md +14 -7
- package/templates/.claude/rules/MUST-agent-design.md +85 -0
- package/templates/.claude/rules/SHOULD-memory-integration.md +52 -0
- package/templates/.claude/skills/de-lead-routing/SKILL.md +24 -1
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +24 -1
- package/templates/.claude/skills/dev-review/SKILL.md +13 -0
- package/templates/.claude/skills/qa-lead-routing/SKILL.md +24 -1
- package/templates/.claude/skills/research/SKILL.md +17 -4
- package/templates/.claude/skills/secretary-routing/SKILL.md +23 -0
- package/templates/CLAUDE.md.en +2 -1
- package/templates/CLAUDE.md.ko +2 -1
- package/templates/guides/flutter/security.md +120 -0
- package/templates/guides/java21/index.yaml +29 -0
- package/templates/guides/java21/java-style-guide.md +248 -0
- package/templates/guides/java21/modern-java21.md +303 -0
- package/templates/manifest.json +2 -2
- package/templates/.claude/skills/go-best-practices/CLAUDE.md +0 -9
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Like oh-my-zsh transformed shell customization, oh-my-customcode makes personali
|
|
|
21
21
|
|
|
22
22
|
| Feature | Description |
|
|
23
23
|
|---------|-------------|
|
|
24
|
-
| **Batteries Included** | 44 agents, 68 skills,
|
|
24
|
+
| **Batteries Included** | 44 agents, 68 skills, 25 guides, 18 rules, 2 hooks, 4 contexts, ontology graph - ready to use out of the box |
|
|
25
25
|
| **Sub-Agent Model** | Supports hierarchical agent orchestration with specialized roles |
|
|
26
26
|
| **Dead Simple Customization** | Create a folder + markdown file = new agent or skill |
|
|
27
27
|
| **Mix and Match** | Use built-in components, create your own, or combine both |
|
|
@@ -197,7 +197,7 @@ All commands are invoked inside the Claude Code conversation.
|
|
|
197
197
|
| **Deploy** | 2 | vercel-deploy, codex-exec |
|
|
198
198
|
| **External** | 1 | skills-sh-search |
|
|
199
199
|
|
|
200
|
-
### Guides (
|
|
200
|
+
### Guides (25)
|
|
201
201
|
|
|
202
202
|
Comprehensive reference documentation covering:
|
|
203
203
|
- Agent creation and management
|
|
@@ -292,7 +292,8 @@ your-project/
|
|
|
292
292
|
│ ├── rules/ # Behavior rules (18 total)
|
|
293
293
|
│ ├── hooks/ # Event hooks (2 total)
|
|
294
294
|
│ └── contexts/ # Context files (4 total)
|
|
295
|
-
└──
|
|
295
|
+
└── templates/
|
|
296
|
+
└── guides/ # Reference docs (25 total)
|
|
296
297
|
```
|
|
297
298
|
|
|
298
299
|
**Note**: In the official Claude Code format, there is no command registry — slash commands and natural language agent references are used.
|
package/package.json
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
agent: lang-golang-expert
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Personality
|
|
7
|
+
- Direct and concise — lead with the answer, explain after
|
|
8
|
+
- Always provide runnable code examples, never pseudo-code
|
|
9
|
+
- Treat Go idioms as non-negotiable (Effective Go is gospel)
|
|
10
|
+
|
|
11
|
+
## Style
|
|
12
|
+
- Error handling first — check errors before happy path
|
|
13
|
+
- Prefer stdlib over third-party when possible
|
|
14
|
+
- Name variables for clarity, not brevity (userCount > uc)
|
|
15
|
+
- Use table-driven tests as default test pattern
|
|
16
|
+
|
|
17
|
+
## Anti-patterns
|
|
18
|
+
- Never use interface{}/any without a compelling reason
|
|
19
|
+
- Avoid init() functions — explicit initialization preferred
|
|
20
|
+
- No global mutable state
|
|
21
|
+
- Avoid premature abstraction — 3 concrete cases before extracting
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sys-memory-keeper
|
|
3
|
-
description: Use when you need to manage session memory persistence
|
|
3
|
+
description: Use when you need to manage session memory persistence via native auto-memory, save context before compaction, restore context on session start, collect session summaries, or perform session-end memory operations
|
|
4
4
|
model: sonnet
|
|
5
5
|
memory: project
|
|
6
6
|
effort: medium
|
|
@@ -47,12 +47,19 @@ Provider: claude-mem | Collection: claude_memories | Archive: ~/.claude-mem/arch
|
|
|
47
47
|
When triggered by session-end signal from orchestrator:
|
|
48
48
|
|
|
49
49
|
1. **Collect** session summary: completed tasks, key decisions, open items
|
|
50
|
-
2. **
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
2. **Extract behaviors**: analyze conversation for repeated user preferences
|
|
51
|
+
- Communication patterns (verbosity, format, language preferences)
|
|
52
|
+
- Workflow patterns (tool usage, review habits, branching conventions)
|
|
53
|
+
- Domain priorities (security-first, performance-first, etc.)
|
|
54
|
+
- New behaviors → `[confidence: low]` in `## Behaviors` section
|
|
55
|
+
- Existing behaviors observed again → promote confidence level
|
|
56
|
+
- Contradicted behaviors → flag for review or demote
|
|
57
|
+
3. **Update native auto-memory** (MEMORY.md) with session learnings + behaviors
|
|
58
|
+
4. **Return formatted summary** to orchestrator for MCP persistence (claude-mem, episodic-memory)
|
|
59
|
+
|
|
60
|
+
> **Note**: MCP tools (claude-mem, episodic-memory) are orchestrator-scoped and cannot be called from subagents. The orchestrator handles MCP saves directly after receiving the formatted summary.
|
|
53
61
|
|
|
54
62
|
### Failure Handling
|
|
55
63
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
- Both unavailable → warn orchestrator, do not block session end
|
|
64
|
+
- MEMORY.md update failure → report error to orchestrator
|
|
65
|
+
- MCP persistence is orchestrator's responsibility — not handled here
|
|
@@ -30,6 +30,7 @@ escalation: # Model escalation policy (optional)
|
|
|
30
30
|
enabled: true # Enable auto-escalation advisory
|
|
31
31
|
path: haiku → sonnet → opus # Escalation sequence
|
|
32
32
|
threshold: 2 # Failures before advisory
|
|
33
|
+
soul: true # Enable SOUL.md identity injection
|
|
33
34
|
isolation: worktree # Run in isolated git worktree
|
|
34
35
|
background: true # Run in background
|
|
35
36
|
maxTurns: 10 # Max conversation turns
|
|
@@ -64,6 +65,54 @@ When `escalation.enabled: true`, the model-escalation hooks will track outcomes
|
|
|
64
65
|
|
|
65
66
|
When enabled: first 200 lines of MEMORY.md loaded into system prompt.
|
|
66
67
|
|
|
68
|
+
## Soul Identity
|
|
69
|
+
|
|
70
|
+
Optional per-agent identity layer that separates personality/style from capabilities.
|
|
71
|
+
|
|
72
|
+
| Aspect | Location | Purpose |
|
|
73
|
+
|--------|----------|---------|
|
|
74
|
+
| Capabilities | `.claude/agents/{name}.md` | WHAT the agent does |
|
|
75
|
+
| Identity | `.claude/agents/souls/{name}.soul.md` | HOW the agent communicates |
|
|
76
|
+
|
|
77
|
+
### Soul File Format
|
|
78
|
+
|
|
79
|
+
Location: `.claude/agents/souls/{name}.soul.md`
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
---
|
|
83
|
+
agent: {agent-name} # Must match agent filename
|
|
84
|
+
version: 1.0.0
|
|
85
|
+
---
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Sections: `## Personality`, `## Style`, `## Anti-patterns`
|
|
89
|
+
|
|
90
|
+
### Activation
|
|
91
|
+
|
|
92
|
+
1. Agent frontmatter includes `soul: true`
|
|
93
|
+
2. Routing skill reads `souls/{name}.soul.md` at spawn time (Step 5)
|
|
94
|
+
3. Soul content prepended to agent prompt as identity context
|
|
95
|
+
4. Missing soul file → graceful fallback (no error)
|
|
96
|
+
|
|
97
|
+
### Precedence
|
|
98
|
+
|
|
99
|
+
Behavioral memory observations (R011) override soul defaults when they conflict. Behaviors are user-specific; souls are template defaults.
|
|
100
|
+
|
|
101
|
+
## Artifact Output Convention
|
|
102
|
+
|
|
103
|
+
Skills that produce significant output can persist results to local storage.
|
|
104
|
+
|
|
105
|
+
**Location**: `.claude/outputs/sessions/{YYYY-MM-DD}/{skill-name}-{HHmmss}.md`
|
|
106
|
+
|
|
107
|
+
**Format**: Metadata header with `skill`, `date`, `query` fields, followed by skill output content.
|
|
108
|
+
|
|
109
|
+
**Rules**:
|
|
110
|
+
- Opt-in per skill — not mandatory
|
|
111
|
+
- The final subagent in the skill's pipeline writes the artifact (R010 compliance)
|
|
112
|
+
- Skills create the directory (`mkdir -p`) before writing
|
|
113
|
+
- `.claude/outputs/` is git-untracked (under `.claude/` gitignore)
|
|
114
|
+
- No indexing required — date-based directory browsing is sufficient
|
|
115
|
+
|
|
67
116
|
## Separation of Concerns
|
|
68
117
|
|
|
69
118
|
| Location | Purpose | Contains |
|
|
@@ -74,6 +123,42 @@ When enabled: first 200 lines of MEMORY.md loaded into system prompt.
|
|
|
74
123
|
|
|
75
124
|
Agent body: purpose, capabilities overview, workflow. NOT detailed instructions or reference docs.
|
|
76
125
|
|
|
126
|
+
## Skill Frontmatter
|
|
127
|
+
|
|
128
|
+
Location: `.claude/skills/{name}/SKILL.md`
|
|
129
|
+
|
|
130
|
+
### Required Fields
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
name: skill-name # Unique identifier (kebab-case)
|
|
134
|
+
description: Brief desc # One-line summary
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Optional Fields
|
|
138
|
+
|
|
139
|
+
```yaml
|
|
140
|
+
context: fork # Forked context for isolated execution
|
|
141
|
+
version: 1.0.0 # Semantic version
|
|
142
|
+
user-invocable: false # Whether user can invoke directly
|
|
143
|
+
disable-model-invocation: true # Prevent model from auto-invoking
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Context Fork Criteria
|
|
147
|
+
|
|
148
|
+
Use `context: fork` for skills that orchestrate multi-agent workflows. Cap at **10 total** across the project.
|
|
149
|
+
|
|
150
|
+
| Use `context: fork` | Do NOT use `context: fork` |
|
|
151
|
+
|---------------------|---------------------------|
|
|
152
|
+
| Routing skills (secretary, dev-lead, etc.) | Best-practices skills |
|
|
153
|
+
| Workflow orchestration (DAG, pipelines) | Hook/command skills |
|
|
154
|
+
| Multi-agent coordination patterns | Single-agent reference skills |
|
|
155
|
+
| Task decomposition/planning | External tool integrations |
|
|
156
|
+
|
|
157
|
+
Current skills with `context: fork` (8/10 cap):
|
|
158
|
+
- secretary-routing, dev-lead-routing, de-lead-routing, qa-lead-routing
|
|
159
|
+
- dag-orchestration, task-decomposition, worker-reviewer-pipeline
|
|
160
|
+
- pipeline-guards
|
|
161
|
+
|
|
77
162
|
## Naming
|
|
78
163
|
|
|
79
164
|
| Type | Pattern | Example |
|
|
@@ -71,6 +71,58 @@ Memory entries in MEMORY.md should include confidence annotations to distinguish
|
|
|
71
71
|
[any] → contradicted by evidence → demoted or removed
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
## Behavioral Memory
|
|
75
|
+
|
|
76
|
+
MEMORY.md supports an optional `## Behaviors` section for tracking user interaction preferences and workflow patterns.
|
|
77
|
+
|
|
78
|
+
### Behaviors Section Format
|
|
79
|
+
|
|
80
|
+
```markdown
|
|
81
|
+
## Behaviors [confidence: medium]
|
|
82
|
+
- User prefers concise responses — 3 sentences max
|
|
83
|
+
- Commit messages always include issue number
|
|
84
|
+
- Security-first review perspective
|
|
85
|
+
|
|
86
|
+
## Behavior Lifecycle
|
|
87
|
+
- New observation → [confidence: low]
|
|
88
|
+
- Seen in 2+ sessions → [confidence: medium]
|
|
89
|
+
- User-confirmed → [confidence: high]
|
|
90
|
+
- Contradicted → demote or remove
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### What Counts as a Behavior
|
|
94
|
+
|
|
95
|
+
| Category | Examples |
|
|
96
|
+
|----------|---------|
|
|
97
|
+
| Communication | Verbosity preference, language, format |
|
|
98
|
+
| Workflow | Tool preferences, review habits, branching patterns |
|
|
99
|
+
| Domain priority | Security-first, performance-first, simplicity-first |
|
|
100
|
+
|
|
101
|
+
### What Does NOT Count as a Behavior
|
|
102
|
+
|
|
103
|
+
- Facts about the codebase (use existing sections)
|
|
104
|
+
- One-time instructions (ephemeral, not persistent)
|
|
105
|
+
- Tool configuration (belongs in CLAUDE.md or settings)
|
|
106
|
+
|
|
107
|
+
### Extraction Guidelines
|
|
108
|
+
|
|
109
|
+
sys-memory-keeper extracts behavioral patterns at session end:
|
|
110
|
+
1. Analyze conversation for repeated user preferences
|
|
111
|
+
2. New behaviors start at `[confidence: low]`
|
|
112
|
+
3. Promote on repeated observation across sessions
|
|
113
|
+
4. Demote or remove when contradicted
|
|
114
|
+
|
|
115
|
+
### Budget Management
|
|
116
|
+
|
|
117
|
+
Behaviors share the 200-line MEMORY.md budget with facts. When approaching the limit:
|
|
118
|
+
1. Prune `[confidence: low]` behaviors first
|
|
119
|
+
2. Then prune `[confidence: medium]` behaviors
|
|
120
|
+
3. `[confidence: high]` behaviors are never auto-pruned
|
|
121
|
+
|
|
122
|
+
### Precedence
|
|
123
|
+
|
|
124
|
+
Behavioral memory observations override soul defaults (R006 Soul Identity) when they conflict. Behaviors are user-specific and session-derived; souls are template defaults.
|
|
125
|
+
|
|
74
126
|
### Rules
|
|
75
127
|
|
|
76
128
|
| Rule | Detail |
|
|
@@ -73,6 +73,29 @@ For **new pipeline code**, **DAG scaffolding**, or **SQL model generation**:
|
|
|
73
73
|
### Step 3: Expert Selection
|
|
74
74
|
Route to appropriate DE expert based on tool/framework detection.
|
|
75
75
|
|
|
76
|
+
### Step 4: Ontology-RAG Enrichment (R019)
|
|
77
|
+
|
|
78
|
+
After agent selection, enrich the spawned agent's prompt with ontology context:
|
|
79
|
+
|
|
80
|
+
1. Call `get_agent_for_task(original_query)` via MCP
|
|
81
|
+
2. Extract `suggested_skills` from response
|
|
82
|
+
3. If `suggested_skills` non-empty, prepend to spawned agent prompt:
|
|
83
|
+
`"Ontology context suggests these skills may be relevant: {suggested_skills}"`
|
|
84
|
+
4. On MCP failure: skip silently, proceed with unmodified prompt
|
|
85
|
+
|
|
86
|
+
**This step is advisory only — it never changes which agent is selected.**
|
|
87
|
+
|
|
88
|
+
### Step 5: Soul Injection
|
|
89
|
+
|
|
90
|
+
If the selected agent has `soul: true` in its frontmatter:
|
|
91
|
+
|
|
92
|
+
1. Read `.claude/agents/souls/{agent-name}.soul.md`
|
|
93
|
+
2. If file exists, prepend soul content to the agent's prompt:
|
|
94
|
+
`"Identity context:\n{soul content}\n\n---\n\n"`
|
|
95
|
+
3. If file doesn't exist → skip silently (no error, no injection)
|
|
96
|
+
|
|
97
|
+
**This step runs after ontology-RAG enrichment. Soul content is identity context, not capability instructions.**
|
|
98
|
+
|
|
76
99
|
## Command Routing
|
|
77
100
|
|
|
78
101
|
```
|
|
@@ -262,7 +285,7 @@ Delegate to mgr-creator with context:
|
|
|
262
285
|
keywords: extracted tool names
|
|
263
286
|
file_patterns: detected config patterns
|
|
264
287
|
skills: auto-discover from .claude/skills/
|
|
265
|
-
guides: auto-discover from guides/
|
|
288
|
+
guides: auto-discover from templates/guides/
|
|
266
289
|
```
|
|
267
290
|
|
|
268
291
|
**Examples of dynamic creation triggers:**
|
|
@@ -107,6 +107,29 @@ For **new file creation**, **boilerplate**, or **test code generation**:
|
|
|
107
107
|
### Step 3: Expert Agent Selection
|
|
108
108
|
Route to appropriate language/framework expert based on file extension and keyword mapping.
|
|
109
109
|
|
|
110
|
+
### Step 4: Ontology-RAG Enrichment (R019)
|
|
111
|
+
|
|
112
|
+
After agent selection, enrich the spawned agent's prompt with ontology context:
|
|
113
|
+
|
|
114
|
+
1. Call `get_agent_for_task(original_query)` via MCP
|
|
115
|
+
2. Extract `suggested_skills` from response
|
|
116
|
+
3. If `suggested_skills` non-empty, prepend to spawned agent prompt:
|
|
117
|
+
`"Ontology context suggests these skills may be relevant: {suggested_skills}"`
|
|
118
|
+
4. On MCP failure: skip silently, proceed with unmodified prompt
|
|
119
|
+
|
|
120
|
+
**This step is advisory only — it never changes which agent is selected.**
|
|
121
|
+
|
|
122
|
+
### Step 5: Soul Injection
|
|
123
|
+
|
|
124
|
+
If the selected agent has `soul: true` in its frontmatter:
|
|
125
|
+
|
|
126
|
+
1. Read `.claude/agents/souls/{agent-name}.soul.md`
|
|
127
|
+
2. If file exists, prepend soul content to the agent's prompt:
|
|
128
|
+
`"Identity context:\n{soul content}\n\n---\n\n"`
|
|
129
|
+
3. If file doesn't exist → skip silently (no error, no injection)
|
|
130
|
+
|
|
131
|
+
**This step runs after ontology-RAG enrichment. Soul content is identity context, not capability instructions.**
|
|
132
|
+
|
|
110
133
|
## Routing Rules
|
|
111
134
|
|
|
112
135
|
Multi-language: detect all languages, route to parallel experts (max 4). Single-language: route to matching expert. Cross-layer (frontend + backend): multiple experts in parallel.
|
|
@@ -126,7 +149,7 @@ Delegate to mgr-creator with context:
|
|
|
126
149
|
keywords: extracted from user input
|
|
127
150
|
file_patterns: detected extensions
|
|
128
151
|
skills: auto-discover from .claude/skills/
|
|
129
|
-
guides: auto-discover from guides/
|
|
152
|
+
guides: auto-discover from templates/guides/
|
|
130
153
|
```
|
|
131
154
|
|
|
132
155
|
**Examples of dynamic creation triggers:**
|
|
@@ -32,6 +32,19 @@ Review code for best practices using language-specific expert agents.
|
|
|
32
32
|
4. Analyze code against best practices
|
|
33
33
|
5. Generate review report
|
|
34
34
|
```
|
|
35
|
+
6. **Artifact persistence** (optional): Review agent saves findings to:
|
|
36
|
+
```
|
|
37
|
+
.claude/outputs/sessions/{YYYY-MM-DD}/dev-review-{HHmmss}.md
|
|
38
|
+
```
|
|
39
|
+
With metadata header:
|
|
40
|
+
```markdown
|
|
41
|
+
---
|
|
42
|
+
skill: dev-review
|
|
43
|
+
date: {ISO-8601 with timezone}
|
|
44
|
+
query: "{original user query}"
|
|
45
|
+
---
|
|
46
|
+
```
|
|
47
|
+
The review agent creates the directory and writes the artifact before returning results (R010 compliance).
|
|
35
48
|
|
|
36
49
|
## Agent Selection
|
|
37
50
|
|
|
@@ -44,6 +44,29 @@ quality_analysis → qa-planner + qa-engineer (parallel)
|
|
|
44
44
|
full_qa_cycle → all agents (sequential)
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
### Ontology-RAG Enrichment (R019)
|
|
48
|
+
|
|
49
|
+
After agent selection, enrich the spawned agent's prompt with ontology context:
|
|
50
|
+
|
|
51
|
+
1. Call `get_agent_for_task(original_query)` via MCP
|
|
52
|
+
2. Extract `suggested_skills` from response
|
|
53
|
+
3. If `suggested_skills` non-empty, prepend to spawned agent prompt:
|
|
54
|
+
`"Ontology context suggests these skills may be relevant: {suggested_skills}"`
|
|
55
|
+
4. On MCP failure: skip silently, proceed with unmodified prompt
|
|
56
|
+
|
|
57
|
+
**This step is advisory only — it never changes which agent is selected.**
|
|
58
|
+
|
|
59
|
+
### Step 5: Soul Injection
|
|
60
|
+
|
|
61
|
+
If the selected agent has `soul: true` in its frontmatter:
|
|
62
|
+
|
|
63
|
+
1. Read `.claude/agents/souls/{agent-name}.soul.md`
|
|
64
|
+
2. If file exists, prepend soul content to the agent's prompt:
|
|
65
|
+
`"Identity context:\n{soul content}\n\n---\n\n"`
|
|
66
|
+
3. If file doesn't exist → skip silently (no error, no injection)
|
|
67
|
+
|
|
68
|
+
**This step runs after ontology-RAG enrichment. Soul content is identity context, not capability instructions.**
|
|
69
|
+
|
|
47
70
|
## Routing Rules
|
|
48
71
|
|
|
49
72
|
### 1. Test Planning
|
|
@@ -293,7 +316,7 @@ Delegate to mgr-creator with context:
|
|
|
293
316
|
type: qa-engineer
|
|
294
317
|
keywords: extracted testing terms
|
|
295
318
|
skills: auto-discover from .claude/skills/
|
|
296
|
-
guides: auto-discover from guides/
|
|
319
|
+
guides: auto-discover from templates/guides/
|
|
297
320
|
```
|
|
298
321
|
|
|
299
322
|
**Examples of dynamic creation triggers:**
|
|
@@ -45,7 +45,7 @@ Batch 2: T5, T6, T7, T8 (Integration + Comparative)
|
|
|
45
45
|
Batch 3: T9, T10 (Innovation)
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
### Phase 2: Cross-Verification Loop (min 2, max
|
|
48
|
+
### Phase 2: Cross-Verification Loop (min 2, max 30 rounds)
|
|
49
49
|
|
|
50
50
|
```
|
|
51
51
|
Team findings ──→ opus 4.6 verification ──→ codex-exec xhigh verification
|
|
@@ -60,7 +60,7 @@ Each round:
|
|
|
60
60
|
3. **Contradiction resolution**: Reconcile divergent findings between teams and verifiers
|
|
61
61
|
4. **Convergence check**: All major claims verified with no outstanding contradictions → proceed
|
|
62
62
|
|
|
63
|
-
Convergence expected by round 3. Hard stop at round
|
|
63
|
+
Convergence expected by round 3. Hard stop at round 30.
|
|
64
64
|
|
|
65
65
|
### Phase 3: Synthesis
|
|
66
66
|
|
|
@@ -71,8 +71,21 @@ Convergence expected by round 3. Hard stop at round 5.
|
|
|
71
71
|
### Phase 4: Output
|
|
72
72
|
|
|
73
73
|
1. Structured markdown report (see Output Format below)
|
|
74
|
-
2.
|
|
75
|
-
|
|
74
|
+
2. **Artifact persistence**: The Phase 4 synthesis agent (opus) writes the report to:
|
|
75
|
+
```
|
|
76
|
+
.claude/outputs/sessions/{YYYY-MM-DD}/research-{HHmmss}.md
|
|
77
|
+
```
|
|
78
|
+
With metadata header:
|
|
79
|
+
```markdown
|
|
80
|
+
---
|
|
81
|
+
skill: research
|
|
82
|
+
date: {ISO-8601 with timezone}
|
|
83
|
+
query: "{original user query}"
|
|
84
|
+
---
|
|
85
|
+
```
|
|
86
|
+
The agent creates the directory (`mkdir -p`) before writing. This is a subagent operation (R010 compliance).
|
|
87
|
+
3. GitHub issue auto-created with findings
|
|
88
|
+
4. Action items with effort estimates
|
|
76
89
|
|
|
77
90
|
## Execution Rules
|
|
78
91
|
|
|
@@ -53,6 +53,29 @@ todo → sys-naggy
|
|
|
53
53
|
batch → multiple (parallel)
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
### Ontology-RAG Enrichment (R019)
|
|
57
|
+
|
|
58
|
+
After agent selection, enrich the spawned agent's prompt with ontology context:
|
|
59
|
+
|
|
60
|
+
1. Call `get_agent_for_task(original_query)` via MCP
|
|
61
|
+
2. Extract `suggested_skills` from response
|
|
62
|
+
3. If `suggested_skills` non-empty, prepend to spawned agent prompt:
|
|
63
|
+
`"Ontology context suggests these skills may be relevant: {suggested_skills}"`
|
|
64
|
+
4. On MCP failure: skip silently, proceed with unmodified prompt
|
|
65
|
+
|
|
66
|
+
**This step is advisory only — it never changes which agent is selected.**
|
|
67
|
+
|
|
68
|
+
### Step 5: Soul Injection
|
|
69
|
+
|
|
70
|
+
If the selected agent has `soul: true` in its frontmatter:
|
|
71
|
+
|
|
72
|
+
1. Read `.claude/agents/souls/{agent-name}.soul.md`
|
|
73
|
+
2. If file exists, prepend soul content to the agent's prompt:
|
|
74
|
+
`"Identity context:\n{soul content}\n\n---\n\n"`
|
|
75
|
+
3. If file doesn't exist → skip silently (no error, no injection)
|
|
76
|
+
|
|
77
|
+
**This step runs after ontology-RAG enrichment. Soul content is identity context, not capability instructions.**
|
|
78
|
+
|
|
56
79
|
## Routing Rules
|
|
57
80
|
|
|
58
81
|
### 1. Single Task Routing
|
package/templates/CLAUDE.md.en
CHANGED
|
@@ -168,6 +168,7 @@ Violation = immediate correction. No exception for "small changes".
|
|
|
168
168
|
| `/optimize-analyze` | Analyze bundle and performance |
|
|
169
169
|
| `/optimize-bundle` | Optimize bundle size |
|
|
170
170
|
| `/optimize-report` | Generate optimization report |
|
|
171
|
+
| `/research` | 10-team parallel deep analysis and cross-verification |
|
|
171
172
|
| `/sauron-watch` | Full R017 verification |
|
|
172
173
|
| `/structured-dev-cycle` | 6-stage structured development cycle (Plan → Verify → Implement → Verify → Compound → Done) |
|
|
173
174
|
| `/lists` | Show all available commands |
|
|
@@ -185,7 +186,7 @@ project/
|
|
|
185
186
|
| +-- rules/ # Global rules (R000-R018)
|
|
186
187
|
| +-- hooks/ # Hook scripts (memory, HUD)
|
|
187
188
|
| +-- contexts/ # Context files (ecomode)
|
|
188
|
-
+-- guides/ # Reference docs (
|
|
189
|
+
+-- guides/ # Reference docs (25 topics)
|
|
189
190
|
```
|
|
190
191
|
|
|
191
192
|
## Orchestration
|
package/templates/CLAUDE.md.ko
CHANGED
|
@@ -168,6 +168,7 @@ oh-my-customcode로 구동됩니다.
|
|
|
168
168
|
| `/optimize-analyze` | 번들 및 성능 분석 |
|
|
169
169
|
| `/optimize-bundle` | 번들 크기 최적화 |
|
|
170
170
|
| `/optimize-report` | 최적화 리포트 생성 |
|
|
171
|
+
| `/research` | 10-team 병렬 딥 분석 및 교차 검증 |
|
|
171
172
|
| `/sauron-watch` | 전체 R017 검증 |
|
|
172
173
|
| `/structured-dev-cycle` | 6단계 구조적 개발 사이클 (Plan → Verify → Implement → Verify → Compound → Done) |
|
|
173
174
|
| `/lists` | 모든 사용 가능한 커맨드 표시 |
|
|
@@ -185,7 +186,7 @@ project/
|
|
|
185
186
|
| +-- rules/ # 전역 규칙 (R000-R018)
|
|
186
187
|
| +-- hooks/ # 훅 스크립트 (메모리, HUD)
|
|
187
188
|
| +-- contexts/ # 컨텍스트 파일 (ecomode)
|
|
188
|
-
+-- guides/ # 레퍼런스 문서 (
|
|
189
|
+
+-- guides/ # 레퍼런스 문서 (25 토픽)
|
|
189
190
|
```
|
|
190
191
|
|
|
191
192
|
## 오케스트레이션
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Flutter Security Guide
|
|
2
|
+
|
|
3
|
+
> Reference: OWASP Mobile Top 10 (2024), Flutter Official Documentation
|
|
4
|
+
|
|
5
|
+
## OWASP Mobile Top 10 Mapping
|
|
6
|
+
|
|
7
|
+
### M1 — Improper Credential Usage
|
|
8
|
+
|
|
9
|
+
- Never hardcode API keys, tokens, or credentials in source code
|
|
10
|
+
- Backend proxy pattern: route ALL sensitive API calls through server
|
|
11
|
+
- `--dart-define-from-file=.env` is for NON-SECRET build config only (values are extractable from binary)
|
|
12
|
+
- Credential rotation: implement token refresh with `dio` interceptor
|
|
13
|
+
- OAuth2 flow: use `flutter_appauth` for PKCE-based authentication
|
|
14
|
+
|
|
15
|
+
### M2 — Inadequate Supply Chain Security
|
|
16
|
+
|
|
17
|
+
- Run `dart pub audit` before every release to check for known vulnerabilities
|
|
18
|
+
- Pin exact versions in `pubspec.yaml` for production (`package: 1.2.3` not `package: ^1.2.3`)
|
|
19
|
+
- Verify package publisher on pub.dev (look for verified publisher badge)
|
|
20
|
+
- Review transitive dependencies: `dart pub deps --style=compact`
|
|
21
|
+
- Avoid packages with no recent updates (> 12 months without commits)
|
|
22
|
+
|
|
23
|
+
### M3 — Insecure Authentication/Authorization
|
|
24
|
+
|
|
25
|
+
- Biometric authentication: `local_auth` package with `BiometricType.fingerprint` / `BiometricType.face`
|
|
26
|
+
- Session management: implement token expiry checking before API calls
|
|
27
|
+
- JWT client-side validation: verify `exp`, `aud`, `iss` claims before using tokens
|
|
28
|
+
- Re-authentication: require biometric/PIN for sensitive operations (payment, profile changes)
|
|
29
|
+
- Deep link auth: validate authentication state before processing deep link navigation
|
|
30
|
+
|
|
31
|
+
### M4 — Insufficient Input/Output Validation
|
|
32
|
+
|
|
33
|
+
- Validate ALL deep link URI parameters with RegExp allowlists
|
|
34
|
+
- Sanitize user input before displaying in WebView (`flutter_inappwebview`)
|
|
35
|
+
- Use `Uri.parse()` with try-catch, never trust raw string URLs
|
|
36
|
+
- Output encoding: escape HTML entities when rendering user content
|
|
37
|
+
- Form validation: use `TextFormField` validators, never trust client-side validation alone
|
|
38
|
+
|
|
39
|
+
### M5 — Insecure Communication
|
|
40
|
+
|
|
41
|
+
- Certificate pinning (SPKI): use `dio` with custom `SecurityContext`
|
|
42
|
+
- Extract SPKI hash: `openssl s_client -connect host:443 | openssl x509 -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | base64`
|
|
43
|
+
- Include backup pins for certificate rotation
|
|
44
|
+
- Android: `network_security_config.xml` with `cleartextTrafficPermitted=false`
|
|
45
|
+
- iOS: ATS enabled (`NSAllowsArbitraryLoads=false`), never override in production
|
|
46
|
+
|
|
47
|
+
### M6 — Inadequate Privacy Controls
|
|
48
|
+
|
|
49
|
+
- Request minimum platform permissions (camera, location, contacts)
|
|
50
|
+
- iOS: provide usage description strings in Info.plist for every permission
|
|
51
|
+
- Android: use runtime permissions, respect "Don't ask again"
|
|
52
|
+
- Data minimization: only collect and store data that is necessary
|
|
53
|
+
- GDPR/CCPA: implement data export and deletion capabilities
|
|
54
|
+
|
|
55
|
+
### M7 — Insufficient Binary Protections
|
|
56
|
+
|
|
57
|
+
- Release builds: `flutter build --obfuscate --split-debug-info=debug-info/`
|
|
58
|
+
- Store debug symbols securely for crash reporting (Crashlytics, Sentry)
|
|
59
|
+
- Android ProGuard: configure `android/app/proguard-rules.pro`
|
|
60
|
+
- Note: `--obfuscate` does NOT apply to `flutter build web` (JS minification is the web equivalent)
|
|
61
|
+
- Anti-tampering: consider `flutter_jailbreak_detection` for integrity checks
|
|
62
|
+
|
|
63
|
+
### M8 — Security Misconfiguration
|
|
64
|
+
|
|
65
|
+
- Android: set `android:debuggable="false"` in release manifest
|
|
66
|
+
- Android: set `android:allowBackup="false"` to prevent ADB data extraction
|
|
67
|
+
- iOS: enable data protection with `NSFileProtectionComplete`
|
|
68
|
+
- Remove all debug logging in release: guard with `kDebugMode`
|
|
69
|
+
- Firebase: secure `google-services.json` / `GoogleService-Info.plist` (add to .gitignore)
|
|
70
|
+
|
|
71
|
+
### M9 — Insecure Data Storage
|
|
72
|
+
|
|
73
|
+
- Sensitive data: `flutter_secure_storage` v10+ (iOS Keychain / Android EncryptedSharedPreferences)
|
|
74
|
+
- iOS: `IOSOptions(accessibility: KeychainAccessibility.first_unlock_this_device)`
|
|
75
|
+
- Android: `AndroidOptions(encryptedSharedPreferences: true)`
|
|
76
|
+
- Web WARNING: `flutter_secure_storage` uses localStorage on Web (XSS vulnerable) — use HttpOnly cookies or in-memory storage
|
|
77
|
+
- Never use `SharedPreferences` for tokens, PII, or credentials
|
|
78
|
+
- Screenshot protection: Android `FLAG_SECURE` via `flutter_windowmanager`
|
|
79
|
+
|
|
80
|
+
### M10 — Insufficient Cryptography
|
|
81
|
+
|
|
82
|
+
- Use `pointycastle` or `cryptography` package for custom crypto operations
|
|
83
|
+
- Avoid: MD5, SHA-1, DES, ECB mode, hardcoded IVs/keys
|
|
84
|
+
- Prefer: AES-256-GCM for symmetric, RSA-OAEP or ECDSA for asymmetric
|
|
85
|
+
- Key storage: always delegate to platform Keychain/Keystore, never store in app data
|
|
86
|
+
- Random number generation: use `Random.secure()` for security-sensitive values
|
|
87
|
+
|
|
88
|
+
## Platform-Specific Security
|
|
89
|
+
|
|
90
|
+
### iOS
|
|
91
|
+
|
|
92
|
+
- Keychain with Secure Enclave: `IOSOptions(useSecureEnclave: true)` for high-value data
|
|
93
|
+
- ATS enforcement: never add `NSAllowsArbitraryLoads` exception for production
|
|
94
|
+
- Jailbreak detection: `flutter_jailbreak_detection` package
|
|
95
|
+
|
|
96
|
+
### Android
|
|
97
|
+
|
|
98
|
+
- Keystore-backed encryption via `EncryptedSharedPreferences`
|
|
99
|
+
- Network security config: pin certificates, block cleartext
|
|
100
|
+
- Root detection: `flutter_jailbreak_detection` or `safe_device`
|
|
101
|
+
- `allowBackup=false` in AndroidManifest.xml
|
|
102
|
+
|
|
103
|
+
### Web
|
|
104
|
+
|
|
105
|
+
- CSP headers: configure on the server hosting Flutter web app
|
|
106
|
+
- Avoid storing sensitive data in localStorage or sessionStorage
|
|
107
|
+
- Use HttpOnly, Secure, SameSite cookies for authentication tokens
|
|
108
|
+
- XSS prevention: sanitize all user-generated content before rendering
|
|
109
|
+
|
|
110
|
+
## Package Recommendations
|
|
111
|
+
|
|
112
|
+
| Category | Package | Notes |
|
|
113
|
+
|----------|---------|-------|
|
|
114
|
+
| Secure Storage | `flutter_secure_storage` | Keychain/Keystore, v10+; Web: localStorage (XSS risk) |
|
|
115
|
+
| OAuth2 / PKCE | `flutter_appauth` | PKCE-based auth flows |
|
|
116
|
+
| Biometrics | `local_auth` | Fingerprint, Face ID |
|
|
117
|
+
| HTTP (pinning) | `dio` | Custom `SecurityContext` for certificate pinning |
|
|
118
|
+
| Crypto | `cryptography` | AES-GCM, RSA-OAEP, ECDSA |
|
|
119
|
+
| Integrity check | `flutter_jailbreak_detection` | Root/jailbreak detection |
|
|
120
|
+
| Screenshot protect | `flutter_windowmanager` | Android `FLAG_SECURE` |
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Java 21 Guide
|
|
2
|
+
|
|
3
|
+
metadata:
|
|
4
|
+
name: java21
|
|
5
|
+
description: Java 21 language reference and modern feature documentation
|
|
6
|
+
|
|
7
|
+
source:
|
|
8
|
+
type: external
|
|
9
|
+
origin: docs.oracle.com
|
|
10
|
+
urls:
|
|
11
|
+
- https://docs.oracle.com/en/java/javase/21/
|
|
12
|
+
- https://openjdk.org/projects/loom/
|
|
13
|
+
- https://openjdk.org/jeps/440
|
|
14
|
+
- https://openjdk.org/jeps/441
|
|
15
|
+
- https://openjdk.org/jeps/444
|
|
16
|
+
- https://google.github.io/styleguide/javaguide.html
|
|
17
|
+
last_fetched: "2026-03-11"
|
|
18
|
+
|
|
19
|
+
documents:
|
|
20
|
+
- name: modern-java21
|
|
21
|
+
path: ./modern-java21.md
|
|
22
|
+
description: Java 21 modern features (Virtual Threads, Pattern Matching, Records, Sealed Classes)
|
|
23
|
+
|
|
24
|
+
- name: java-style-guide
|
|
25
|
+
path: ./java-style-guide.md
|
|
26
|
+
description: Google Java Style Guide conventions
|
|
27
|
+
|
|
28
|
+
used_by:
|
|
29
|
+
- lang-java21-expert
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Java Style Guide
|
|
2
|
+
|
|
3
|
+
> Source: https://google.github.io/styleguide/javaguide.html
|
|
4
|
+
|
|
5
|
+
## File Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Source file:
|
|
9
|
+
1. License/copyright (if any)
|
|
10
|
+
2. Package statement
|
|
11
|
+
3. Import statements
|
|
12
|
+
4. Exactly one top-level class
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Import Ordering
|
|
16
|
+
|
|
17
|
+
```java
|
|
18
|
+
// 1. Static imports (all together)
|
|
19
|
+
import static org.junit.Assert.assertEquals;
|
|
20
|
+
|
|
21
|
+
// 2. Non-static imports (all together, no subgroups)
|
|
22
|
+
import com.example.Foo;
|
|
23
|
+
import java.util.List;
|
|
24
|
+
import org.springframework.boot.SpringApplication;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
No wildcard imports except `static` test imports.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Naming Conventions
|
|
32
|
+
|
|
33
|
+
| Element | Style | Example |
|
|
34
|
+
|---------|-------|---------|
|
|
35
|
+
| Packages | `lowercase` | `com.example.network` |
|
|
36
|
+
| Classes | `UpperCamelCase` | `OrderProcessor` |
|
|
37
|
+
| Records | `UpperCamelCase` | `UserRecord` |
|
|
38
|
+
| Methods | `lowerCamelCase` | `processOrder()` |
|
|
39
|
+
| Local vars | `lowerCamelCase` | `itemCount` |
|
|
40
|
+
| Constants | `UPPER_SNAKE_CASE` | `MAX_RETRIES` |
|
|
41
|
+
| Type params | Single letter or `UpperCamelCase + T` | `T`, `E`, `RequestT` |
|
|
42
|
+
|
|
43
|
+
### Acronyms
|
|
44
|
+
|
|
45
|
+
Treat acronyms as words: `XmlParser`, not `XMLParser`. Exception: well-known 2-letter ones like `IO`.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Formatting
|
|
50
|
+
|
|
51
|
+
### Indentation
|
|
52
|
+
|
|
53
|
+
- 2 spaces (not tabs) for block indentation
|
|
54
|
+
- 4 spaces for line continuation
|
|
55
|
+
|
|
56
|
+
```java
|
|
57
|
+
// Block indentation: 2 spaces
|
|
58
|
+
if (condition) {
|
|
59
|
+
doSomething();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Line continuation: 4 spaces
|
|
63
|
+
String result = longMethodName(
|
|
64
|
+
argument1,
|
|
65
|
+
argument2);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Braces
|
|
69
|
+
|
|
70
|
+
Always use braces, even for single-statement bodies:
|
|
71
|
+
|
|
72
|
+
```java
|
|
73
|
+
// Correct
|
|
74
|
+
if (condition) {
|
|
75
|
+
doSomething();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Wrong (no braces)
|
|
79
|
+
if (condition)
|
|
80
|
+
doSomething();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Column Limit
|
|
84
|
+
|
|
85
|
+
100 characters per line. Wrap when exceeding.
|
|
86
|
+
|
|
87
|
+
### Blank Lines
|
|
88
|
+
|
|
89
|
+
```java
|
|
90
|
+
class MyClass {
|
|
91
|
+
private int field; // field
|
|
92
|
+
// one blank line
|
|
93
|
+
public MyClass() { } // constructor
|
|
94
|
+
// one blank line
|
|
95
|
+
public void method() { } // method
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Class Structure
|
|
102
|
+
|
|
103
|
+
```java
|
|
104
|
+
public class MyClass {
|
|
105
|
+
// 1. Static fields
|
|
106
|
+
private static final Logger log = LoggerFactory.getLogger(MyClass.class);
|
|
107
|
+
|
|
108
|
+
// 2. Instance fields
|
|
109
|
+
private final String name;
|
|
110
|
+
|
|
111
|
+
// 3. Constructors
|
|
112
|
+
public MyClass(String name) {
|
|
113
|
+
this.name = name;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 4. Static factory methods (if applicable)
|
|
117
|
+
public static MyClass of(String name) {
|
|
118
|
+
return new MyClass(name);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 5. Instance methods (public → package → protected → private)
|
|
122
|
+
public String getName() { return name; }
|
|
123
|
+
|
|
124
|
+
private void helper() { }
|
|
125
|
+
|
|
126
|
+
// 6. Inner classes/interfaces (last)
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Programming Practices
|
|
133
|
+
|
|
134
|
+
### Annotations
|
|
135
|
+
|
|
136
|
+
```java
|
|
137
|
+
// One annotation per line for class/method
|
|
138
|
+
@Override
|
|
139
|
+
@Nullable
|
|
140
|
+
public String format(String input) { }
|
|
141
|
+
|
|
142
|
+
// Multiple short annotations on one line for field is OK
|
|
143
|
+
@Nullable @Deprecated String field;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Numeric Literals
|
|
147
|
+
|
|
148
|
+
```java
|
|
149
|
+
long big = 1_000_000L;
|
|
150
|
+
double pi = 3.14_159;
|
|
151
|
+
int hex = 0xFF_EC_D1_8C;
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Switch Expressions (prefer over statements)
|
|
155
|
+
|
|
156
|
+
```java
|
|
157
|
+
// Prefer switch expression
|
|
158
|
+
int days = switch (month) {
|
|
159
|
+
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> 31;
|
|
160
|
+
case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
|
|
161
|
+
case FEBRUARY -> 28;
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Avoid Long Methods
|
|
166
|
+
|
|
167
|
+
Keep methods short and focused. Extract helpers for blocks exceeding ~20 lines.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Javadoc
|
|
172
|
+
|
|
173
|
+
### Required for
|
|
174
|
+
|
|
175
|
+
- Every `public` class, interface, enum, record
|
|
176
|
+
- Every `public` or `protected` method (unless trivially obvious)
|
|
177
|
+
|
|
178
|
+
### Format
|
|
179
|
+
|
|
180
|
+
```java
|
|
181
|
+
/**
|
|
182
|
+
* Returns the user associated with the given ID.
|
|
183
|
+
*
|
|
184
|
+
* <p>This method performs a database lookup. It is safe to call
|
|
185
|
+
* from multiple threads.
|
|
186
|
+
*
|
|
187
|
+
* @param id the user identifier (must be positive)
|
|
188
|
+
* @return the user, or empty if not found
|
|
189
|
+
* @throws IllegalArgumentException if {@code id <= 0}
|
|
190
|
+
*/
|
|
191
|
+
public Optional<User> findById(long id) { }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Inline Tags
|
|
195
|
+
|
|
196
|
+
```java
|
|
197
|
+
/**
|
|
198
|
+
* See {@link UserRepository} for persistence details.
|
|
199
|
+
* Use {@code null} to reset the state.
|
|
200
|
+
*/
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Prohibited: Non-Javadoc Comments for API
|
|
204
|
+
|
|
205
|
+
```java
|
|
206
|
+
// Wrong: plain comment for public method
|
|
207
|
+
// Returns the user by id
|
|
208
|
+
public Optional<User> findById(long id) { }
|
|
209
|
+
|
|
210
|
+
// Correct: Javadoc
|
|
211
|
+
/** Returns the user by id, or empty if not found. */
|
|
212
|
+
public Optional<User> findById(long id) { }
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Common Antipatterns to Avoid
|
|
218
|
+
|
|
219
|
+
```java
|
|
220
|
+
// ❌ Raw types
|
|
221
|
+
List list = new ArrayList();
|
|
222
|
+
// ✓
|
|
223
|
+
List<String> list = new ArrayList<>();
|
|
224
|
+
|
|
225
|
+
// ❌ String concatenation in loop
|
|
226
|
+
String s = "";
|
|
227
|
+
for (String item : items) s += item;
|
|
228
|
+
// ✓
|
|
229
|
+
StringBuilder sb = new StringBuilder();
|
|
230
|
+
for (String item : items) sb.append(item);
|
|
231
|
+
String s = sb.toString();
|
|
232
|
+
|
|
233
|
+
// ❌ Return null for collections
|
|
234
|
+
public List<String> getItems() { return null; }
|
|
235
|
+
// ✓
|
|
236
|
+
public List<String> getItems() { return Collections.emptyList(); }
|
|
237
|
+
|
|
238
|
+
// ❌ Catch Exception broadly
|
|
239
|
+
try { process(); } catch (Exception e) { }
|
|
240
|
+
// ✓
|
|
241
|
+
try { process(); } catch (IOException e) { log.error("IO error", e); }
|
|
242
|
+
|
|
243
|
+
// ❌ Mutable public field
|
|
244
|
+
public String name;
|
|
245
|
+
// ✓
|
|
246
|
+
private String name;
|
|
247
|
+
public String getName() { return name; }
|
|
248
|
+
```
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Modern Java 21 Features
|
|
2
|
+
|
|
3
|
+
> Sources: https://openjdk.org/jeps/ (JEP 431, 440, 441, 444)
|
|
4
|
+
|
|
5
|
+
## Virtual Threads (JEP 444)
|
|
6
|
+
|
|
7
|
+
Virtual Threads are lightweight threads managed by the JVM, enabling millions of concurrent tasks without thread pool tuning.
|
|
8
|
+
|
|
9
|
+
### Key Properties
|
|
10
|
+
|
|
11
|
+
| Property | Platform Thread | Virtual Thread |
|
|
12
|
+
|----------|----------------|----------------|
|
|
13
|
+
| Creation cost | High (OS thread) | Low (JVM-managed) |
|
|
14
|
+
| Memory footprint | ~1MB per thread | ~few KB |
|
|
15
|
+
| Blocking behavior | Blocks OS thread | Unmounts carrier thread |
|
|
16
|
+
| Pooling | Needed | Not recommended |
|
|
17
|
+
|
|
18
|
+
### Usage
|
|
19
|
+
|
|
20
|
+
```java
|
|
21
|
+
// Virtual Thread executor (preferred for I/O-bound tasks)
|
|
22
|
+
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
|
23
|
+
List<Future<String>> futures = IntStream.range(0, 10_000)
|
|
24
|
+
.mapToObj(i -> executor.submit(() -> fetchData(i)))
|
|
25
|
+
.toList();
|
|
26
|
+
// all 10,000 tasks run concurrently
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Direct creation
|
|
30
|
+
Thread.ofVirtual().name("vt-worker").start(() -> processRequest());
|
|
31
|
+
|
|
32
|
+
// Factory for thread pools
|
|
33
|
+
ThreadFactory factory = Thread.ofVirtual().name("worker-", 0).factory();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Structured Concurrency (JEP 453, Preview)
|
|
37
|
+
|
|
38
|
+
```java
|
|
39
|
+
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
|
|
40
|
+
Future<String> user = scope.fork(() -> fetchUser(id));
|
|
41
|
+
Future<String> orders = scope.fork(() -> fetchOrders(id));
|
|
42
|
+
|
|
43
|
+
scope.join().throwIfFailed();
|
|
44
|
+
|
|
45
|
+
return new UserProfile(user.get(), orders.get());
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Pinning Avoidance
|
|
50
|
+
|
|
51
|
+
Virtual Threads are **pinned** (cannot unmount) inside `synchronized` blocks. Prefer `ReentrantLock`:
|
|
52
|
+
|
|
53
|
+
```java
|
|
54
|
+
// Avoid (causes pinning)
|
|
55
|
+
synchronized (lock) {
|
|
56
|
+
callBlockingIO();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Prefer
|
|
60
|
+
private final ReentrantLock lock = new ReentrantLock();
|
|
61
|
+
lock.lock();
|
|
62
|
+
try {
|
|
63
|
+
callBlockingIO();
|
|
64
|
+
} finally {
|
|
65
|
+
lock.unlock();
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Pattern Matching for instanceof (JEP 394)
|
|
72
|
+
|
|
73
|
+
```java
|
|
74
|
+
// Before Java 16
|
|
75
|
+
if (obj instanceof String) {
|
|
76
|
+
String s = (String) obj;
|
|
77
|
+
return s.length();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Java 21
|
|
81
|
+
if (obj instanceof String s) {
|
|
82
|
+
return s.length();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// With guard
|
|
86
|
+
if (obj instanceof String s && !s.isEmpty()) {
|
|
87
|
+
return s.toUpperCase();
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Pattern Matching for switch (JEP 441)
|
|
94
|
+
|
|
95
|
+
```java
|
|
96
|
+
// Type patterns
|
|
97
|
+
String format = switch (obj) {
|
|
98
|
+
case Integer i -> String.format("int %d", i);
|
|
99
|
+
case Double d -> String.format("double %.2f", d);
|
|
100
|
+
case String s -> String.format("String '%s'", s);
|
|
101
|
+
case null -> "null value";
|
|
102
|
+
default -> obj.toString();
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Guarded patterns (when clause)
|
|
106
|
+
String classify = switch (number) {
|
|
107
|
+
case Integer i when i < 0 -> "negative";
|
|
108
|
+
case Integer i when i == 0 -> "zero";
|
|
109
|
+
case Integer i -> "positive";
|
|
110
|
+
default -> "non-integer";
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Record Classes (JEP 395)
|
|
117
|
+
|
|
118
|
+
Records are immutable data carriers with auto-generated `equals`, `hashCode`, `toString`, and accessors.
|
|
119
|
+
|
|
120
|
+
```java
|
|
121
|
+
// Basic record
|
|
122
|
+
record Point(int x, int y) {}
|
|
123
|
+
|
|
124
|
+
Point p = new Point(3, 4);
|
|
125
|
+
int x = p.x(); // accessor (not getX())
|
|
126
|
+
System.out.println(p); // Point[x=3, y=4]
|
|
127
|
+
|
|
128
|
+
// Compact constructor (validation)
|
|
129
|
+
record Range(int min, int max) {
|
|
130
|
+
Range {
|
|
131
|
+
if (min > max)
|
|
132
|
+
throw new IllegalArgumentException(
|
|
133
|
+
"min %d > max %d".formatted(min, max));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Custom methods
|
|
138
|
+
record Circle(double radius) {
|
|
139
|
+
static final double PI = Math.PI;
|
|
140
|
+
|
|
141
|
+
double area() { return PI * radius * radius; }
|
|
142
|
+
|
|
143
|
+
Circle scale(double factor) { return new Circle(radius * factor); }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Implementing interface
|
|
147
|
+
interface Describable { String describe(); }
|
|
148
|
+
record Color(int r, int g, int b) implements Describable {
|
|
149
|
+
public String describe() {
|
|
150
|
+
return "rgb(%d,%d,%d)".formatted(r, g, b);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### When to Use Records vs Classes
|
|
156
|
+
|
|
157
|
+
| Use Record | Use Class |
|
|
158
|
+
|------------|-----------|
|
|
159
|
+
| Pure data containers | Entities with mutable state |
|
|
160
|
+
| DTOs, value objects | Domain objects with lifecycle |
|
|
161
|
+
| API response types | Services, repositories |
|
|
162
|
+
| Config/settings | Mutable builders |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Record Patterns (JEP 440)
|
|
167
|
+
|
|
168
|
+
Deconstruct records directly in `instanceof` and `switch`:
|
|
169
|
+
|
|
170
|
+
```java
|
|
171
|
+
// instanceof deconstruction
|
|
172
|
+
if (obj instanceof Point(int x, int y)) {
|
|
173
|
+
System.out.println("x=" + x + ", y=" + y);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// switch deconstruction
|
|
177
|
+
String describe = switch (shape) {
|
|
178
|
+
case Circle(double r) -> "circle r=%.2f".formatted(r);
|
|
179
|
+
case Rectangle(double w, double h) -> "rect %.1fx%.1f".formatted(w, h);
|
|
180
|
+
default -> "unknown";
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Nested patterns
|
|
184
|
+
record ColoredPoint(Point point, Color color) {}
|
|
185
|
+
|
|
186
|
+
if (obj instanceof ColoredPoint(Point(int x, int y), Color(int r, int g, int b))) {
|
|
187
|
+
System.out.printf("Colored point at (%d,%d) with rgb(%d,%d,%d)%n",
|
|
188
|
+
x, y, r, g, b);
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Sealed Classes (JEP 409)
|
|
195
|
+
|
|
196
|
+
Sealed classes restrict which classes can implement/extend them, enabling exhaustive pattern matching.
|
|
197
|
+
|
|
198
|
+
```java
|
|
199
|
+
// Sealed interface with records
|
|
200
|
+
sealed interface Shape permits Circle, Rectangle, Triangle {}
|
|
201
|
+
|
|
202
|
+
record Circle(double radius) implements Shape {}
|
|
203
|
+
record Rectangle(double width, double height) implements Shape {}
|
|
204
|
+
record Triangle(double base, double height) implements Shape {}
|
|
205
|
+
|
|
206
|
+
// Exhaustive switch — no default needed
|
|
207
|
+
double area = switch (shape) {
|
|
208
|
+
case Circle(double r) -> Math.PI * r * r;
|
|
209
|
+
case Rectangle(double w, double h) -> w * h;
|
|
210
|
+
case Triangle(double b, double h) -> 0.5 * b * h;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Sealed class hierarchy (non-record)
|
|
214
|
+
sealed class Vehicle permits Car, Truck, Motorcycle {}
|
|
215
|
+
final class Car extends Vehicle { }
|
|
216
|
+
non-sealed class Truck extends Vehicle { } // allows further subclassing
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Benefits
|
|
220
|
+
|
|
221
|
+
- Compiler enforces exhaustive handling in `switch`
|
|
222
|
+
- Clear closed type hierarchy in domain model
|
|
223
|
+
- Better than `enum` when subtypes carry different data
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Sequenced Collections (JEP 431)
|
|
228
|
+
|
|
229
|
+
New interfaces: `SequencedCollection`, `SequencedSet`, `SequencedMap`.
|
|
230
|
+
|
|
231
|
+
```java
|
|
232
|
+
// SequencedCollection
|
|
233
|
+
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
|
|
234
|
+
String first = list.getFirst(); // "a"
|
|
235
|
+
String last = list.getLast(); // "c"
|
|
236
|
+
list.addFirst("z"); // ["z", "a", "b", "c"]
|
|
237
|
+
list.addLast("end"); // ["z", "a", "b", "c", "end"]
|
|
238
|
+
list.removeFirst(); // ["a", "b", "c", "end"]
|
|
239
|
+
|
|
240
|
+
// Reversed view (live, backed by original)
|
|
241
|
+
List<String> reversed = list.reversed();
|
|
242
|
+
|
|
243
|
+
// SequencedMap
|
|
244
|
+
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
|
|
245
|
+
map.put("one", 1);
|
|
246
|
+
map.put("two", 2);
|
|
247
|
+
map.put("three", 3);
|
|
248
|
+
|
|
249
|
+
Map.Entry<String, Integer> first = map.firstEntry(); // "one"=1
|
|
250
|
+
Map.Entry<String, Integer> last = map.lastEntry(); // "three"=3
|
|
251
|
+
map.putFirst("zero", 0); // inserts at front
|
|
252
|
+
SequencedMap<String, Integer> rev = map.reversed();
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Migration from Legacy Java
|
|
258
|
+
|
|
259
|
+
### Replace instanceof chains
|
|
260
|
+
|
|
261
|
+
```java
|
|
262
|
+
// Legacy (avoid)
|
|
263
|
+
if (obj instanceof String) {
|
|
264
|
+
return ((String) obj).length();
|
|
265
|
+
} else if (obj instanceof Integer) {
|
|
266
|
+
return ((Integer) obj).intValue();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Modern
|
|
270
|
+
return switch (obj) {
|
|
271
|
+
case String s -> s.length();
|
|
272
|
+
case Integer i -> i;
|
|
273
|
+
default -> -1;
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Replace POJOs with Records
|
|
278
|
+
|
|
279
|
+
```java
|
|
280
|
+
// Legacy POJO (avoid for pure data)
|
|
281
|
+
public class Point {
|
|
282
|
+
private final int x, y;
|
|
283
|
+
public Point(int x, int y) { this.x = x; this.y = y; }
|
|
284
|
+
public int getX() { return x; }
|
|
285
|
+
public int getY() { return y; }
|
|
286
|
+
@Override public boolean equals(Object o) { ... }
|
|
287
|
+
@Override public int hashCode() { ... }
|
|
288
|
+
@Override public String toString() { ... }
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Modern Record
|
|
292
|
+
record Point(int x, int y) {}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Replace thread pools for I/O with Virtual Threads
|
|
296
|
+
|
|
297
|
+
```java
|
|
298
|
+
// Legacy (avoid for I/O-bound)
|
|
299
|
+
ExecutorService pool = Executors.newFixedThreadPool(200);
|
|
300
|
+
|
|
301
|
+
// Modern
|
|
302
|
+
ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor();
|
|
303
|
+
```
|
package/templates/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.31.1",
|
|
3
3
|
"lastUpdated": "2026-03-09T00:00:00.000Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"name": "guides",
|
|
25
25
|
"path": "guides",
|
|
26
26
|
"description": "Reference documentation",
|
|
27
|
-
"files":
|
|
27
|
+
"files": 25
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"name": "hooks",
|