@spilno/herald-mcp 1.26.0 → 1.27.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.
- package/README.md +111 -105
- package/dist/cli/init.d.ts +0 -2
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +39 -89
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/templates/claude-md.d.ts +1 -3
- package/dist/cli/templates/claude-md.d.ts.map +1 -1
- package/dist/cli/templates/claude-md.js +11 -42
- package/dist/cli/templates/claude-md.js.map +1 -1
- package/dist/index.js +98 -423
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/cli/templates/hookify-rules.d.ts +0 -22
- package/dist/cli/templates/hookify-rules.d.ts.map +0 -1
- package/dist/cli/templates/hookify-rules.js +0 -57
- package/dist/cli/templates/hookify-rules.js.map +0 -1
- package/dist/sanitization.d.ts +0 -68
- package/dist/sanitization.d.ts.map +0 -1
- package/dist/sanitization.js +0 -236
- package/dist/sanitization.js.map +0 -1
- package/dist/sanitization.spec.d.ts +0 -2
- package/dist/sanitization.spec.d.ts.map +0 -1
- package/dist/sanitization.spec.js +0 -90
- package/dist/sanitization.spec.js.map +0 -1
package/README.md
CHANGED
|
@@ -18,153 +18,159 @@ AI agents start fresh each session. Herald gives them memory:
|
|
|
18
18
|
## Quick Start
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
|
|
21
|
+
# One command setup for Claude Desktop
|
|
22
22
|
npx @spilno/herald-mcp init
|
|
23
|
+
|
|
24
|
+
# Or install globally
|
|
25
|
+
npm install -g @spilno/herald-mcp
|
|
26
|
+
herald-mcp init
|
|
23
27
|
```
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
3. Creates/updates `CLAUDE.md` with patterns baked in
|
|
29
|
+
Done. Herald is now available to Claude.
|
|
30
|
+
|
|
31
|
+
## Core Tools
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
| Tool | Purpose |
|
|
34
|
+
|------|---------|
|
|
35
|
+
| `herald_predict` | Generate structure from natural language |
|
|
36
|
+
| `herald_refine` | Refine predictions with feedback |
|
|
37
|
+
| `herald_patterns` | Query what worked before |
|
|
38
|
+
| `herald_reflect` | Capture patterns and antipatterns |
|
|
39
|
+
| `herald_feedback` | Reinforce helpful patterns |
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
### Pattern Memory
|
|
33
42
|
|
|
34
|
-
```bash
|
|
35
|
-
npx @spilno/herald-mcp init [options]
|
|
36
43
|
```
|
|
44
|
+
AI: herald_patterns()
|
|
45
|
+
→ Returns: patterns that worked, antipatterns to avoid
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
| `--sync`, `-s` | Just sync patterns to CLAUDE.md (quick update) |
|
|
41
|
-
| `--hookify` | Generate hookify rules for auto pattern reminders |
|
|
42
|
-
| `--company`, `-c` | Override company (default: folder name) |
|
|
43
|
-
| `--project`, `-p` | Override project (default: folder name) |
|
|
44
|
-
| `--user`, `-u` | Override user (default: "default") |
|
|
45
|
-
| `--force`, `-f` | Overwrite existing config |
|
|
46
|
-
| `--help`, `-h` | Show help |
|
|
47
|
-
|
|
48
|
-
**Examples:**
|
|
49
|
-
```bash
|
|
50
|
-
# Basic setup (zero config)
|
|
51
|
-
npx @spilno/herald-mcp init
|
|
47
|
+
AI: herald_predict("create safety assessment module")
|
|
48
|
+
→ Returns: structured prediction based on accumulated patterns
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
User: "That worked well"
|
|
51
|
+
AI: herald_reflect(feeling="success", insight="field grouping approach")
|
|
52
|
+
→ Pattern captured, weight increased
|
|
53
|
+
```
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
npx @spilno/herald-mcp init --hookify
|
|
55
|
+
## Session Flow
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
```bash
|
|
58
|
+
# Start a session
|
|
59
|
+
herald-mcp predict "create incident report module"
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
# Refine iteratively
|
|
62
|
+
herald-mcp refine "add witness section"
|
|
63
|
+
herald-mcp refine "require photos for severity > 3"
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
# Accept when satisfied
|
|
66
|
+
herald-mcp observe yes
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
↓ inherits from
|
|
70
|
-
project (team patterns)
|
|
71
|
-
↓ inherits from
|
|
72
|
-
company (org-wide patterns)
|
|
68
|
+
# Resume anytime
|
|
69
|
+
herald-mcp resume
|
|
73
70
|
```
|
|
74
71
|
|
|
75
|
-
|
|
72
|
+
## Configuration
|
|
76
73
|
|
|
77
|
-
|
|
74
|
+
### Claude Desktop / Claude Code
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"herald": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["@spilno/herald-mcp"],
|
|
82
|
+
"env": {
|
|
83
|
+
"HERALD_API_URL": "https://getceda.com"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
78
89
|
|
|
79
|
-
|
|
90
|
+
### Environment Variables
|
|
80
91
|
|
|
81
|
-
|
|
|
82
|
-
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
92
|
+
| Variable | Required | Description |
|
|
93
|
+
|----------|----------|-------------|
|
|
94
|
+
| `HERALD_API_URL` | Yes | CEDA server (default: https://getceda.com) |
|
|
95
|
+
| `HERALD_COMPANY` | No | Multi-tenant company context |
|
|
96
|
+
| `HERALD_PROJECT` | No | Project context |
|
|
97
|
+
| `HERALD_USER` | No | User context |
|
|
85
98
|
|
|
86
|
-
##
|
|
99
|
+
## Multi-Tenant Isolation
|
|
87
100
|
|
|
88
|
-
|
|
89
|
-
|------|---------|
|
|
90
|
-
| `herald_patterns` | Query what worked before (with inheritance) |
|
|
91
|
-
| `herald_reflect` | Capture patterns and antipatterns |
|
|
92
|
-
| `herald_predict` | Generate structure from natural language |
|
|
93
|
-
| `herald_refine` | Refine predictions with feedback |
|
|
94
|
-
| `herald_feedback` | Reinforce helpful patterns |
|
|
95
|
-
|
|
96
|
-
### Pattern Capture
|
|
97
|
-
|
|
98
|
-
When something works or fails, capture it:
|
|
101
|
+
Patterns are isolated by context:
|
|
99
102
|
|
|
100
103
|
```
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
User: "The ASCII visualization approach"
|
|
104
|
-
→ Pattern captured, available in future sessions
|
|
104
|
+
Company A patterns → Only visible to Company A
|
|
105
|
+
Project X patterns → Only visible to Project X users
|
|
105
106
|
```
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
→ Antipattern captured, Claude will avoid this
|
|
108
|
+
Set context via environment or headers:
|
|
109
|
+
```bash
|
|
110
|
+
export HERALD_COMPANY=acme
|
|
111
|
+
export HERALD_PROJECT=safety-modules
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
##
|
|
114
|
+
## Herald Context Sync
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
Herald instances share insights across contexts:
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
| Tool | Purpose |
|
|
119
|
+
|------|---------|
|
|
120
|
+
| `herald_context_status` | Check status of Herald instances |
|
|
121
|
+
| `herald_share_insight` | Share pattern to other contexts |
|
|
122
|
+
| `herald_query_insights` | Query shared insights |
|
|
123
|
+
| `herald_sync` | Flush local buffer to cloud |
|
|
121
124
|
|
|
122
|
-
|
|
123
|
-
- **On prompt**: Remind to check patterns at session start
|
|
124
|
-
- **On session end**: Remind to capture patterns before leaving
|
|
125
|
+
## Chat Mode
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
For humans who prefer conversation:
|
|
127
128
|
|
|
128
|
-
|
|
129
|
+
```bash
|
|
130
|
+
herald-mcp chat
|
|
131
|
+
```
|
|
129
132
|
|
|
130
|
-
|
|
133
|
+
```
|
|
134
|
+
You: I need a permit-to-work module
|
|
135
|
+
Herald: I've designed a Permit-to-Work module with 5 sections...
|
|
131
136
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| `.mcp.json` | MCP server configuration for Claude Code |
|
|
135
|
-
| `CLAUDE.md` | Project instructions with baked patterns |
|
|
136
|
-
| `.claude/hookify.*.local.md` | Auto-reminder rules (if --hookify) |
|
|
137
|
+
You: Add gas testing checklist
|
|
138
|
+
Herald: Added gas testing to Pre-Work Safety section...
|
|
137
139
|
|
|
138
|
-
|
|
140
|
+
You: Perfect
|
|
141
|
+
Herald: Module accepted and saved.
|
|
142
|
+
```
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
|----------|---------|-------------|
|
|
142
|
-
| `CEDA_URL` | https://getceda.com | CEDA backend URL |
|
|
143
|
-
| `HERALD_COMPANY` | folder name | Company context |
|
|
144
|
-
| `HERALD_PROJECT` | folder name | Project context |
|
|
145
|
-
| `HERALD_USER` | "default" | User context |
|
|
144
|
+
## Command Reference
|
|
146
145
|
|
|
147
|
-
|
|
146
|
+
```bash
|
|
147
|
+
herald-mcp init # Setup for Claude Desktop
|
|
148
|
+
herald-mcp chat # Interactive conversation mode
|
|
149
|
+
herald-mcp predict <signal> # Generate prediction
|
|
150
|
+
herald-mcp refine <signal> # Refine current prediction
|
|
151
|
+
herald-mcp resume # Resume last session
|
|
152
|
+
herald-mcp observe <yes|no> # Accept or reject prediction
|
|
153
|
+
herald-mcp new # Start fresh session
|
|
154
|
+
herald-mcp health # Check CEDA connection
|
|
155
|
+
herald-mcp stats # Show loaded patterns
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Architecture
|
|
148
159
|
|
|
149
160
|
```
|
|
150
161
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
151
|
-
│
|
|
152
|
-
│
|
|
153
|
-
│
|
|
162
|
+
│ AI Agent │────▶│ Herald │────▶│ CEDA │
|
|
163
|
+
│ (Claude/ │ │ (MCP) │ │ (Pattern │
|
|
164
|
+
│ Devin/etc) │◀────│ │◀────│ Memory) │
|
|
154
165
|
└─────────────┘ └─────────────┘ └─────────────┘
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
│
|
|
167
|
+
┌──────┴──────┐
|
|
168
|
+
│ Patterns │
|
|
169
|
+
│ Antipatterns│
|
|
170
|
+
│ Feedback │
|
|
171
|
+
└─────────────┘
|
|
161
172
|
```
|
|
162
173
|
|
|
163
|
-
1. **Session Start**: Claude reads `herald://patterns` resource
|
|
164
|
-
2. **During Work**: Patterns guide behavior
|
|
165
|
-
3. **Session End**: Capture new patterns with `herald_reflect`
|
|
166
|
-
4. **Next Session**: New patterns available automatically
|
|
167
|
-
|
|
168
174
|
## What is CEDA?
|
|
169
175
|
|
|
170
176
|
CEDA (Cognitive Event-Driven Architecture) is pattern memory for AI:
|
|
@@ -187,4 +193,4 @@ MIT
|
|
|
187
193
|
|
|
188
194
|
---
|
|
189
195
|
|
|
190
|
-
*Herald v1.
|
|
196
|
+
*Herald v1.20.0 — Pattern memory for AI agents*
|
package/dist/cli/init.d.ts
CHANGED
|
@@ -14,8 +14,6 @@ export interface InitOptions {
|
|
|
14
14
|
project?: string;
|
|
15
15
|
user?: string;
|
|
16
16
|
noClaudeMd?: boolean;
|
|
17
|
-
sync?: boolean;
|
|
18
|
-
hookify?: boolean;
|
|
19
17
|
}
|
|
20
18
|
export declare function parseInitArgs(args: string[]): InitOptions;
|
|
21
19
|
export declare function runInit(args?: string[]): Promise<void>;
|
package/dist/cli/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAoDH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,CAqBzD;AAED,wBAAsB,OAAO,CAAC,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA+HhE"}
|
package/dist/cli/init.js
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
11
11
|
import { join, basename } from "path";
|
|
12
12
|
import { updateClaudeMdContent, fetchLearnedPatterns } from "./templates/claude-md.js";
|
|
13
|
-
|
|
14
|
-
function buildHeraldConfig(company, project) {
|
|
13
|
+
function buildHeraldConfig(company, project, user) {
|
|
15
14
|
return {
|
|
16
15
|
mcpServers: {
|
|
17
16
|
herald: {
|
|
@@ -20,10 +19,13 @@ function buildHeraldConfig(company, project) {
|
|
|
20
19
|
env: {
|
|
21
20
|
CEDA_URL: "https://getceda.com",
|
|
22
21
|
HERALD_COMPANY: company,
|
|
23
|
-
HERALD_PROJECT: project
|
|
22
|
+
HERALD_PROJECT: project,
|
|
23
|
+
HERALD_USER: user
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
}
|
|
26
|
+
},
|
|
27
|
+
// Required for project-level MCP servers to load properly
|
|
28
|
+
enableAllProjectMcpServers: true
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
function printInitHelp() {
|
|
@@ -36,21 +38,18 @@ Usage:
|
|
|
36
38
|
|
|
37
39
|
That's it. Company and project default to folder name.
|
|
38
40
|
|
|
39
|
-
Options:
|
|
40
|
-
--sync, -s Sync patterns to CLAUDE.md (quick update, no full init)
|
|
41
|
-
--hookify Generate hookify rules for auto pattern reminders
|
|
41
|
+
Options (optional):
|
|
42
42
|
--company, -c Override company (default: folder name)
|
|
43
43
|
--project, -p Override project (default: folder name)
|
|
44
|
-
--user, -u Override user (default:
|
|
45
|
-
--force, -f
|
|
44
|
+
--user, -u Override user (default: system username)
|
|
45
|
+
--force, -f Clean slate - overwrite entire config (not merge)
|
|
46
46
|
--no-claude-md Skip CLAUDE.md modification
|
|
47
47
|
--help, -h Show this help
|
|
48
48
|
|
|
49
49
|
Examples:
|
|
50
50
|
npx @spilno/herald-mcp@latest init
|
|
51
|
-
npx @spilno/herald-mcp@latest init --sync # Just sync patterns
|
|
52
|
-
npx @spilno/herald-mcp@latest init --hookify # Add auto-reminders
|
|
53
51
|
npx @spilno/herald-mcp@latest init --company goprint
|
|
52
|
+
npx @spilno/herald-mcp@latest init --company goprint --project kiosk
|
|
54
53
|
|
|
55
54
|
Then start Claude Code and say "herald health" to verify.
|
|
56
55
|
`);
|
|
@@ -77,51 +76,9 @@ export function parseInitArgs(args) {
|
|
|
77
76
|
else if (arg === "--no-claude-md") {
|
|
78
77
|
options.noClaudeMd = true;
|
|
79
78
|
}
|
|
80
|
-
else if (arg === "--sync" || arg === "-s") {
|
|
81
|
-
options.sync = true;
|
|
82
|
-
}
|
|
83
|
-
else if (arg === "--hookify") {
|
|
84
|
-
options.hookify = true;
|
|
85
|
-
}
|
|
86
79
|
}
|
|
87
80
|
return options;
|
|
88
81
|
}
|
|
89
|
-
async function runSyncPatterns(cwd, claudeMdPath, options) {
|
|
90
|
-
const projectName = basename(cwd);
|
|
91
|
-
const mcpJsonPath = join(cwd, ".mcp.json");
|
|
92
|
-
// Try to get context from .mcp.json, fall back to folder name
|
|
93
|
-
let company = options.company || projectName;
|
|
94
|
-
let project = options.project || projectName;
|
|
95
|
-
let user = options.user || "default";
|
|
96
|
-
if (existsSync(mcpJsonPath)) {
|
|
97
|
-
try {
|
|
98
|
-
const mcpConfig = JSON.parse(readFileSync(mcpJsonPath, "utf-8"));
|
|
99
|
-
const heraldEnv = mcpConfig.mcpServers?.herald?.env || {};
|
|
100
|
-
company = options.company || heraldEnv.HERALD_COMPANY || projectName;
|
|
101
|
-
project = options.project || heraldEnv.HERALD_PROJECT || projectName;
|
|
102
|
-
user = options.user || heraldEnv.HERALD_USER || "default";
|
|
103
|
-
}
|
|
104
|
-
catch { /* ignore */ }
|
|
105
|
-
}
|
|
106
|
-
const context = { company, project, user };
|
|
107
|
-
const cedaUrl = "https://getceda.com";
|
|
108
|
-
console.log(`Syncing patterns for ${user}→${project}→${company}...`);
|
|
109
|
-
const learnedPatterns = await fetchLearnedPatterns(cedaUrl, company, project, user);
|
|
110
|
-
if (!learnedPatterns) {
|
|
111
|
-
console.log("Failed to fetch patterns from CEDA");
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const totalPatterns = learnedPatterns.patterns.length + learnedPatterns.antipatterns.length;
|
|
115
|
-
console.log(`Found ${totalPatterns} patterns (${learnedPatterns.patterns.length} success, ${learnedPatterns.antipatterns.length} antipatterns)`);
|
|
116
|
-
let existingClaudeMd = null;
|
|
117
|
-
if (existsSync(claudeMdPath)) {
|
|
118
|
-
existingClaudeMd = readFileSync(claudeMdPath, "utf-8");
|
|
119
|
-
}
|
|
120
|
-
const updatedClaudeMd = updateClaudeMdContent(existingClaudeMd, context, projectName, learnedPatterns);
|
|
121
|
-
writeFileSync(claudeMdPath, updatedClaudeMd, "utf-8");
|
|
122
|
-
console.log(`✓ CLAUDE.md updated with ${totalPatterns} patterns`);
|
|
123
|
-
console.log(`\nPatterns are now baked into CLAUDE.md for offline access.`);
|
|
124
|
-
}
|
|
125
82
|
export async function runInit(args = []) {
|
|
126
83
|
const options = parseInitArgs(args);
|
|
127
84
|
if (options.help) {
|
|
@@ -130,12 +87,9 @@ export async function runInit(args = []) {
|
|
|
130
87
|
}
|
|
131
88
|
const cwd = process.cwd();
|
|
132
89
|
const projectName = basename(cwd);
|
|
133
|
-
const
|
|
90
|
+
const claudeDir = join(cwd, ".claude");
|
|
91
|
+
const settingsPath = join(claudeDir, "settings.local.json");
|
|
134
92
|
const claudeMdPath = join(cwd, "CLAUDE.md");
|
|
135
|
-
// Quick sync mode: just update CLAUDE.md with latest patterns
|
|
136
|
-
if (options.sync) {
|
|
137
|
-
return runSyncPatterns(cwd, claudeMdPath, options);
|
|
138
|
-
}
|
|
139
93
|
// Zero-config: derive from folder name, flags override
|
|
140
94
|
const company = options.company || projectName;
|
|
141
95
|
const project = options.project || projectName;
|
|
@@ -144,52 +98,62 @@ export async function runInit(args = []) {
|
|
|
144
98
|
project,
|
|
145
99
|
user: options.user || "default",
|
|
146
100
|
};
|
|
147
|
-
// Check for old herald configs and warn
|
|
148
|
-
const
|
|
149
|
-
const oldSettingsPath = join(oldClaudeDir, "settings.local.json");
|
|
101
|
+
// Check for old herald configs and warn
|
|
102
|
+
const oldSettingsPath = join(claudeDir, "settings.json");
|
|
150
103
|
if (existsSync(oldSettingsPath)) {
|
|
151
104
|
try {
|
|
152
105
|
const oldConfig = JSON.parse(readFileSync(oldSettingsPath, "utf-8"));
|
|
153
106
|
if (oldConfig.mcpServers?.herald) {
|
|
154
|
-
console.log("⚠️ Found old Herald config in .
|
|
155
|
-
console.log(" This location is no longer supported. Migrating to .mcp.json");
|
|
107
|
+
console.log("⚠️ Found old Herald config in settings.json - will use settings.local.json instead");
|
|
156
108
|
}
|
|
157
109
|
}
|
|
158
110
|
catch { /* ignore */ }
|
|
159
111
|
}
|
|
160
|
-
if (existsSync(
|
|
112
|
+
if (existsSync(settingsPath) && !options.force) {
|
|
161
113
|
console.log(`
|
|
162
|
-
.
|
|
114
|
+
.claude/settings.local.json already exists.
|
|
163
115
|
|
|
164
116
|
To view current config:
|
|
165
|
-
cat .
|
|
117
|
+
cat .claude/settings.local.json
|
|
166
118
|
|
|
167
119
|
To overwrite:
|
|
168
120
|
npx @spilno/herald-mcp init --force
|
|
169
121
|
`);
|
|
170
122
|
return;
|
|
171
123
|
}
|
|
172
|
-
|
|
124
|
+
if (!existsSync(claudeDir)) {
|
|
125
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
126
|
+
console.log("Created .claude directory");
|
|
127
|
+
}
|
|
128
|
+
const user = options.user || process.env.USER || "default";
|
|
129
|
+
const heraldConfig = buildHeraldConfig(company, project, user);
|
|
173
130
|
let finalConfig = heraldConfig;
|
|
174
|
-
if (existsSync(
|
|
131
|
+
if (existsSync(settingsPath) && !options.force) {
|
|
132
|
+
// Merge mode: preserve existing config, add/update Herald
|
|
175
133
|
try {
|
|
176
|
-
const existingContent = readFileSync(
|
|
134
|
+
const existingContent = readFileSync(settingsPath, "utf-8");
|
|
177
135
|
const existingConfig = JSON.parse(existingContent);
|
|
178
136
|
finalConfig = {
|
|
179
137
|
...existingConfig,
|
|
180
138
|
mcpServers: {
|
|
181
139
|
...existingConfig.mcpServers,
|
|
182
140
|
...heraldConfig.mcpServers
|
|
183
|
-
}
|
|
141
|
+
},
|
|
142
|
+
// Ensure project MCP servers are enabled
|
|
143
|
+
enableAllProjectMcpServers: true
|
|
184
144
|
};
|
|
185
|
-
console.log("Merging with existing .
|
|
145
|
+
console.log("Merging with existing settings.local.json");
|
|
186
146
|
}
|
|
187
147
|
catch {
|
|
188
|
-
console.log("Overwriting invalid .
|
|
148
|
+
console.log("Overwriting invalid settings.local.json");
|
|
189
149
|
}
|
|
190
150
|
}
|
|
191
|
-
|
|
192
|
-
|
|
151
|
+
else if (options.force && existsSync(settingsPath)) {
|
|
152
|
+
// Force mode: clean slate - only Herald config
|
|
153
|
+
console.log("Overwriting existing settings.local.json (--force)");
|
|
154
|
+
}
|
|
155
|
+
writeFileSync(settingsPath, JSON.stringify(finalConfig, null, 2) + "\n", "utf-8");
|
|
156
|
+
console.log("✓ Created .claude/settings.local.json");
|
|
193
157
|
if (!options.noClaudeMd) {
|
|
194
158
|
let existingClaudeMd = null;
|
|
195
159
|
if (existsSync(claudeMdPath)) {
|
|
@@ -214,26 +178,12 @@ To overwrite:
|
|
|
214
178
|
console.log("Created CLAUDE.md with Herald integration");
|
|
215
179
|
}
|
|
216
180
|
}
|
|
217
|
-
// Generate hookify rules if requested
|
|
218
|
-
if (options.hookify) {
|
|
219
|
-
const claudeDir = join(cwd, ".claude");
|
|
220
|
-
if (!existsSync(claudeDir)) {
|
|
221
|
-
mkdirSync(claudeDir, { recursive: true });
|
|
222
|
-
}
|
|
223
|
-
const hookifyRules = getHookifyRulesContent();
|
|
224
|
-
for (const rule of hookifyRules) {
|
|
225
|
-
const rulePath = join(claudeDir, rule.filename);
|
|
226
|
-
writeFileSync(rulePath, rule.content, "utf-8");
|
|
227
|
-
}
|
|
228
|
-
console.log(`✓ Created ${hookifyRules.length} hookify rules in .claude/`);
|
|
229
|
-
console.log(" - Pattern check reminder on prompts");
|
|
230
|
-
console.log(" - Pattern capture reminder on session end");
|
|
231
|
-
}
|
|
232
181
|
console.log(`
|
|
233
182
|
✓ Herald configured
|
|
234
183
|
|
|
235
184
|
Company: ${company}
|
|
236
185
|
Project: ${project}
|
|
186
|
+
User: ${user}
|
|
237
187
|
Backend: https://getceda.com
|
|
238
188
|
|
|
239
189
|
Next: Start Claude Code in this directory.
|
package/dist/cli/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAsB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,17 +13,15 @@ export interface LearnedPatterns {
|
|
|
13
13
|
patterns: Array<{
|
|
14
14
|
insight: string;
|
|
15
15
|
reinforcement?: string;
|
|
16
|
-
scope?: string;
|
|
17
16
|
}>;
|
|
18
17
|
antipatterns: Array<{
|
|
19
18
|
insight: string;
|
|
20
19
|
warning?: string;
|
|
21
|
-
scope?: string;
|
|
22
20
|
}>;
|
|
23
21
|
}
|
|
24
22
|
export declare const HERALD_SECTION_MARKER = "## Herald Integration";
|
|
25
23
|
export declare const HERALD_SECTION_TEMPLATE = "## Herald Integration\n\nYou have Herald MCP tools available. USE THEM.\n\n### On Session Start\n- Call `herald_patterns()` to learn from past sessions\n- Review antipatterns below BEFORE starting work\n\n### Pattern Capture\n- When things go WRONG: \"Herald reflect - that was rough\"\n- When things go WELL: \"Herald reflect - that was smooth\"\n- For deep analysis: \"Herald simulate\"\n\n### Context\n- Company: {{company}}\n- Project: {{project}}\n- User: {{user}}\n\n{{learned_patterns}}\n";
|
|
26
24
|
export declare function renderHeraldSection(context: HeraldContext, learnedPatterns?: LearnedPatterns): string;
|
|
27
|
-
export declare function fetchLearnedPatterns(cedaUrl: string, company: string, project: string
|
|
25
|
+
export declare function fetchLearnedPatterns(cedaUrl: string, company: string, project: string): Promise<LearnedPatterns | null>;
|
|
28
26
|
export declare function updateClaudeMdContent(existingContent: string | null, context: HeraldContext, projectName: string, learnedPatterns?: LearnedPatterns): string;
|
|
29
27
|
//# sourceMappingURL=claude-md.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-md.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/claude-md.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"claude-md.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/claude-md.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,YAAY,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5D;AAED,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D,eAAO,MAAM,uBAAuB,ofAmBnC,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,EAAE,eAAe,CAAC,EAAE,eAAe,GAAG,MAAM,CA+BrG;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAsBjC;AAED,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,GAAG,IAAI,EAC9B,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,eAAe,GAChC,MAAM,CAaR"}
|
|
@@ -29,12 +29,10 @@ export function renderHeraldSection(context, learnedPatterns) {
|
|
|
29
29
|
let learnedSection = "";
|
|
30
30
|
if (learnedPatterns && (learnedPatterns.antipatterns.length > 0 || learnedPatterns.patterns.length > 0)) {
|
|
31
31
|
learnedSection = "### Learned from Past Sessions\n\n";
|
|
32
|
-
learnedSection += `*Inheritance: ${context.user}→${context.project}→${context.company}*\n\n`;
|
|
33
32
|
if (learnedPatterns.antipatterns.length > 0) {
|
|
34
33
|
learnedSection += "**Antipatterns (AVOID THESE):**\n";
|
|
35
34
|
learnedPatterns.antipatterns.slice(0, 5).forEach((ap, i) => {
|
|
36
|
-
|
|
37
|
-
learnedSection += `${i + 1}. ${ap.insight}${scopeTag}`;
|
|
35
|
+
learnedSection += `${i + 1}. ${ap.insight}`;
|
|
38
36
|
if (ap.warning)
|
|
39
37
|
learnedSection += ` → ${ap.warning}`;
|
|
40
38
|
learnedSection += "\n";
|
|
@@ -44,8 +42,7 @@ export function renderHeraldSection(context, learnedPatterns) {
|
|
|
44
42
|
if (learnedPatterns.patterns.length > 0) {
|
|
45
43
|
learnedSection += "**Patterns (DO THESE):**\n";
|
|
46
44
|
learnedPatterns.patterns.slice(0, 5).forEach((p, i) => {
|
|
47
|
-
|
|
48
|
-
learnedSection += `${i + 1}. ${p.insight}${scopeTag}`;
|
|
45
|
+
learnedSection += `${i + 1}. ${p.insight}`;
|
|
49
46
|
if (p.reinforcement)
|
|
50
47
|
learnedSection += ` → ${p.reinforcement}`;
|
|
51
48
|
learnedSection += "\n";
|
|
@@ -58,45 +55,17 @@ export function renderHeraldSection(context, learnedPatterns) {
|
|
|
58
55
|
.replace("{{user}}", context.user)
|
|
59
56
|
.replace("{{learned_patterns}}", learnedSection);
|
|
60
57
|
}
|
|
61
|
-
export async function fetchLearnedPatterns(cedaUrl, company, project
|
|
58
|
+
export async function fetchLearnedPatterns(cedaUrl, company, project) {
|
|
62
59
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
`${cedaUrl}/api/herald/reflections?company=${company}&project=${project}&limit=10`,
|
|
67
|
-
`${cedaUrl}/api/herald/reflections?company=${company}&limit=10`,
|
|
68
|
-
];
|
|
69
|
-
const seenInsights = new Set();
|
|
70
|
-
const patterns = [];
|
|
71
|
-
const antipatterns = [];
|
|
72
|
-
const scopes = ["user", "project", "company"];
|
|
73
|
-
for (let i = 0; i < queries.length; i++) {
|
|
74
|
-
try {
|
|
75
|
-
const response = await fetch(queries[i]);
|
|
76
|
-
if (!response.ok)
|
|
77
|
-
continue;
|
|
78
|
-
const data = await response.json();
|
|
79
|
-
const scope = scopes[i];
|
|
80
|
-
for (const p of data.patterns || []) {
|
|
81
|
-
const key = p.insight.toLowerCase().trim();
|
|
82
|
-
if (!seenInsights.has(key)) {
|
|
83
|
-
seenInsights.add(key);
|
|
84
|
-
patterns.push({ ...p, scope });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
for (const ap of data.antipatterns || []) {
|
|
88
|
-
const key = ap.insight.toLowerCase().trim();
|
|
89
|
-
if (!seenInsights.has(key)) {
|
|
90
|
-
seenInsights.add(key);
|
|
91
|
-
antipatterns.push({ ...ap, scope });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// Continue to next level
|
|
97
|
-
}
|
|
60
|
+
const response = await fetch(`${cedaUrl}/api/herald/reflections?company=${company}&project=${project}&limit=10`);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
return null;
|
|
98
63
|
}
|
|
99
|
-
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
return {
|
|
66
|
+
patterns: data.patterns || [],
|
|
67
|
+
antipatterns: data.antipatterns || [],
|
|
68
|
+
};
|
|
100
69
|
}
|
|
101
70
|
catch {
|
|
102
71
|
return null;
|