@rohirik/openltm-core 2.8.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 (97) hide show
  1. package/README.md +67 -0
  2. package/assets/opencode/agents/aegis.md +211 -0
  3. package/assets/opencode/plugins/aegis.ts +3 -0
  4. package/assets/opencode/skills/AgentTrustBoundaries/ContextCrushDefense.md +104 -0
  5. package/assets/opencode/skills/AgentTrustBoundaries/SKILL.md +31 -0
  6. package/assets/opencode/skills/AgentTrustBoundaries/TrustBoundaryPatterns.md +114 -0
  7. package/assets/opencode/skills/AgentTrustBoundaries/Workflows/DefendContextCrush.md +27 -0
  8. package/assets/opencode/skills/AgentTrustBoundaries/Workflows/HandleUntrustedContent.md +27 -0
  9. package/assets/opencode/skills/CommandPathSafety/CommandInjectionPatterns.md +95 -0
  10. package/assets/opencode/skills/CommandPathSafety/PathTraversalAndInstallerSafety.md +106 -0
  11. package/assets/opencode/skills/CommandPathSafety/SKILL.md +31 -0
  12. package/assets/opencode/skills/CommandPathSafety/Workflows/EnforcePathBoundaries.md +27 -0
  13. package/assets/opencode/skills/CommandPathSafety/Workflows/HardenCommandExecution.md +27 -0
  14. package/assets/opencode/skills/SecretSafeHandling/CloudCredentialPatterns.md +106 -0
  15. package/assets/opencode/skills/SecretSafeHandling/SKILL.md +31 -0
  16. package/assets/opencode/skills/SecretSafeHandling/SecretHandlingPlaybook.md +102 -0
  17. package/assets/opencode/skills/SecretSafeHandling/Workflows/DesignSecretSafeFlow.md +27 -0
  18. package/assets/opencode/skills/SecretSafeHandling/Workflows/RemoveSecretExposure.md +27 -0
  19. package/package.json +41 -0
  20. package/src/__tests__/cli/claude.test.ts +122 -0
  21. package/src/__tests__/cli/detect.test.ts +91 -0
  22. package/src/__tests__/cli/install.test.ts +161 -0
  23. package/src/__tests__/cli/opencode.test.ts +169 -0
  24. package/src/__tests__/cli/pi.test.ts +113 -0
  25. package/src/__tests__/cli.test.ts +70 -0
  26. package/src/__tests__/events/crossProcess.test.ts +82 -0
  27. package/src/__tests__/events/index.test.ts +32 -0
  28. package/src/__tests__/extensions.test.ts +81 -0
  29. package/src/__tests__/migrations/retention.test.ts +118 -0
  30. package/src/__tests__/queue/index.test.ts +61 -0
  31. package/src/__tests__/scheduler/index.test.ts +39 -0
  32. package/src/__tests__/vec/index.test.ts +130 -0
  33. package/src/__tests__/vec/parity.test.ts +70 -0
  34. package/src/adapterTypes.ts +23 -0
  35. package/src/cli/_shared.ts +120 -0
  36. package/src/cli/bin.ts +97 -0
  37. package/src/cli/claude.ts +124 -0
  38. package/src/cli/detect.ts +55 -0
  39. package/src/cli/hook.ts +25 -0
  40. package/src/cli/index.ts +22 -0
  41. package/src/cli/install.ts +185 -0
  42. package/src/cli/opencode.ts +193 -0
  43. package/src/cli/pi.ts +74 -0
  44. package/src/cli/types.ts +78 -0
  45. package/src/config.ts +163 -0
  46. package/src/context.ts +172 -0
  47. package/src/dao/conflicts.ts +26 -0
  48. package/src/dao/contextItems.ts +70 -0
  49. package/src/dao/embeddings.ts +78 -0
  50. package/src/dao/index.ts +9 -0
  51. package/src/dao/provenanceAudit.ts +108 -0
  52. package/src/dao/types.ts +142 -0
  53. package/src/db.ts +780 -0
  54. package/src/dedup.ts +12 -0
  55. package/src/embeddings.ts +386 -0
  56. package/src/events/index.ts +130 -0
  57. package/src/extensions.ts +140 -0
  58. package/src/graph.ts +268 -0
  59. package/src/index.ts +95 -0
  60. package/src/janitor/archive.ts +66 -0
  61. package/src/janitor/decay.ts +60 -0
  62. package/src/janitor/dedup.ts +333 -0
  63. package/src/janitor/embeddings.ts +209 -0
  64. package/src/janitor/index.ts +215 -0
  65. package/src/janitor/promote.ts +188 -0
  66. package/src/janitor/providers/anthropic.ts +91 -0
  67. package/src/janitor/providers/cohere.ts +135 -0
  68. package/src/janitor/providers/gemini.ts +156 -0
  69. package/src/janitor/providers/ollama.ts +177 -0
  70. package/src/janitor/providers/openai.ts +121 -0
  71. package/src/janitor/providers/openrouter.ts +182 -0
  72. package/src/janitor/providers/types.ts +154 -0
  73. package/src/janitor/providers/utils.ts +35 -0
  74. package/src/janitor/supersedes.ts +199 -0
  75. package/src/lib/honker.ts +54 -0
  76. package/src/lib/honkerTypes.ts +109 -0
  77. package/src/lib/jsonlLogger.ts +92 -0
  78. package/src/lib/writeQueue.ts +28 -0
  79. package/src/migrations.ts +415 -0
  80. package/src/paths.ts +22 -0
  81. package/src/proposals.ts +120 -0
  82. package/src/providers/disabled.ts +19 -0
  83. package/src/providers/embeddingProvider.ts +49 -0
  84. package/src/providers/gemini.ts +37 -0
  85. package/src/providers/index.ts +2 -0
  86. package/src/providers/ollama.ts +43 -0
  87. package/src/providers/openai.ts +35 -0
  88. package/src/queue/index.ts +53 -0
  89. package/src/queue/worker.ts +77 -0
  90. package/src/recall/categorise.ts +139 -0
  91. package/src/recall/explainer.ts +76 -0
  92. package/src/scheduler/index.ts +97 -0
  93. package/src/schema.sql +191 -0
  94. package/src/secretsScrubber.ts +105 -0
  95. package/src/shared-db.ts +158 -0
  96. package/src/vec/index.ts +161 -0
  97. package/tsconfig.json +9 -0
@@ -0,0 +1,106 @@
1
+ # Path Traversal And Installer Safety
2
+
3
+ ## Boundary Model
4
+
5
+ Every write, extract, copy, replace, or delete operation needs an explicit allowed root. Normalize the candidate path, resolve it against the root, and verify the final destination remains inside that root before touching the filesystem.
6
+
7
+ ## Traversal And Zip-Slip Risks
8
+
9
+ Untrusted paths may contain `..`, absolute prefixes, mixed separators, symlinks, or archive metadata that escapes the intended directory. Zip-slip attacks use crafted archive entries such as `../../outside.txt` to overwrite files outside the extraction root.
10
+
11
+ ## Installer Hardening Principles
12
+
13
+ Operate on a fixed list of managed targets. Avoid wildcard writes. Keep overwrite behavior intentional. Refuse to follow symlinks into unexpected locations. Validate both the source artifact and the final destination before moving files into place.
14
+
15
+ ## TypeScript Examples
16
+
17
+ ### Safe
18
+
19
+ ```ts
20
+ import path from "node:path";
21
+
22
+ export function resolveWithinRoot(root: string, candidate: string): string {
23
+ const resolved = path.resolve(root, candidate);
24
+
25
+ if (!resolved.startsWith(`${root}${path.sep}`) && resolved !== root) {
26
+ throw new Error("Path escapes allowed root");
27
+ }
28
+
29
+ return resolved;
30
+ }
31
+ ```
32
+
33
+ ### Unsafe
34
+
35
+ ```ts
36
+ import path from "node:path";
37
+
38
+ export function installFile(root: string, candidate: string): string {
39
+ return path.join(root, candidate);
40
+ }
41
+ ```
42
+
43
+ `path.join` alone does not prove the final path stays inside the root.
44
+
45
+ ## Python Examples
46
+
47
+ ### Safe
48
+
49
+ ```python
50
+ from pathlib import Path
51
+
52
+
53
+ def resolve_within_root(root: Path, candidate: str) -> Path:
54
+ resolved = (root / candidate).resolve()
55
+ if root.resolve() not in [resolved, *resolved.parents]:
56
+ raise ValueError("Path escapes allowed root")
57
+ return resolved
58
+ ```
59
+
60
+ ### Unsafe
61
+
62
+ ```python
63
+ from pathlib import Path
64
+
65
+
66
+ def write_target(root: Path, candidate: str) -> Path:
67
+ return root / candidate
68
+ ```
69
+
70
+ ## Bash Examples
71
+
72
+ ### Safe
73
+
74
+ ```bash
75
+ #!/usr/bin/env bash
76
+ set -euo pipefail
77
+
78
+ root_dir="$1"
79
+ candidate="$2"
80
+ target="$(python3 -c 'from pathlib import Path; import sys; root = Path(sys.argv[1]).resolve(); target = (root / sys.argv[2]).resolve(); print(target if root in [target, *target.parents] else "")' "$root_dir" "$candidate")"
81
+
82
+ [[ -n "$target" ]] || { printf 'Path escapes root\n' >&2; exit 1; }
83
+ ```
84
+
85
+ ### Unsafe
86
+
87
+ ```bash
88
+ #!/usr/bin/env bash
89
+ set -euo pipefail
90
+
91
+ cp "$2" "$1/$3"
92
+ ```
93
+
94
+ String concatenation does not enforce destination boundaries.
95
+
96
+ ## Archive Extraction Checks
97
+
98
+ Validate every entry name before extraction. Reject absolute paths, parent traversal, device names, and symlink targets that resolve outside the root. Treat archive metadata as attacker-controlled input, not as trustworthy filesystem intent.
99
+
100
+ ## Review Checklist
101
+
102
+ - [ ] Every filesystem operation resolves against an explicit allowed root.
103
+ - [ ] Final destinations are checked after normalization and resolution.
104
+ - [ ] Installer flows avoid wildcard writes and unexpected symlink traversal.
105
+ - [ ] Archive extraction defends against zip-slip and absolute-path escapes.
106
+ - [ ] TypeScript, Python, and Bash examples all validate final path boundaries.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: command-path-safety
3
+ description: "USE WHEN hardening command execution, path handling, or installer boundaries."
4
+ ---
5
+
6
+ # Command Path Safety
7
+
8
+ Treat commands and paths as structured, validated inputs.
9
+
10
+ ## Workflow Routing
11
+
12
+ | Workflow | Trigger | File |
13
+ |---------|---------|------|
14
+ | **HardenCommandExecution** | "command injection", "shell safety", "exec hardening", "safe subprocess" | `Workflows/HardenCommandExecution.md` |
15
+ | **EnforcePathBoundaries** | "path traversal", "zip slip", "installer safety", "path boundaries" | `Workflows/EnforcePathBoundaries.md` |
16
+
17
+ ## SkillSearch
18
+
19
+ - Command injection guidance: `SkillSearch('command path safety command injection patterns')` → loads `CommandInjectionPatterns.md`
20
+ - Path and installer guidance: `SkillSearch('command path safety path traversal installer safety')` → loads `PathTraversalAndInstallerSafety.md`
21
+
22
+ ## Use This Skill To
23
+
24
+ - Replace shell interpolation with validated structured execution.
25
+ - Enforce path roots for writes, extraction, and installer operations.
26
+ - Review archive and installer behavior for traversal and boundary escapes.
27
+
28
+ ## Not This Skill
29
+
30
+ - Not for exploit scanning, malware analysis, or final security verdicts.
31
+ - Hand off to @aegis for deep audits, exploitability assessment, or repo-wide review.
@@ -0,0 +1,27 @@
1
+ # EnforcePathBoundaries
2
+
3
+ ## Use When
4
+
5
+ Use when code writes files, extracts archives, installs assets, or accepts filenames that could escape an intended root.
6
+
7
+ ## Procedure
8
+
9
+ 1. Define the exact allowed root for the operation and list every candidate source of path input.
10
+ 2. Normalize and resolve each candidate path against the allowed root before any filesystem action.
11
+ 3. Reject traversal markers, absolute escapes, unexpected symlink targets, and archive entries that resolve outside the root.
12
+ 4. Replace wildcard or implicit installer writes with an explicit managed target list.
13
+ 5. Re-check the final destination after resolution and before overwrite, extraction, or replacement occurs.
14
+ 6. Review TypeScript, Python, and Bash implementations for equivalent boundary gaps, then add adversarial tests.
15
+
16
+ ## Done When
17
+
18
+ - [ ] Every destination is proven to remain inside an allowed root.
19
+ - [ ] Traversal, zip-slip, and symlink escapes are rejected.
20
+ - [ ] Installer operations act only on explicit managed targets.
21
+ - [ ] Cross-language path handling follows the same boundary rules.
22
+
23
+ ## Escalate To @aegis When
24
+
25
+ - You suspect an active path traversal exploit or malicious archive.
26
+ - The boundary crosses user directories, system paths, or deployment artifacts.
27
+ - You need a wider audit of installer or extraction behavior.
@@ -0,0 +1,27 @@
1
+ # HardenCommandExecution
2
+
3
+ ## Use When
4
+
5
+ Use when code builds shell commands, launches subprocesses, or passes untrusted values into command execution paths.
6
+
7
+ ## Procedure
8
+
9
+ 1. Identify the executable, every argument source, and every place untrusted data could influence parsing.
10
+ 2. Replace shell-built command strings with structured argv invocation wherever the runtime allows it.
11
+ 3. Validate attacker-controlled fields for separators, substitutions, globs, newlines, and leading-dash option injection.
12
+ 4. Insert `--` before positional untrusted values when the target command supports it.
13
+ 5. Remove `eval`, `exec` string templates, `os.system`, and comparable dynamic shell patterns from the flow.
14
+ 6. Review TypeScript, Python, and Bash entry points for equivalent weaknesses, then add adversarial tests.
15
+
16
+ ## Done When
17
+
18
+ - [ ] Commands use explicit executable plus arguments.
19
+ - [ ] Metacharacter and option-injection risks are addressed.
20
+ - [ ] Dynamic shell construction paths are removed or tightly constrained.
21
+ - [ ] Cross-language command execution patterns are consistently hardened.
22
+
23
+ ## Escalate To @aegis When
24
+
25
+ - You need exploitability assessment for an existing injection path.
26
+ - The flow executes downloaded or attacker-supplied programs.
27
+ - Repo-wide command execution review is required.
@@ -0,0 +1,106 @@
1
+ # Cloud Credential Patterns
2
+
3
+ ## Recognition Principles
4
+
5
+ Recognize provider credential formats so you can redact, quarantine, or avoid reproducing them. Do not copy live-looking values into code, docs, or tests. Use obviously fake placeholders that communicate format without resembling deployable credentials.
6
+
7
+ ## AWS Patterns
8
+
9
+ Common AWS access key identifiers begin with prefixes such as `AKIA` or `ASIA`, followed by additional uppercase alphanumeric characters. Session tokens and secret access keys are separate values and should never be shown in full.
10
+
11
+ Use placeholders like `AKIA_FAKE_EXAMPLE_ONLY` or `ASIA_FAKE_SESSION_ONLY`. Keep them visibly synthetic with separators or words.
12
+
13
+ ## GCP Patterns
14
+
15
+ Google API keys often begin with `AIza`. Service account credentials are usually JSON documents containing keys such as `type`, `project_id`, `client_email`, and `private_key`. Never reproduce a realistic private key block or full JSON blob from a live account.
16
+
17
+ Use placeholders like `AIza_FAKE_EXAMPLE_ONLY` and `"private_key": "<REDACTED-FAKE-KEY>"`.
18
+
19
+ ## Azure Patterns
20
+
21
+ Azure leaks often appear inside connection strings or signed URLs. Watch for fields like `AccountKey=` in storage connection strings and query parameters such as `sig=` in SAS URLs. Redact the value while preserving enough shape to identify the credential class.
22
+
23
+ Use placeholders like `AccountKey=FAKE_EXAMPLE_ONLY` and `sig=FAKE_SIGNATURE_ONLY`.
24
+
25
+ ## TypeScript Examples
26
+
27
+ ### Safe
28
+
29
+ ```ts
30
+ export function redactCloudSecret(value: string): string {
31
+ if (value.startsWith("AKIA") || value.startsWith("ASIA")) {
32
+ return "<AWS-KEY-REDACTED>";
33
+ }
34
+
35
+ if (value.startsWith("AIza")) {
36
+ return "<GCP-API-KEY-REDACTED>";
37
+ }
38
+
39
+ return value.replace(/AccountKey=[^;]+/, "AccountKey=<REDACTED>");
40
+ }
41
+ ```
42
+
43
+ ### Unsafe
44
+
45
+ ```ts
46
+ export const exampleConfig = {
47
+ accessKeyId: "AKIAFAKEBUTTOOREAL1234",
48
+ apiKey: "AIzaFakeButLooksReal1234567890",
49
+ };
50
+ ```
51
+
52
+ Even fake examples must be obviously fake, not merely invalid.
53
+
54
+ ## Python Examples
55
+
56
+ ### Safe
57
+
58
+ ```python
59
+ def mask_connection_string(text: str) -> str:
60
+ return text.replace("AccountKey=FAKE_EXAMPLE_ONLY", "AccountKey=<REDACTED>")
61
+ ```
62
+
63
+ ### Unsafe
64
+
65
+ ```python
66
+ SERVICE_ACCOUNT = {
67
+ "type": "service_account",
68
+ "private_key": "-----BEGIN PRIVATE KEY-----\npretend\n-----END PRIVATE KEY-----",
69
+ }
70
+ ```
71
+
72
+ Avoid private-key shaped fixtures, even when they are fake.
73
+
74
+ ## Bash Examples
75
+
76
+ ### Safe
77
+
78
+ ```bash
79
+ #!/usr/bin/env bash
80
+ set -euo pipefail
81
+
82
+ printf 'Using %s\n' 'AWS_ACCESS_KEY_ID=<REDACTED>'
83
+ ```
84
+
85
+ ### Unsafe
86
+
87
+ ```bash
88
+ #!/usr/bin/env bash
89
+ set -euo pipefail
90
+
91
+ export AZURE_STORAGE_CONNECTION_STRING='DefaultEndpointsProtocol=https;AccountName=demo;AccountKey=FAKE_EXAMPLE_ONLY'
92
+ ```
93
+
94
+ Avoid shell examples that normalize inline secret storage, even with placeholders.
95
+
96
+ ## Redaction Guidance
97
+
98
+ Preserve provider clues while removing the secret value. Examples: keep `AKIA...` as `AKIA<REDACTED>`, keep `sig=` as `sig=<REDACTED>`, and keep service account field names while replacing values. The goal is reviewability without leakage.
99
+
100
+ ## Review Checklist
101
+
102
+ - [ ] AWS, GCP, and Azure credential shapes are recognized without reproducing realistic values.
103
+ - [ ] Examples use obviously fake placeholders, not plausible credentials.
104
+ - [ ] TypeScript, Python, and Bash examples all prefer redaction over inline storage.
105
+ - [ ] Service account JSON and signed URLs are described safely.
106
+ - [ ] Documentation preserves provider context while removing secret material.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: secret-safe-handling
3
+ description: "USE WHEN handling credentials, env vars, logs, tests, or docs safely."
4
+ ---
5
+
6
+ # Secret Safe Handling
7
+
8
+ Reference secret interfaces without exposing secret values.
9
+
10
+ ## Workflow Routing
11
+
12
+ | Workflow | Trigger | File |
13
+ |---------|---------|------|
14
+ | **DesignSecretSafeFlow** | "secret flow", "credential wiring", "env vars", "safe secret access" | `Workflows/DesignSecretSafeFlow.md` |
15
+ | **RemoveSecretExposure** | "secret leak", "redact token", "credential exposure", "cleanup secret" | `Workflows/RemoveSecretExposure.md` |
16
+
17
+ ## SkillSearch
18
+
19
+ - Cloud credential identification: `SkillSearch('secret safe handling cloud credential patterns')` → loads `CloudCredentialPatterns.md`
20
+ - Safe handling guidance: `SkillSearch('secret safe handling playbook')` → loads `SecretHandlingPlaybook.md`
21
+
22
+ ## Use This Skill To
23
+
24
+ - Recognize cloud credential shapes without copying live values.
25
+ - Design code, docs, tests, and logs that avoid secret disclosure.
26
+ - Remediate accidental exposure using redaction and rotation-oriented workflows.
27
+
28
+ ## Not This Skill
29
+
30
+ - Not for secret scanning, incident verdicts, or provider-side breach analysis.
31
+ - Hand off to @aegis for detection runs, deep audits, or formal security judgments.
@@ -0,0 +1,102 @@
1
+ # Secret Handling Playbook
2
+
3
+ ## Core Practice
4
+
5
+ Secrets should flow through dedicated interfaces such as environment variables, secret managers, injected files with limited scope, or runtime handles. They should not flow through source code literals, screenshots, snapshots, shell history, or debug logs.
6
+
7
+ ## Safe Wiring Patterns
8
+
9
+ Use names, paths, and contracts instead of values. Say `process.env.API_TOKEN`, `os.environ["API_TOKEN"]`, or `/run/secrets/api-token` rather than showing any token content.
10
+
11
+ Prefer the shortest path from secret source to secret consumer. Every extra copy creates another leak surface.
12
+
13
+ ## Logging Patterns
14
+
15
+ Log secret state, not secret content. Safe logs include presence, source, age, rotation state, or validation outcome. Unsafe logs include full tokens, connection strings, authorization headers, signed URLs, or PEM blocks.
16
+
17
+ ## Test And Documentation Patterns
18
+
19
+ Tests should use synthetic placeholders such as `<TOKEN_FROM_TEST_FIXTURE>` or `FAKE_EXAMPLE_ONLY`. Documentation should show where a value goes and how to inject it, not what a realistic value looks like.
20
+
21
+ Snapshots, screenshots, and copy-paste setup guides often become leak vectors. Review them as carefully as source code.
22
+
23
+ ## TypeScript Examples
24
+
25
+ ### Safe
26
+
27
+ ```ts
28
+ export function readApiToken(): string {
29
+ const token = process.env.API_TOKEN;
30
+
31
+ if (!token) {
32
+ throw new Error("API_TOKEN is required");
33
+ }
34
+
35
+ return token;
36
+ }
37
+ ```
38
+
39
+ ### Unsafe
40
+
41
+ ```ts
42
+ export const API_TOKEN = "FAKE_TOKEN_EXAMPLE_ONLY";
43
+ ```
44
+
45
+ Even fake literals teach the wrong integration pattern.
46
+
47
+ ## Python Examples
48
+
49
+ ### Safe
50
+
51
+ ```python
52
+ import os
53
+
54
+
55
+ def get_db_password() -> str:
56
+ password = os.environ["DB_PASSWORD"]
57
+ return password
58
+ ```
59
+
60
+ ### Unsafe
61
+
62
+ ```python
63
+ def debug_secret(password: str) -> None:
64
+ print(f"db password={password}")
65
+ ```
66
+
67
+ ## Bash Examples
68
+
69
+ ### Safe
70
+
71
+ ```bash
72
+ #!/usr/bin/env bash
73
+ set -euo pipefail
74
+
75
+ if [[ -z "${API_TOKEN:-}" ]]; then
76
+ printf 'API_TOKEN is missing\n' >&2
77
+ exit 1
78
+ fi
79
+ ```
80
+
81
+ ### Unsafe
82
+
83
+ ```bash
84
+ #!/usr/bin/env bash
85
+ set -euo pipefail
86
+
87
+ curl -H "Authorization: Bearer ${API_TOKEN}" "https://example.invalid"
88
+ ```
89
+
90
+ Inline shell expansion can leak into process listings, traces, and logs.
91
+
92
+ ## Cleanup Strategy For Exposed Secrets
93
+
94
+ When exposure occurs, first contain the artifact, then redact visible copies, then plan rotation with the system owner. Code cleanup alone is not enough if the secret may already have been observed. Preserve evidence for incident response without repeating the secret.
95
+
96
+ ## Review Checklist
97
+
98
+ - [ ] Secrets enter code through approved interfaces instead of literals.
99
+ - [ ] Logs, tests, docs, and screenshots avoid revealing secret values.
100
+ - [ ] TypeScript, Python, and Bash examples model safe integration patterns.
101
+ - [ ] Redaction preserves context without disclosing the value.
102
+ - [ ] Cleanup guidance includes containment and rotation-oriented follow-up.
@@ -0,0 +1,27 @@
1
+ # DesignSecretSafeFlow
2
+
3
+ ## Use When
4
+
5
+ Use when designing code, docs, tests, or automation that must access credentials without exposing them.
6
+
7
+ ## Procedure
8
+
9
+ 1. Identify the secret producer, the secret consumer, and every place the value could be copied or logged.
10
+ 2. Choose a dedicated handoff mechanism such as environment injection, secret manager lookup, or restricted runtime file.
11
+ 3. Replace any literal examples with interface-only references, placeholders, or redacted shapes.
12
+ 4. Audit logs, error paths, tests, and docs to ensure they reveal state only, not secret content.
13
+ 5. Minimize secret lifetime and duplication by passing the value directly to the consumer with the fewest intermediate hops.
14
+ 6. Review the design for leak surfaces across TypeScript, Python, and Bash touchpoints, then add adversarial tests.
15
+
16
+ ## Done When
17
+
18
+ - [ ] No design artifact embeds a secret literal.
19
+ - [ ] Secret flow uses an approved injection or retrieval path.
20
+ - [ ] Logs, docs, and tests show interface shape without value disclosure.
21
+ - [ ] Leak surfaces are identified and reduced.
22
+
23
+ ## Escalate To @aegis When
24
+
25
+ - You suspect a real credential has already been exposed.
26
+ - You need repo-wide secret detection or incident-oriented review.
27
+ - The flow touches multiple systems and requires a formal security judgment.
@@ -0,0 +1,27 @@
1
+ # RemoveSecretExposure
2
+
3
+ ## Use When
4
+
5
+ Use when code, docs, logs, tests, or examples contain a secret or secret-like value that must be contained and cleaned up.
6
+
7
+ ## Procedure
8
+
9
+ 1. Identify every artifact that contains the exposed value or a derived copy such as logs, snapshots, comments, or transcripts.
10
+ 2. Contain further spread by removing the value from active prompts, outputs, and generated artifacts.
11
+ 3. Replace the exposed value with a redacted marker that preserves provider or field context.
12
+ 4. Update code, docs, tests, and shell snippets so future examples use interface-only references or obviously fake placeholders.
13
+ 5. Document whether rotation or owner notification is required without repeating the secret in the note.
14
+ 6. Re-review the cleaned artifacts for residual copies and screenshot risk, then add adversarial tests.
15
+
16
+ ## Done When
17
+
18
+ - [ ] The exposed value is no longer present in maintained artifacts.
19
+ - [ ] Replacement text is clearly redacted and non-usable.
20
+ - [ ] Follow-up notes avoid reintroducing the secret.
21
+ - [ ] Future examples use safe placeholders or secret interfaces.
22
+
23
+ ## Escalate To @aegis When
24
+
25
+ - The exposure may involve a real cloud credential, token, or private key.
26
+ - You need forensic guidance, scan coverage, or incident severity judgment.
27
+ - The leak spans commit history, artifacts, or multiple repositories.
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@rohirik/openltm-core",
3
+ "version": "2.8.0",
4
+ "description": "Shared LTM storage engine — path-agnostic SQLite core used by Claude Code, OpenCode, and Pi adapters",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts",
9
+ "./cli": "./src/cli/index.ts"
10
+ },
11
+ "bin": {
12
+ "ltm": "src/cli/bin.ts"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/RohiRIK/OpenLtm"
17
+ },
18
+ "keywords": [
19
+ "ltm",
20
+ "long-term-memory",
21
+ "claude-code",
22
+ "sqlite",
23
+ "memory"
24
+ ],
25
+ "license": "MIT",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "dependencies": {
30
+ "@clack/prompts": "^1.3.0",
31
+ "@iarna/toml": "^2.2.5",
32
+ "bun-types": "^1.0.0",
33
+ "sqlite-vec": "0.1.9"
34
+ },
35
+ "optionalDependencies": {
36
+ "@russellthehippo/honker-bun": "^0.2.2"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.0.0"
40
+ }
41
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * cli/claude.test.ts — Unit tests for the Claude Code installer.
3
+ */
4
+ import { describe, it, expect, beforeEach, afterEach } from "bun:test";
5
+ import { mkdirSync, rmSync, existsSync, readFileSync, writeFileSync } from "fs";
6
+ import { join } from "path";
7
+ import os from "os";
8
+
9
+ function makeTmp(): string {
10
+ const dir = join(
11
+ os.tmpdir(),
12
+ `ltm-claude-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
13
+ );
14
+ mkdirSync(dir, { recursive: true });
15
+ return dir;
16
+ }
17
+
18
+ function readSettings(tmpDir: string): Record<string, unknown> {
19
+ const p = join(tmpDir, ".claude", "settings.json");
20
+ return JSON.parse(readFileSync(p, "utf8")) as Record<string, unknown>;
21
+ }
22
+
23
+ describe("installClaude", () => {
24
+ let tmpDir: string;
25
+
26
+ beforeEach(() => {
27
+ tmpDir = makeTmp();
28
+ });
29
+
30
+ afterEach(() => {
31
+ rmSync(tmpDir, { recursive: true, force: true });
32
+ });
33
+
34
+ it("creates settings.json with MCP entry and all 3 hooks", async () => {
35
+ const { installClaude } = await import("../../cli/claude.js");
36
+ const result = await installClaude({ homedir: tmpDir });
37
+ expect(result.status).toBe("installed");
38
+
39
+ const s = readSettings(tmpDir);
40
+ const servers = s["mcpServers"] as Record<string, unknown>;
41
+ expect(servers).toBeDefined();
42
+ expect(servers["openltm"]).toBeDefined();
43
+
44
+ const hooks = s["hooks"] as Record<string, unknown[]>;
45
+ expect(hooks).toBeDefined();
46
+ expect(Array.isArray(hooks["SessionStart"])).toBe(true);
47
+ expect(Array.isArray(hooks["PreCompact"])).toBe(true);
48
+ expect(Array.isArray(hooks["PostEditCheck"])).toBe(true);
49
+ });
50
+
51
+ it("second run returns status skipped (idempotent)", async () => {
52
+ const { installClaude } = await import("../../cli/claude.js");
53
+ const first = await installClaude({ homedir: tmpDir });
54
+ expect(first.status).toBe("installed");
55
+
56
+ const second = await installClaude({ homedir: tmpDir });
57
+ expect(second.status).toBe("skipped");
58
+ });
59
+
60
+ it("preserves existing mcpServers entries from other tools", async () => {
61
+ // Pre-create a settings.json with another MCP server
62
+ mkdirSync(join(tmpDir, ".claude"), { recursive: true });
63
+ writeFileSync(
64
+ join(tmpDir, ".claude", "settings.json"),
65
+ JSON.stringify({ mcpServers: { existingTool: { command: "npx", args: ["some-tool"] } } }),
66
+ "utf8",
67
+ );
68
+
69
+ const { installClaude } = await import("../../cli/claude.js");
70
+ await installClaude({ homedir: tmpDir });
71
+
72
+ const s = readSettings(tmpDir);
73
+ const servers = s["mcpServers"] as Record<string, unknown>;
74
+ expect(servers["existingTool"]).toBeDefined();
75
+ expect(servers["openltm"]).toBeDefined();
76
+ });
77
+
78
+ it("dryRun=true does not write any files", async () => {
79
+ const { installClaude } = await import("../../cli/claude.js");
80
+ const result = await installClaude({ homedir: tmpDir, dryRun: true });
81
+ expect(result.status).toBe("installed");
82
+
83
+ const settingsFile = join(tmpDir, ".claude", "settings.json");
84
+ expect(existsSync(settingsFile)).toBe(false);
85
+ });
86
+
87
+ it("creates settings.json when .claude/ directory does not exist", async () => {
88
+ const { installClaude } = await import("../../cli/claude.js");
89
+ // tmpDir has no .claude subdirectory
90
+ const result = await installClaude({ homedir: tmpDir });
91
+ expect(result.status).toBe("installed");
92
+ expect(existsSync(join(tmpDir, ".claude", "settings.json"))).toBe(true);
93
+ });
94
+
95
+ it("includes the correct MCP command and args", async () => {
96
+ const { installClaude } = await import("../../cli/claude.js");
97
+ await installClaude({ homedir: tmpDir });
98
+
99
+ const s = readSettings(tmpDir);
100
+ const ltm = (s["mcpServers"] as Record<string, unknown>)["openltm"] as {
101
+ command: string;
102
+ args: string[];
103
+ };
104
+ expect(ltm.command).toBe("bunx");
105
+ expect(ltm.args).toEqual(["@rohirik/openltm-core", "mcp-serve"]);
106
+ });
107
+
108
+ it("includes all 3 hook events with correct command", async () => {
109
+ const { installClaude } = await import("../../cli/claude.js");
110
+ await installClaude({ homedir: tmpDir });
111
+
112
+ const s = readSettings(tmpDir);
113
+ const hooks = s["hooks"] as Record<string, Array<{ command: string; args: string[] }>>;
114
+
115
+ for (const event of ["SessionStart", "PreCompact", "PostEditCheck"]) {
116
+ const entries = hooks[event];
117
+ expect(entries.length).toBeGreaterThan(0);
118
+ expect(entries[0]!.command).toBe("bunx");
119
+ expect(entries[0]!.args[0]).toBe("@rohirik/openltm-core");
120
+ }
121
+ });
122
+ });