erne-universal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/.claude-plugin/plugin.json +92 -0
  2. package/LICENSE +21 -0
  3. package/README.md +73 -0
  4. package/agents/architect.md +64 -0
  5. package/agents/code-reviewer.md +72 -0
  6. package/agents/expo-config-resolver.md +77 -0
  7. package/agents/native-bridge-builder.md +98 -0
  8. package/agents/performance-profiler.md +89 -0
  9. package/agents/tdd-guide.md +86 -0
  10. package/agents/ui-designer.md +100 -0
  11. package/agents/upgrade-assistant.md +106 -0
  12. package/bin/cli.js +55 -0
  13. package/commands/animate.md +70 -0
  14. package/commands/build-fix.md +57 -0
  15. package/commands/code-review.md +51 -0
  16. package/commands/component.md +93 -0
  17. package/commands/debug.md +74 -0
  18. package/commands/deploy.md +82 -0
  19. package/commands/learn.md +56 -0
  20. package/commands/native-module.md +51 -0
  21. package/commands/navigate.md +69 -0
  22. package/commands/perf.md +68 -0
  23. package/commands/plan.md +49 -0
  24. package/commands/quality-gate.md +80 -0
  25. package/commands/retrospective.md +70 -0
  26. package/commands/setup-device.md +99 -0
  27. package/commands/tdd.md +51 -0
  28. package/commands/upgrade.md +78 -0
  29. package/contexts/dev.md +29 -0
  30. package/contexts/review.md +32 -0
  31. package/contexts/vibe.md +44 -0
  32. package/docs/agents.md +41 -0
  33. package/docs/commands.md +53 -0
  34. package/docs/creating-skills.md +63 -0
  35. package/docs/getting-started.md +60 -0
  36. package/docs/hooks-profiles.md +73 -0
  37. package/docs/superpowers/plans/2026-03-10-erne-plan-1-infrastructure-hooks.md +3973 -0
  38. package/docs/superpowers/plans/2026-03-10-erne-plan-2-content-layer.md +4496 -0
  39. package/docs/superpowers/plans/2026-03-10-erne-plan-3-skills-knowledge-base.md +1952 -0
  40. package/docs/superpowers/plans/2026-03-10-erne-plan-4-install-cli-distribution.md +1624 -0
  41. package/docs/superpowers/specs/2026-03-10-everything-react-native-expo-design.md +581 -0
  42. package/examples/claude-md-bare-rn.md +46 -0
  43. package/examples/claude-md-expo-managed.md +45 -0
  44. package/examples/eas-json-standard.json +41 -0
  45. package/hooks/hooks.json +113 -0
  46. package/hooks/profiles/minimal.json +9 -0
  47. package/hooks/profiles/standard.json +17 -0
  48. package/hooks/profiles/strict.json +22 -0
  49. package/install.sh +50 -0
  50. package/mcp-configs/agent-device.json +10 -0
  51. package/mcp-configs/appstore-connect.json +15 -0
  52. package/mcp-configs/expo-api.json +13 -0
  53. package/mcp-configs/figma.json +13 -0
  54. package/mcp-configs/firebase.json +14 -0
  55. package/mcp-configs/github.json +13 -0
  56. package/mcp-configs/memory.json +13 -0
  57. package/mcp-configs/play-console.json +14 -0
  58. package/mcp-configs/sentry.json +15 -0
  59. package/mcp-configs/supabase.json +14 -0
  60. package/package.json +50 -0
  61. package/rules/bare-rn/coding-style.md +62 -0
  62. package/rules/bare-rn/patterns.md +54 -0
  63. package/rules/bare-rn/security.md +58 -0
  64. package/rules/bare-rn/testing.md +78 -0
  65. package/rules/common/coding-style.md +50 -0
  66. package/rules/common/development-workflow.md +55 -0
  67. package/rules/common/git-workflow.md +40 -0
  68. package/rules/common/navigation.md +56 -0
  69. package/rules/common/patterns.md +59 -0
  70. package/rules/common/performance.md +55 -0
  71. package/rules/common/security.md +64 -0
  72. package/rules/common/state-management.md +86 -0
  73. package/rules/common/testing.md +61 -0
  74. package/rules/expo/coding-style.md +54 -0
  75. package/rules/expo/patterns.md +71 -0
  76. package/rules/expo/security.md +41 -0
  77. package/rules/expo/testing.md +68 -0
  78. package/rules/native-android/coding-style.md +81 -0
  79. package/rules/native-android/patterns.md +77 -0
  80. package/rules/native-android/security.md +80 -0
  81. package/rules/native-android/testing.md +94 -0
  82. package/rules/native-ios/coding-style.md +72 -0
  83. package/rules/native-ios/patterns.md +72 -0
  84. package/rules/native-ios/security.md +59 -0
  85. package/rules/native-ios/testing.md +79 -0
  86. package/schemas/hooks.schema.json +34 -0
  87. package/schemas/plugin.schema.json +55 -0
  88. package/scripts/hooks/accessibility-check.js +117 -0
  89. package/scripts/hooks/bundle-size-check.js +31 -0
  90. package/scripts/hooks/check-console-log.js +37 -0
  91. package/scripts/hooks/check-expo-config.js +40 -0
  92. package/scripts/hooks/check-platform-specific.js +40 -0
  93. package/scripts/hooks/check-reanimated-worklet.js +51 -0
  94. package/scripts/hooks/continuous-learning-observer.js +24 -0
  95. package/scripts/hooks/evaluate-session.js +26 -0
  96. package/scripts/hooks/lib/hook-utils.js +52 -0
  97. package/scripts/hooks/native-compat-check.js +42 -0
  98. package/scripts/hooks/performance-budget.js +57 -0
  99. package/scripts/hooks/post-edit-format.js +38 -0
  100. package/scripts/hooks/post-edit-typecheck.js +31 -0
  101. package/scripts/hooks/pre-commit-lint.js +44 -0
  102. package/scripts/hooks/pre-edit-test-gate.js +68 -0
  103. package/scripts/hooks/run-with-flags.js +93 -0
  104. package/scripts/hooks/security-scan.js +65 -0
  105. package/scripts/hooks/session-start.js +77 -0
  106. package/scripts/lint-content.js +62 -0
  107. package/scripts/validate-all.js +137 -0
  108. package/skills/coding-standards/SKILL.md +88 -0
  109. package/skills/continuous-learning-v2/SKILL.md +61 -0
  110. package/skills/continuous-learning-v2/agent-prompts/pattern-analyzer.md +51 -0
  111. package/skills/continuous-learning-v2/agent-prompts/skill-generator.md +74 -0
  112. package/skills/continuous-learning-v2/config.json +25 -0
  113. package/skills/continuous-learning-v2/hook-templates/evaluate-session.cjs.template +69 -0
  114. package/skills/continuous-learning-v2/hook-templates/observer-hook.cjs.template +54 -0
  115. package/skills/continuous-learning-v2/scripts/analyze-patterns.js +50 -0
  116. package/skills/continuous-learning-v2/scripts/extract-session-patterns.js +54 -0
  117. package/skills/continuous-learning-v2/scripts/validate-content.js +88 -0
  118. package/skills/native-module-scaffold/SKILL.md +118 -0
  119. package/skills/performance-optimization/SKILL.md +103 -0
  120. package/skills/security-review/SKILL.md +99 -0
  121. package/skills/tdd-workflow/SKILL.md +142 -0
  122. package/skills/upgrade-workflow/SKILL.md +140 -0
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ // scripts/validate-all.js — Validate all ERNE content files
3
+ // Checks: frontmatter format, JSON validity, required fields, file counts
4
+
5
+ 'use strict';
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ let errors = 0;
11
+ let warnings = 0;
12
+ let checked = 0;
13
+
14
+ function error(msg) { errors++; console.error(` ✗ ${msg}`); }
15
+ function warn(msg) { warnings++; console.warn(` ⚠ ${msg}`); }
16
+ function ok(msg) { console.log(` ✓ ${msg}`); }
17
+
18
+ // ─── Validate frontmatter in .md files ───
19
+ function validateFrontmatter(filePath, requiredFields) {
20
+ checked++;
21
+ const content = fs.readFileSync(filePath, 'utf8');
22
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
23
+
24
+ if (!match) {
25
+ error(`${filePath}: Missing frontmatter`);
26
+ return;
27
+ }
28
+
29
+ const frontmatter = match[1];
30
+ for (const field of requiredFields) {
31
+ if (!frontmatter.includes(`${field}:`)) {
32
+ error(`${filePath}: Missing required field '${field}'`);
33
+ }
34
+ }
35
+ }
36
+
37
+ // ─── Validate JSON files ───
38
+ function validateJson(filePath) {
39
+ checked++;
40
+ try {
41
+ const content = fs.readFileSync(filePath, 'utf8');
42
+ JSON.parse(content);
43
+ } catch (e) {
44
+ error(`${filePath}: Invalid JSON — ${e.message}`);
45
+ }
46
+ }
47
+
48
+ // ─── Validate directory file counts ───
49
+ function validateCount(dir, ext, expected, label) {
50
+ const files = fs.readdirSync(dir).filter(f => f.endsWith(ext));
51
+ if (files.length !== expected) {
52
+ error(`${label}: Expected ${expected} files, found ${files.length}`);
53
+ } else {
54
+ ok(`${label}: ${files.length} files`);
55
+ }
56
+ }
57
+
58
+ // ─── Main validation ───
59
+ console.log('\n ERNE Content Validation\n');
60
+
61
+ // Agents
62
+ console.log(' Agents:');
63
+ validateCount('agents', '.md', 8, 'agents/');
64
+ const agentFiles = fs.readdirSync('agents').filter(f => f.endsWith('.md'));
65
+ for (const f of agentFiles) {
66
+ validateFrontmatter(path.join('agents', f), ['name', 'description']);
67
+ }
68
+
69
+ // Commands
70
+ console.log(' Commands:');
71
+ validateCount('commands', '.md', 16, 'commands/');
72
+ const cmdFiles = fs.readdirSync('commands').filter(f => f.endsWith('.md'));
73
+ for (const f of cmdFiles) {
74
+ validateFrontmatter(path.join('commands', f), ['name', 'description']);
75
+ }
76
+
77
+ // Rules
78
+ console.log(' Rules:');
79
+ const ruleLayers = ['common', 'expo', 'bare-rn', 'native-ios', 'native-android'];
80
+ for (const layer of ruleLayers) {
81
+ const layerDir = path.join('rules', layer);
82
+ if (!fs.existsSync(layerDir)) {
83
+ error(`rules/${layer}/: Missing directory`);
84
+ continue;
85
+ }
86
+ const ruleFiles = fs.readdirSync(layerDir).filter(f => f.endsWith('.md'));
87
+ ok(`rules/${layer}/: ${ruleFiles.length} files`);
88
+ for (const f of ruleFiles) {
89
+ validateFrontmatter(path.join(layerDir, f), ['description']);
90
+ }
91
+ }
92
+
93
+ // Hook profiles
94
+ console.log(' Hooks:');
95
+ validateJson('hooks/hooks.json');
96
+ for (const profile of ['minimal', 'standard', 'strict']) {
97
+ validateJson(path.join('hooks', 'profiles', `${profile}.json`));
98
+ }
99
+
100
+ // MCP configs
101
+ console.log(' MCP Configs:');
102
+ const mcpFiles = fs.readdirSync('mcp-configs').filter(f => f.endsWith('.json'));
103
+ ok(`mcp-configs/: ${mcpFiles.length} files`);
104
+ for (const f of mcpFiles) {
105
+ validateJson(path.join('mcp-configs', f));
106
+ }
107
+
108
+ // Contexts
109
+ console.log(' Contexts:');
110
+ validateCount('contexts', '.md', 3, 'contexts/');
111
+
112
+ // Skills
113
+ console.log(' Skills:');
114
+ const skillDirs = fs.readdirSync('skills', { withFileTypes: true })
115
+ .filter(d => d.isDirectory())
116
+ .map(d => d.name);
117
+ ok(`skills/: ${skillDirs.length} skill directories`);
118
+ for (const dir of skillDirs) {
119
+ const skillMd = path.join('skills', dir, 'SKILL.md');
120
+ if (!fs.existsSync(skillMd)) {
121
+ error(`skills/${dir}/: Missing SKILL.md`);
122
+ } else {
123
+ checked++;
124
+ }
125
+ }
126
+
127
+ // Schemas
128
+ console.log(' Schemas:');
129
+ validateJson('schemas/hooks.schema.json');
130
+ validateJson('schemas/plugin.schema.json');
131
+
132
+ // Summary
133
+ console.log(`\n Checked ${checked} files: ${errors} errors, ${warnings} warnings\n`);
134
+
135
+ if (errors > 0) {
136
+ process.exit(1);
137
+ }
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: coding-standards
3
+ description: Enforce React Native coding standards as an actionable workflow
4
+ ---
5
+
6
+ # Coding Standards Enforcement
7
+
8
+ You are enforcing coding standards for a React Native project. This skill turns passive rules into an active audit workflow.
9
+
10
+ ## When to Use This Skill
11
+
12
+ Invoke when:
13
+ - Starting work on a new codebase
14
+ - Reviewing code for standards compliance
15
+ - Setting up a new project's conventions
16
+
17
+ ## Audit Process
18
+
19
+ ### Step 1: Detect Project Configuration
20
+
21
+ Read the project's `.claude/rules/` to understand which standards apply:
22
+ - `common/` rules always apply
23
+ - `expo/` rules if Expo managed project
24
+ - `bare-rn/` rules if bare React Native
25
+ - `native-ios/` if iOS native code present
26
+ - `native-android/` if Android native code present
27
+
28
+ ### Step 2: Scan for Violations
29
+
30
+ Check each category systematically:
31
+
32
+ **Component Structure:**
33
+ - [ ] Functional components only (no class components)
34
+ - [ ] Named exports (not default exports)
35
+ - [ ] Props interface defined above component
36
+ - [ ] Proper TypeScript types (no `any`)
37
+
38
+ **Styling:**
39
+ - [ ] NativeWind/Tailwind classes used (no `StyleSheet.create`)
40
+ - [ ] Consistent color usage (design tokens, not hex literals)
41
+ - [ ] Responsive design using NativeWind breakpoints
42
+ - [ ] Dark mode support via `dark:` variants
43
+
44
+ **State Management:**
45
+ - [ ] Zustand for client state (no Redux, no Context for global state)
46
+ - [ ] TanStack Query for server state (no manual fetch+useState)
47
+ - [ ] No prop drilling beyond 2 levels
48
+ - [ ] Computed values derived, not stored
49
+
50
+ **Navigation:**
51
+ - [ ] Expo Router file-based routing
52
+ - [ ] Typed routes using `href` type safety
53
+ - [ ] Proper layout files (`_layout.tsx`)
54
+ - [ ] Deep linking configured
55
+
56
+ **Performance:**
57
+ - [ ] `FlashList` for lists (not `FlatList`)
58
+ - [ ] `expo-image` for images (not `Image`)
59
+ - [ ] Memoization where appropriate (`useMemo`, `useCallback`)
60
+ - [ ] Animations on UI thread (Reanimated worklets)
61
+
62
+ **Testing:**
63
+ - [ ] Tests exist for new code
64
+ - [ ] Tests use RNTL (not Enzyme)
65
+ - [ ] Tests test behavior, not implementation
66
+ - [ ] Mock at boundaries only
67
+
68
+ ### Step 3: Generate Report
69
+
70
+ ```
71
+ ## Coding Standards Audit
72
+
73
+ ### Summary
74
+ - Files scanned: N
75
+ - Violations found: N
76
+ - Auto-fixable: N
77
+
78
+ ### Violations by Category
79
+ [Category]: [count]
80
+ - [file:line] — [description]
81
+
82
+ ### Recommendations
83
+ [Prioritized list of fixes]
84
+ ```
85
+
86
+ ### Step 4: Apply Fixes
87
+
88
+ For auto-fixable violations (imports, styling patterns), offer to fix them. For manual fixes (architecture changes), provide specific guidance.
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: continuous-learning-v2
3
+ description: Auto-generate skills and rules from observed React Native development patterns
4
+ ---
5
+
6
+ # Continuous Learning v2
7
+
8
+ This skill manages the continuous learning pipeline — observing patterns during development sessions and converting them into persistent rules and skills.
9
+
10
+ ## Architecture
11
+
12
+ ```
13
+ PostToolUse hook (real-time)
14
+ → continuous-learning-observer.cjs (lightweight pattern capture)
15
+ → patterns stored in .claude/memory/observations/
16
+
17
+ /learn command (manual, comprehensive)
18
+ → extract-session-patterns.js (full session analysis)
19
+ → analyze-patterns.js (pattern clustering + dedup)
20
+ → skill-generator prompt (create new content)
21
+ → validate-content.js (verify new content is valid)
22
+
23
+ /retrospective command (session end)
24
+ → evaluate-session.js (quality metrics + suggestions)
25
+ ```
26
+
27
+ ## How It Works
28
+
29
+ ### Real-Time (Automatic)
30
+
31
+ The `continuous-learning-observer.cjs` hook runs on `PostToolUse` events. It:
32
+ 1. Captures the tool name, file paths, and outcome
33
+ 2. Detects repeated patterns (same fix applied > 3 times)
34
+ 3. Stores observations in `.claude/memory/observations/` as JSON
35
+ 4. Lightweight — adds < 50ms to each tool call
36
+
37
+ ### Manual Analysis (`/learn`)
38
+
39
+ When the user runs `/learn`, the pipeline:
40
+ 1. Reads all observations from the current session
41
+ 2. Clusters them by type (style fix, import pattern, architecture choice)
42
+ 3. Compares against existing rules and skills
43
+ 4. Generates candidates for new content
44
+ 5. Presents candidates for user approval
45
+ 6. Writes approved content to `.claude/rules/` or `.claude/skills/`
46
+
47
+ ### Session Evaluation (`/retrospective`)
48
+
49
+ At session end, `evaluate-session.js`:
50
+ 1. Aggregates all metrics (files changed, tests added, build status)
51
+ 2. Evaluates which rules triggered and their usefulness
52
+ 3. Suggests rule calibration (tighten/loosen globs, adjust content)
53
+ 4. Generates a session quality report
54
+
55
+ ## Configuration
56
+
57
+ See `config.json` for tuning parameters:
58
+ - `observationThreshold`: How many times a pattern must repeat before flagging (default: 3)
59
+ - `maxObservationsPerSession`: Prevent memory bloat (default: 100)
60
+ - `autoApprove`: If true, auto-approve low-risk content (default: false)
61
+ - `contentTypes`: What types to generate — `["rule", "skill"]`
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: pattern-analyzer
3
+ description: Internal prompt for analyzing collected patterns — NOT a top-level agent
4
+ ---
5
+
6
+ # Pattern Analysis Prompt
7
+
8
+ You are analyzing patterns observed during a React Native development session. Your job is to identify recurring patterns worth capturing as rules or skills.
9
+
10
+ ## Input
11
+
12
+ You receive a JSON array of observations:
13
+ ```json
14
+ [
15
+ {
16
+ "timestamp": "2024-03-10T14:30:00Z",
17
+ "tool": "Edit",
18
+ "files": ["src/components/Button.tsx"],
19
+ "pattern": "Replaced StyleSheet.create with NativeWind classes",
20
+ "count": 5
21
+ }
22
+ ]
23
+ ```
24
+
25
+ ## Analysis Process
26
+
27
+ 1. **Group by category** — Cluster similar observations
28
+ 2. **Filter by threshold** — Only patterns with 3+ occurrences
29
+ 3. **Check novelty** — Compare against existing rules in `.claude/rules/`
30
+ 4. **Assess value** — Is this pattern worth encoding as a rule?
31
+
32
+ ## Output
33
+
34
+ For each candidate:
35
+ ```json
36
+ {
37
+ "type": "rule",
38
+ "category": "common/coding-style",
39
+ "title": "Prefer NativeWind over StyleSheet",
40
+ "confidence": "high",
41
+ "occurrences": 5,
42
+ "suggestedContent": "..."
43
+ }
44
+ ```
45
+
46
+ ## Rules for Analysis
47
+
48
+ - High confidence: Pattern appeared 5+ times with same fix
49
+ - Medium confidence: Pattern appeared 3-4 times
50
+ - Low confidence: Pattern appeared but context varied
51
+ - Skip: One-off patterns, project-specific hacks, temporary workarounds
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: skill-generator
3
+ description: Internal prompt for generating new skill content from analyzed patterns — NOT a top-level agent
4
+ ---
5
+
6
+ # Skill/Rule Generation Prompt
7
+
8
+ You are generating a new Claude Code rule or skill from an analyzed pattern. Create content that follows ERNE conventions.
9
+
10
+ ## Input
11
+
12
+ You receive a pattern analysis:
13
+ ```json
14
+ {
15
+ "type": "rule",
16
+ "category": "common/coding-style",
17
+ "title": "Prefer NativeWind over StyleSheet",
18
+ "confidence": "high",
19
+ "occurrences": 5,
20
+ "examples": ["..."]
21
+ }
22
+ ```
23
+
24
+ ## Generation Rules
25
+
26
+ ### For Rules (`.claude/rules/*.md`)
27
+
28
+ Format:
29
+ ```markdown
30
+ ---
31
+ description: [One-line description of what this rule enforces]
32
+ globs: [File patterns this applies to, e.g., "src/**/*.tsx"]
33
+ alwaysApply: false
34
+ ---
35
+
36
+ # [Rule Title]
37
+
38
+ [Clear statement of the rule]
39
+
40
+ ## Do This
41
+ [Correct example with code]
42
+
43
+ ## Don't Do This
44
+ [Incorrect example with code]
45
+
46
+ ## Why
47
+ [Brief rationale]
48
+ ```
49
+
50
+ ### For Skills (`.claude/skills/*/SKILL.md`)
51
+
52
+ Format:
53
+ ```markdown
54
+ ---
55
+ name: [kebab-case-name]
56
+ description: [One-line description]
57
+ ---
58
+
59
+ # [Skill Title]
60
+
61
+ [When to invoke]
62
+ [Step-by-step workflow]
63
+ [Examples]
64
+ [Expected output]
65
+ ```
66
+
67
+ ## Quality Checks
68
+
69
+ Before outputting:
70
+ - [ ] Frontmatter is valid YAML
71
+ - [ ] Content is specific and actionable (not generic advice)
72
+ - [ ] Code examples are correct and runnable
73
+ - [ ] Rule doesn't conflict with existing rules
74
+ - [ ] Skill has clear invocation criteria
@@ -0,0 +1,25 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "observationThreshold": 3,
4
+ "maxObservationsPerSession": 100,
5
+ "autoApprove": false,
6
+ "contentTypes": ["rule", "skill"],
7
+ "observationDir": ".claude/memory/observations",
8
+ "generatedDir": ".claude/memory/generated",
9
+ "patterns": {
10
+ "minOccurrences": 3,
11
+ "categories": [
12
+ "style-fix",
13
+ "import-pattern",
14
+ "architecture-choice",
15
+ "error-handling",
16
+ "testing-pattern",
17
+ "performance-fix"
18
+ ]
19
+ },
20
+ "generation": {
21
+ "ruleTemplate": "agent-prompts/skill-generator.md",
22
+ "analyzerPrompt": "agent-prompts/pattern-analyzer.md",
23
+ "validationScript": "scripts/validate-content.js"
24
+ }
25
+ }
@@ -0,0 +1,69 @@
1
+ // Template for evaluate-session.cjs hook
2
+ // This runs on session end to generate a retrospective
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const CONFIG = {
8
+ observationDir: '{{observationDir}}',
9
+ generatedDir: '{{generatedDir}}',
10
+ };
11
+
12
+ module.exports = async () => {
13
+ // Read all session observations
14
+ const obsDir = CONFIG.observationDir;
15
+ if (!fs.existsSync(obsDir)) {
16
+ return { message: 'No observations found for this session.' };
17
+ }
18
+
19
+ const sessionFiles = fs.readdirSync(obsDir)
20
+ .filter(f => f.startsWith('session-'))
21
+ .sort();
22
+
23
+ if (sessionFiles.length === 0) {
24
+ return { message: 'No observations found for this session.' };
25
+ }
26
+
27
+ // Aggregate observations
28
+ let allObservations = [];
29
+ for (const file of sessionFiles) {
30
+ try {
31
+ const data = JSON.parse(fs.readFileSync(path.join(obsDir, file), 'utf8'));
32
+ allObservations = allObservations.concat(data);
33
+ } catch { /* skip corrupt files */ }
34
+ }
35
+
36
+ // Generate metrics
37
+ const metrics = {
38
+ totalObservations: allObservations.length,
39
+ toolUsage: {},
40
+ filesModified: new Set(),
41
+ timespan: {
42
+ start: allObservations[0]?.timestamp,
43
+ end: allObservations[allObservations.length - 1]?.timestamp,
44
+ },
45
+ };
46
+
47
+ for (const obs of allObservations) {
48
+ metrics.toolUsage[obs.tool] = (metrics.toolUsage[obs.tool] || 0) + 1;
49
+ (obs.files || []).forEach(f => metrics.filesModified.add(f));
50
+ }
51
+
52
+ metrics.filesModified = [...metrics.filesModified];
53
+
54
+ // Write retrospective
55
+ const retro = {
56
+ timestamp: new Date().toISOString(),
57
+ metrics,
58
+ observations: allObservations,
59
+ };
60
+
61
+ fs.mkdirSync(CONFIG.generatedDir, { recursive: true });
62
+ const retroFile = path.join(CONFIG.generatedDir, `retro-${Date.now()}.json`);
63
+ fs.writeFileSync(retroFile, JSON.stringify(retro, null, 2));
64
+
65
+ return {
66
+ message: `Retrospective saved to ${retroFile}`,
67
+ metrics,
68
+ };
69
+ };
@@ -0,0 +1,54 @@
1
+ // Template for continuous-learning-observer.cjs hook
2
+ // This runs on PostToolUse events and captures patterns
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const CONFIG = {
8
+ observationDir: '{{observationDir}}',
9
+ maxPerSession: {{maxObservationsPerSession}},
10
+ threshold: {{observationThreshold}},
11
+ };
12
+
13
+ module.exports = async ({ tool, filePaths, result }) => {
14
+ // Only observe file-editing tools
15
+ if (!['Edit', 'Write', 'NotebookEdit'].includes(tool)) return;
16
+
17
+ const sessionFile = path.join(CONFIG.observationDir, `session-${Date.now()}.json`);
18
+
19
+ // Ensure observation directory exists
20
+ fs.mkdirSync(CONFIG.observationDir, { recursive: true });
21
+
22
+ // Read existing observations for this session
23
+ let observations = [];
24
+ const sessionFiles = fs.readdirSync(CONFIG.observationDir)
25
+ .filter(f => f.startsWith('session-'))
26
+ .sort()
27
+ .slice(-1);
28
+
29
+ if (sessionFiles.length > 0) {
30
+ try {
31
+ observations = JSON.parse(
32
+ fs.readFileSync(path.join(CONFIG.observationDir, sessionFiles[0]), 'utf8')
33
+ );
34
+ } catch { /* fresh session */ }
35
+ }
36
+
37
+ // Don't exceed max observations
38
+ if (observations.length >= CONFIG.maxPerSession) return;
39
+
40
+ // Record observation
41
+ observations.push({
42
+ timestamp: new Date().toISOString(),
43
+ tool,
44
+ files: filePaths || [],
45
+ resultPreview: typeof result === 'string' ? result.slice(0, 200) : '',
46
+ });
47
+
48
+ // Write back
49
+ const targetFile = sessionFiles.length > 0
50
+ ? path.join(CONFIG.observationDir, sessionFiles[0])
51
+ : sessionFile;
52
+
53
+ fs.writeFileSync(targetFile, JSON.stringify(observations, null, 2));
54
+ };
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ // Analyze extracted patterns and generate candidates
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const CONFIG_PATH = path.resolve(__dirname, '../config.json');
8
+ const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
9
+
10
+ function analyzePatterns(patterns) {
11
+ const candidates = [];
12
+
13
+ // Find repeated edits to same file patterns
14
+ const filePatterns = {};
15
+ for (const obs of patterns) {
16
+ for (const file of (obs.files || [])) {
17
+ const dir = path.dirname(file);
18
+ const ext = path.extname(file);
19
+ const key = `${dir}/*${ext}`;
20
+ if (!filePatterns[key]) filePatterns[key] = { count: 0, files: [] };
21
+ filePatterns[key].count++;
22
+ filePatterns[key].files.push(file);
23
+ }
24
+ }
25
+
26
+ // Generate candidates for patterns above threshold
27
+ for (const [pattern, data] of Object.entries(filePatterns)) {
28
+ if (data.count >= config.observationThreshold) {
29
+ candidates.push({
30
+ type: 'rule',
31
+ pattern,
32
+ occurrences: data.count,
33
+ files: [...new Set(data.files)].slice(0, 5),
34
+ confidence: data.count >= 5 ? 'high' : 'medium',
35
+ });
36
+ }
37
+ }
38
+
39
+ return candidates;
40
+ }
41
+
42
+ // Read from stdin or file
43
+ const input = process.argv[2];
44
+ if (input && fs.existsSync(input)) {
45
+ const data = JSON.parse(fs.readFileSync(input, 'utf8'));
46
+ const candidates = analyzePatterns(data);
47
+ console.log(JSON.stringify(candidates, null, 2));
48
+ } else {
49
+ console.log('Usage: node analyze-patterns.js <observations.json>');
50
+ }
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ // Extract patterns from session observations for /learn command
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const OBS_DIR = path.resolve('.claude/memory/observations');
8
+
9
+ function extractPatterns() {
10
+ if (!fs.existsSync(OBS_DIR)) {
11
+ console.log('No observations directory found.');
12
+ process.exit(0);
13
+ }
14
+
15
+ const files = fs.readdirSync(OBS_DIR).filter(f => f.endsWith('.json'));
16
+ let allObs = [];
17
+
18
+ for (const file of files) {
19
+ try {
20
+ const data = JSON.parse(fs.readFileSync(path.join(OBS_DIR, file), 'utf8'));
21
+ allObs = allObs.concat(Array.isArray(data) ? data : [data]);
22
+ } catch (e) {
23
+ console.error(`Skipping corrupt file: ${file}`);
24
+ }
25
+ }
26
+
27
+ console.log(`Loaded ${allObs.length} observations from ${files.length} files.`);
28
+
29
+ // Group by file extension to find patterns
30
+ const byExtension = {};
31
+ for (const obs of allObs) {
32
+ for (const file of (obs.files || [])) {
33
+ const ext = path.extname(file);
34
+ if (!byExtension[ext]) byExtension[ext] = [];
35
+ byExtension[ext].push(obs);
36
+ }
37
+ }
38
+
39
+ // Group by tool to find repeated fix patterns
40
+ const byTool = {};
41
+ for (const obs of allObs) {
42
+ if (!byTool[obs.tool]) byTool[obs.tool] = [];
43
+ byTool[obs.tool].push(obs);
44
+ }
45
+
46
+ // Output summary
47
+ console.log('\n=== Pattern Summary ===');
48
+ console.log(`File types modified: ${Object.keys(byExtension).join(', ')}`);
49
+ console.log(`Tools used: ${JSON.stringify(byTool, (k, v) => Array.isArray(v) ? v.length : v)}`);
50
+
51
+ return { byExtension, byTool, total: allObs.length };
52
+ }
53
+
54
+ extractPatterns();