snipara-companion 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.
package/README.md ADDED
@@ -0,0 +1,458 @@
1
+ # snipara-companion
2
+
3
+ Legacy hook-oriented CLI package kept in the monorepo for older `rlm-hook` workflows.
4
+
5
+ Current automation source-of-truth docs are:
6
+
7
+ - `docs/guides/AUTOMATION_HOOKS.md`
8
+ - `docs/guides/OPENCLAW_HOOKS.md`
9
+ - `docs/operations/REPO_CHANGES_AND_AUTOMATIONS.md`
10
+
11
+ The local CLI also acts as an optional thin companion for common hosted workflows such as
12
+ querying context, generating plans, uploading docs, fetching chunks, bootstrapping sessions, and
13
+ committing end-of-task outcomes.
14
+
15
+ The current hosted API base is `https://api.snipara.com`.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install -g snipara-companion
21
+ # or
22
+ pnpm add -g snipara-companion
23
+ # or
24
+ yarn global add snipara-companion
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### 1. Initialize Configuration
30
+
31
+ ```bash
32
+ rlm-hook init
33
+ ```
34
+
35
+ This interactive setup will:
36
+
37
+ - Prompt for your RLM API key
38
+ - Prompt for your project ID
39
+ - Configure the API URL (default: https://api.snipara.com)
40
+ - Generate Claude Code hook configuration
41
+
42
+ ### 2. Configure Claude Code Hooks
43
+
44
+ The `init` command generates a `.claude/settings.json` file. Copy the hooks configuration to your Claude Code settings:
45
+
46
+ ```json
47
+ {
48
+ "hooks": {
49
+ "PreToolUse": [
50
+ {
51
+ "matcher": "Read|Grep|Glob",
52
+ "hooks": [
53
+ {
54
+ "type": "command",
55
+ "command": "rlm-hook pre-tool \"$TOOL_INPUT\""
56
+ }
57
+ ]
58
+ }
59
+ ],
60
+ "PostToolUse": [
61
+ {
62
+ "matcher": "Read|Edit|Write",
63
+ "hooks": [
64
+ {
65
+ "type": "command",
66
+ "command": "rlm-hook post-tool \"$TOOL_INPUT\""
67
+ }
68
+ ]
69
+ }
70
+ ],
71
+ "Stop": [
72
+ {
73
+ "matcher": ".*",
74
+ "hooks": [
75
+ {
76
+ "type": "command",
77
+ "command": "rlm-hook session-end"
78
+ }
79
+ ]
80
+ }
81
+ ]
82
+ }
83
+ }
84
+ ```
85
+
86
+ Note: Claude SessionStart hooks require `jq` to format the hook output JSON.
87
+
88
+ ### Cursor Hooks
89
+
90
+ ```bash
91
+ rlm-hook init --with-hooks --client cursor
92
+ ```
93
+
94
+ Creates:
95
+
96
+ - `.cursor/hooks.json`
97
+ - `.cursor/hooks/preCompact.sh`
98
+ - `.cursor/hooks/sessionStart.sh`
99
+ - `.cursor/hooks/beforeReadFile.sh`
100
+ - `.cursor/hooks/afterFileEdit.sh`
101
+
102
+ ### Windsurf Hooks
103
+
104
+ ```bash
105
+ rlm-hook init --with-hooks --client windsurf
106
+ ```
107
+
108
+ Creates:
109
+
110
+ - `.windsurf/cascade-hooks.json`
111
+ - `.windsurf/hooks/pre-read.sh`
112
+ - `.windsurf/hooks/post-read.sh`
113
+ - `.windsurf/hooks/post-write.sh`
114
+
115
+ ### 3. Start Using RLM
116
+
117
+ Your Claude Code sessions will now automatically:
118
+
119
+ - Query RLM for relevant context before file operations
120
+ - Track which files are accessed during the session
121
+ - Emit canonical automation events for hook activity
122
+ - Persist session context when the conversation ends
123
+
124
+ ## Commands
125
+
126
+ ### `rlm-hook init`
127
+
128
+ Interactive setup wizard for configuring RLM integration.
129
+
130
+ ```bash
131
+ rlm-hook init
132
+ ```
133
+
134
+ Options:
135
+
136
+ - `--api-key <key>` - Skip prompt for API key
137
+ - `--project-id <id>` - Skip prompt for project ID
138
+ - `--client <client>` - Client type (`claude-code`, `cursor`, `windsurf`)
139
+ - `--with-hooks` - Install hooks automatically
140
+ - `--dir <directory>` - Project directory for hooks (default: current)
141
+
142
+ Examples:
143
+
144
+ ```bash
145
+ rlm-hook init --with-hooks --client claude-code
146
+ rlm-hook init --with-hooks --client cursor
147
+ rlm-hook init --with-hooks --client windsurf
148
+ ```
149
+
150
+ ### `rlm-hook config`
151
+
152
+ Display current configuration.
153
+
154
+ ```bash
155
+ rlm-hook config
156
+ ```
157
+
158
+ ### `rlm-hook pre-tool <input>`
159
+
160
+ PreToolUse hook handler. Called before Read/Grep/Glob operations.
161
+
162
+ ```bash
163
+ rlm-hook pre-tool '{"path": "/src/api/auth.ts"}'
164
+ ```
165
+
166
+ Features:
167
+
168
+ - Queries RLM for relevant context based on the file/search
169
+ - Caches results for 5 minutes to avoid repeated queries
170
+ - Emits a `tool_call` automation event for hook telemetry
171
+ - Returns optimized context as a system message
172
+
173
+ ### `rlm-hook post-tool <input>`
174
+
175
+ PostToolUse hook handler. Called after Read/Edit/Write operations.
176
+
177
+ ```bash
178
+ rlm-hook post-tool '{"file_path": "/src/api/auth.ts"}'
179
+ ```
180
+
181
+ Features:
182
+
183
+ - Tracks accessed files for session persistence
184
+ - Emits a `tool_result` automation event with tracked files
185
+ - Non-blocking - doesn't slow down your workflow
186
+
187
+ ### `rlm-hook session-end`
188
+
189
+ Stop hook handler. Called when Claude Code session ends.
190
+
191
+ ```bash
192
+ rlm-hook session-end
193
+ ```
194
+
195
+ Features:
196
+
197
+ - Emits a `session_end` automation event
198
+ - Persists session context to RLM
199
+ - Clears local cache
200
+
201
+ ### `rlm-hook session status`
202
+
203
+ Show current session information.
204
+
205
+ ```bash
206
+ rlm-hook session status
207
+ ```
208
+
209
+ ### `rlm-hook session reset`
210
+
211
+ Reset session and start fresh.
212
+
213
+ ```bash
214
+ rlm-hook session reset
215
+ ```
216
+
217
+ ### `rlm-hook emit-event`
218
+
219
+ Forward a canonical lifecycle event into Snipara's hosted automation API.
220
+
221
+ ```bash
222
+ rlm-hook emit-event \
223
+ --event-type tool_call \
224
+ --payload '{"hook":"pre-tool","tool":"Read","query":"auth middleware"}'
225
+ ```
226
+
227
+ Use this when a thin local adapter needs to report lifecycle activity without owning
228
+ durable memory policy locally.
229
+
230
+ ### Workflow Commands
231
+
232
+ These are thin local wrappers around hosted Snipara workflows:
233
+
234
+ ```bash
235
+ rlm-hook query --query "auth middleware"
236
+ rlm-hook plan --query "implement OAuth device flow"
237
+ rlm-hook upload --path docs/spec.md --file ./docs/spec.md
238
+ rlm-hook chunk get --chunk-id chunk_123
239
+ rlm-hook multi-query --queries "auth flow" "rate limiting"
240
+ rlm-hook orchestrate --query "understand the auth architecture"
241
+ rlm-hook load-document --path docs/auth.md
242
+ rlm-hook events recent --limit 20
243
+ rlm-hook session-bootstrap --max-critical-tokens 2000 --max-daily-tokens 1000
244
+ rlm-hook task-commit --summary "Shipped event ingestion and dashboard inspection" --files apps/web/src/components/automation/automation-settings-panel.tsx
245
+ ```
246
+
247
+ By default these commands print human-readable terminal output. Add `--json` when you want the raw
248
+ hosted response.
249
+
250
+ For release-hardening and local packaging checks:
251
+
252
+ ```bash
253
+ pnpm --filter snipara-companion pack:smoke
254
+ pnpm --filter create-snipara pack:smoke
255
+ ```
256
+
257
+ To test a packed tarball manually, use `npm exec --package`:
258
+
259
+ ```bash
260
+ npm pack
261
+ npm exec --package ./snipara-companion-0.1.0.tgz rlm-hook -- --help
262
+ ```
263
+
264
+ Do not use `npx /path/to/snipara-companion-*.tgz`. npm will try to execute the tarball itself instead of
265
+ resolving the packaged `rlm-hook` binary.
266
+
267
+ Design rule:
268
+
269
+ - local CLI = workflow facade
270
+ - hosted Snipara = source of truth for context, chunks, plans, memory, and review policy
271
+
272
+ ### `rlm-hook cache clear`
273
+
274
+ Clear the local query cache.
275
+
276
+ ```bash
277
+ rlm-hook cache clear
278
+ ```
279
+
280
+ ## Configuration
281
+
282
+ Configuration is stored at `~/.rlmsaas/config.json`:
283
+
284
+ ```json
285
+ {
286
+ "apiKey": "rlm_your_api_key",
287
+ "projectId": "proj_abc123",
288
+ "apiUrl": "https://api.snipara.com",
289
+ "sessionId": "session_xyz789"
290
+ }
291
+ ```
292
+
293
+ ### Environment Variables
294
+
295
+ You can also use environment variables:
296
+
297
+ | Variable | Description |
298
+ | -------------------- | ----------------------------- |
299
+ | `SNIPARA_API_KEY` | API key (overrides config) |
300
+ | `SNIPARA_PROJECT_ID` | Project ID (overrides config) |
301
+ | `SNIPARA_API_URL` | API URL (overrides config) |
302
+
303
+ ## How It Works
304
+
305
+ ### PreToolUse Hook
306
+
307
+ When Claude Code is about to read a file or search the codebase:
308
+
309
+ 1. The hook extracts the file path or search query
310
+ 2. Queries RLM for relevant documentation context
311
+ 3. Returns context that's injected into Claude's system message
312
+ 4. Results are cached locally for 5 minutes
313
+
314
+ ```
315
+ ┌─────────────────────────────────────────────┐
316
+ │ Claude Code: "Let me read auth.ts..." │
317
+ ├─────────────────────────────────────────────┤
318
+ │ rlm-hook pre-tool │
319
+ │ ├─ Extract: "auth.ts" │
320
+ │ ├─ Check cache: miss │
321
+ │ ├─ Query RLM: "authentication auth.ts" │
322
+ │ └─ Return: Relevant docs + standards │
323
+ ├─────────────────────────────────────────────┤
324
+ │ Claude: Reads file WITH context │
325
+ └─────────────────────────────────────────────┘
326
+ ```
327
+
328
+ ### PostToolUse Hook
329
+
330
+ After Claude Code reads or modifies a file:
331
+
332
+ 1. The hook extracts the file path
333
+ 2. Tracks the file in the current session
334
+ 3. Non-blocking - returns immediately
335
+
336
+ ### Stop Hook
337
+
338
+ When the Claude Code session ends:
339
+
340
+ 1. Persists all tracked files to RLM
341
+ 2. Allows future sessions to pick up context
342
+ 3. Clears local cache
343
+
344
+ ## Caching
345
+
346
+ The CLI includes a local cache to minimize API calls:
347
+
348
+ - **Cache Location**: `~/.rlmsaas/cache.json`
349
+ - **TTL**: 5 minutes per query
350
+ - **Key**: Hash of query + project ID
351
+
352
+ Clear the cache manually:
353
+
354
+ ```bash
355
+ rlm-hook cache clear
356
+ ```
357
+
358
+ ## API Integration
359
+
360
+ The CLI communicates with these RLM endpoints:
361
+
362
+ | Endpoint | Purpose |
363
+ | ----------------------------------------- | -------------------- |
364
+ | `POST /api/v1/:projectId/context` | Query for context |
365
+ | `POST /api/v1/:projectId/session/track` | Track accessed files |
366
+ | `POST /api/v1/:projectId/session/persist` | Persist session |
367
+ | `GET /api/v1/:projectId/health` | Test connection |
368
+
369
+ ## Troubleshooting
370
+
371
+ ### "API key not configured"
372
+
373
+ Run `rlm-hook init` to set up your configuration.
374
+
375
+ ### "Project ID not configured"
376
+
377
+ Run `rlm-hook init` or set the `RLM_PROJECT_ID` environment variable.
378
+
379
+ ### "Connection timeout"
380
+
381
+ 1. Check your internet connection
382
+ 2. Verify the API URL is correct
383
+ 3. Check RLM service status
384
+
385
+ ### "Invalid API key"
386
+
387
+ 1. Verify your API key in the RLM dashboard
388
+ 2. Ensure the key hasn't been revoked
389
+ 3. Check the key has access to the project
390
+
391
+ ### Hook not triggering
392
+
393
+ 1. Verify hooks are configured in `.claude/settings.json`
394
+ 2. Check the hook matcher patterns
395
+ 3. Ensure `rlm-hook` is in your PATH
396
+
397
+ ### "CLI usage/help printed during hooks"
398
+
399
+ 1. Ensure the hook command includes tool input: `rlm-hook pre-tool "$TOOL_INPUT"`
400
+ 2. Confirm the matcher targets the right tools (Read/Grep/Glob or Read/Edit/Write)
401
+ 3. Restart Claude Code after updating `.claude/settings.json`
402
+
403
+ ### "Files not tracked after edits"
404
+
405
+ 1. Confirm `PostToolUse` is configured for `Read|Edit|Write`
406
+ 2. Verify the hook calls `rlm-hook post-tool "$TOOL_INPUT"`
407
+ 3. Run with debug output: `RLM_DEBUG=1 rlm-hook post-tool '{"file_path":"file.ts"}'`
408
+
409
+ ### "Need more debug output"
410
+
411
+ Set `RLM_DEBUG=1` to surface hook errors on stderr.
412
+
413
+ ### Cache issues
414
+
415
+ Clear the cache and try again:
416
+
417
+ ```bash
418
+ rlm-hook cache clear
419
+ ```
420
+
421
+ ## Development
422
+
423
+ ### Building
424
+
425
+ ```bash
426
+ cd packages/cli
427
+ pnpm install
428
+ pnpm build
429
+ ```
430
+
431
+ ### Testing locally
432
+
433
+ ```bash
434
+ pnpm dev # Watch mode
435
+ node dist/index.js init
436
+ ```
437
+
438
+ ### Running tests
439
+
440
+ ```bash
441
+ pnpm test
442
+ ```
443
+
444
+ ## Requirements
445
+
446
+ - Node.js 18 or later
447
+ - Claude Code (or compatible MCP client)
448
+ - RLM SaaS account with API key
449
+
450
+ ## Related
451
+
452
+ - [RLM SaaS Documentation](https://docs.rlm.dev)
453
+ - [Claude Code Hooks](https://docs.claude.ai/claude-code/hooks)
454
+ - [MCP Protocol](https://modelcontextprotocol.io)
455
+
456
+ ## License
457
+
458
+ MIT
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ declare function resolveQueryFromToolInput(toolInput?: string, tool?: string): string | null;
3
+
4
+ declare function extractFilesFromToolInput(toolInput?: string): string[];
5
+
6
+ type PrivacyLevel = "standard" | "sensitive" | "restricted";
7
+ type CanonicalEventType = "session_start" | "session_end" | "compact" | "message_user" | "message_assistant" | "tool_call" | "tool_result" | "file_changed" | "error_observed";
8
+ interface CanonicalEventOptions {
9
+ eventType: CanonicalEventType;
10
+ client?: string;
11
+ workspace?: string;
12
+ sessionId?: string;
13
+ agentId?: string;
14
+ privacyLevel?: PrivacyLevel;
15
+ payload?: Record<string, unknown>;
16
+ }
17
+ declare function buildCanonicalEvent(options: CanonicalEventOptions): {
18
+ type: CanonicalEventType;
19
+ client: string;
20
+ workspace: string;
21
+ session_id: string;
22
+ agent_id: string;
23
+ timestamp: string;
24
+ privacy_level: PrivacyLevel;
25
+ payload: Record<string, unknown>;
26
+ };
27
+
28
+ interface ContextQueryResult {
29
+ sections: Array<{
30
+ title: string;
31
+ content: string;
32
+ file: string;
33
+ lines: [number, number];
34
+ relevance_score: number;
35
+ token_count: number;
36
+ truncated: boolean;
37
+ }>;
38
+ total_tokens: number;
39
+ max_tokens: number;
40
+ query: string;
41
+ suggestions?: string[];
42
+ }
43
+ interface TrackFileResult {
44
+ success: boolean;
45
+ files_tracked: number;
46
+ }
47
+ interface SessionPersistResult {
48
+ success: boolean;
49
+ session_id: string;
50
+ files_tracked: number;
51
+ }
52
+ interface EmitEventResult {
53
+ accepted: number;
54
+ sessionIds: string[];
55
+ }
56
+ interface RecentAutomationEvent {
57
+ id: string;
58
+ sessionId: string;
59
+ createdAt: string;
60
+ event: {
61
+ type: string;
62
+ client: string;
63
+ workspace: string;
64
+ session_id: string;
65
+ agent_id: string;
66
+ timestamp: string;
67
+ privacy_level: "standard" | "sensitive" | "restricted";
68
+ payload?: Record<string, unknown>;
69
+ };
70
+ }
71
+ interface RecentAutomationEventsResult {
72
+ events: RecentAutomationEvent[];
73
+ count: number;
74
+ }
75
+ interface SessionMemoriesResult {
76
+ critical?: Array<Record<string, unknown>>;
77
+ daily?: Array<Record<string, unknown>>;
78
+ total_tokens?: number;
79
+ [key: string]: unknown;
80
+ }
81
+ /**
82
+ * RLM SaaS API Client - Uses MCP JSON-RPC protocol
83
+ */
84
+ declare class RLMClient {
85
+ private config;
86
+ private timeout;
87
+ constructor(timeout?: number);
88
+ /**
89
+ * Make an MCP JSON-RPC request
90
+ */
91
+ private mcpCall;
92
+ callTool<T>(toolName: string, args: Record<string, unknown>): Promise<T>;
93
+ /**
94
+ * Query for optimized context using rlm_context_query
95
+ */
96
+ queryContext(query: string, maxTokens?: number): Promise<ContextQueryResult>;
97
+ /**
98
+ * Track accessed files using rlm_remember
99
+ */
100
+ trackFiles(files: string[]): Promise<TrackFileResult>;
101
+ /**
102
+ * Persist session context using rlm_remember
103
+ */
104
+ persistSession(): Promise<SessionPersistResult>;
105
+ /**
106
+ * Get session status (recall recent memories)
107
+ */
108
+ getSession(): Promise<{
109
+ session_id: string;
110
+ files_tracked: number;
111
+ }>;
112
+ /**
113
+ * Test API connection using rlm_stats
114
+ */
115
+ testConnection(): Promise<boolean>;
116
+ emitEvent(event: {
117
+ type: string;
118
+ client: string;
119
+ workspace: string;
120
+ session_id: string;
121
+ agent_id: string;
122
+ timestamp: string;
123
+ privacy_level: "standard" | "sensitive" | "restricted";
124
+ payload?: Record<string, unknown>;
125
+ }): Promise<EmitEventResult>;
126
+ getAutomationEvents(args?: {
127
+ sessionId?: string;
128
+ limit?: number;
129
+ }): Promise<RecentAutomationEventsResult>;
130
+ plan(query: string, maxTokens?: number): Promise<Record<string, unknown>>;
131
+ uploadDocument(path: string, content: string): Promise<Record<string, unknown>>;
132
+ getChunk(chunkId: string): Promise<Record<string, unknown>>;
133
+ multiQuery(queries: Array<{
134
+ query: string;
135
+ maxTokens?: number;
136
+ }>, maxTokens?: number): Promise<Record<string, unknown>>;
137
+ orchestrate(query: string, maxTokens?: number): Promise<Record<string, unknown>>;
138
+ loadDocument(path: string): Promise<Record<string, unknown>>;
139
+ getSessionMemories(maxCriticalTokens?: number, maxDailyTokens?: number): Promise<SessionMemoriesResult>;
140
+ endOfTaskCommit(args: {
141
+ summary: string;
142
+ category?: string;
143
+ outcome?: "completed" | "partial" | "blocked" | "abandoned";
144
+ filesTouched?: string[];
145
+ }): Promise<Record<string, unknown>>;
146
+ }
147
+ /**
148
+ * Create a default client instance
149
+ */
150
+ declare function createClient(timeout?: number): RLMClient;
151
+
152
+ export { buildCanonicalEvent, createClient, extractFilesFromToolInput, resolveQueryFromToolInput };