Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
.wax-repo/
59 changes: 58 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,61 @@ Always
1. Use swift 6.2 best practices

Do NOT:
1. Edit Plan Documents
1. Edit Plan Documents

---

## WaxMCP Memory Protocol (Mandatory)

Use the WaxMCP tools to persist and retrieve context across sessions. This prevents context loss during long-running tasks and enables continuity when resuming work.

### Tool Reference

| Tool | Purpose | Key Params |
|------|---------|------------|
| `wax_remember` | Store a memory | `content` (text), `metadata` (dict) |
| `wax_recall` | Retrieve memories by semantic query | `query` (text), `limit` (int) |
| `wax_search` | Raw search hits (text or hybrid) | `query` (text) |
| `wax_flush` | Persist pending writes to disk | — |
| `wax_stats` | Check memory system state | — |

### When to Write (`wax_remember`)

Call `wax_remember` at these mandatory checkpoints:

- **Plan start** — Store the plan outline before beginning implementation
- **Task completion** — Record what was done, files changed, and outcome
- **Key decisions** — Capture rationale for architectural or design choices
- **Discoveries** — Log unexpected findings, gotchas, or codebase patterns
- **Errors and fixes** — Record root cause + fix so future sessions don't re-investigate
- **To-do items** — Store deferred work and open questions before context compacts

### When to Read (`wax_recall` / `wax_search`)

Call `wax_recall` or `wax_search` at these mandatory checkpoints:

- **Session start** — Query for recent context on the current project before doing any work
- **Before planning** — Check for prior plans, decisions, and deferred items
- **Context feels stale** — When unsure about earlier decisions or state, query rather than guess
- **Resuming interrupted work** — Always recall before continuing a previously paused task

### Metadata Convention

Always include these metadata keys for searchability:

```json
{
"project": "<project-name>",
"type": "plan | decision | discovery | bugfix | todo | completion",
"phase": "planning | implementing | reviewing | debugging"
}
```

### Flush Discipline

Call `wax_flush` to ensure writes are durable:

- After storing 3+ memories in sequence
- Before ending a session or switching projects
- Before any operation that may trigger context compaction
- After storing critical decisions or error resolutions
6 changes: 1 addition & 5 deletions Sources/Hive/Sources/HiveCheckpointWax/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,5 @@

<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->

### Feb 10, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #3889 | 1:51 PM | 🔵 | HiveCheckpointWaxStore Implementation | ~559 |
*No recent activity*
</claude-mem-context>
6 changes: 1 addition & 5 deletions Sources/Hive/Sources/HiveConduit/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,5 @@

<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->

### Feb 9, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #1009 | 1:02 AM | 🔵 | HiveConduit Module Structure Mirrors HiveDSL Pattern | ~358 |
*No recent activity*
</claude-mem-context>
6 changes: 1 addition & 5 deletions Sources/Hive/Sources/HiveCore/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,5 @@

<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->

### Feb 10, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #3191 | 4:11 AM | 🔵 | HiveCore Mental Model and Usage Patterns | ~478 |
*No recent activity*
</claude-mem-context>
6 changes: 1 addition & 5 deletions Sources/Hive/Sources/HiveCore/Checkpointing/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,5 @@

<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->

### Feb 10, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #3881 | 1:49 PM | 🔵 | Hive Checkpoint System Complete Implementation | ~593 |
*No recent activity*
</claude-mem-context>
43 changes: 43 additions & 0 deletions Sources/Hive/Sources/HiveCore/DataStructures/HiveBitset.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// Compact dynamic bitset backed by 64-bit machine words.
///
/// The bitset size is fixed at initialization by `wordCount`.
struct HiveBitset: Sendable, Equatable {
private var words: [UInt64]

init(wordCount: Int) {
self.words = Array(repeating: 0, count: max(wordCount, 0))
}

init(bitCapacity: Int) {
let wordsNeeded = max((max(bitCapacity, 0) + 63) / 64, 1)
self.init(wordCount: wordsNeeded)
}

var isEmpty: Bool {
words.allSatisfy { $0 == 0 }
}

mutating func removeAll() {
for index in words.indices {
words[index] = 0
}
}

mutating func insert(_ bitIndex: Int) {
guard let location = wordLocation(for: bitIndex) else { return }
words[location.word] |= location.mask
}

func contains(_ bitIndex: Int) -> Bool {
guard let location = wordLocation(for: bitIndex) else { return false }
return (words[location.word] & location.mask) != 0
}

private func wordLocation(for bitIndex: Int) -> (word: Int, mask: UInt64)? {
guard bitIndex >= 0 else { return nil }
let word = bitIndex / 64
guard words.indices.contains(word) else { return nil }
let bitOffset = bitIndex % 64
return (word: word, mask: UInt64(1) << UInt64(bitOffset))
}
}
103 changes: 103 additions & 0 deletions Sources/Hive/Sources/HiveCore/DataStructures/HiveInvertedIndex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Foundation

/// Lightweight BM25-style inverted index for in-memory recall.
struct HiveInvertedIndex: Sendable {
private(set) var postingsByTerm: [String: [String: Int]] = [:]
private(set) var termFrequenciesByDocID: [String: [String: Int]] = [:]
private(set) var docLengthByDocID: [String: Int] = [:]
private(set) var totalDocLength: Int = 0

var totalDocs: Int {
docLengthByDocID.count
}

var avgDocLength: Double {
guard totalDocs > 0 else { return 0 }
return Double(totalDocLength) / Double(totalDocs)
}

mutating func upsert(docID: String, text: String) {
if docLengthByDocID[docID] != nil {
remove(docID: docID)
}

let terms = Self.tokenize(text)
let termFrequencies = Self.termFrequencies(terms)
termFrequenciesByDocID[docID] = termFrequencies
docLengthByDocID[docID] = terms.count
totalDocLength += terms.count

for (term, frequency) in termFrequencies {
postingsByTerm[term, default: [:]][docID] = frequency
}
}

mutating func remove(docID: String) {
guard let termFrequencies = termFrequenciesByDocID.removeValue(forKey: docID),
let length = docLengthByDocID.removeValue(forKey: docID) else {
return
}

totalDocLength -= length
for term in termFrequencies.keys {
postingsByTerm[term]?[docID] = nil
if postingsByTerm[term]?.isEmpty == true {
postingsByTerm[term] = nil
}
}
}

func query(terms: [String], limit: Int) -> [(docID: String, score: Double)] {
guard limit > 0 else { return [] }
guard totalDocs > 0 else { return [] }
guard terms.isEmpty == false else { return [] }

let k1 = 1.2
let b = 0.75
let avgdl = max(avgDocLength, 1e-9)
let normalizedTerms = terms.map { $0.lowercased() }

var scoresByDocID: [String: Double] = [:]
for term in normalizedTerms {
guard let postings = postingsByTerm[term], postings.isEmpty == false else { continue }

let docFrequency = postings.count
let idf = log(1.0 + ((Double(totalDocs - docFrequency) + 0.5) / (Double(docFrequency) + 0.5)))
for (docID, termFrequency) in postings {
let tf = Double(termFrequency)
let docLength = Double(docLengthByDocID[docID] ?? 0)
let denominator = tf + k1 * (1.0 - b + b * (docLength / avgdl))
guard denominator > 0 else { continue }
let score = idf * ((tf * (k1 + 1.0)) / denominator)
scoresByDocID[docID, default: 0] += score
}
}

return scoresByDocID
.filter { $0.value > 0 }
.sorted { lhs, rhs in
if lhs.value == rhs.value {
return HiveOrdering.lexicographicallyPrecedes(lhs.key, rhs.key)
}
return lhs.value > rhs.value
}
.prefix(limit)
.map { (docID: $0.key, score: $0.value) }
}

static func tokenize(_ text: String) -> [String] {
text
.lowercased()
.split(whereSeparator: { $0.isLetter == false && $0.isNumber == false })
.map(String.init)
}

private static func termFrequencies(_ terms: [String]) -> [String: Int] {
var frequencies: [String: Int] = [:]
frequencies.reserveCapacity(terms.count)
for term in terms {
frequencies[term, default: 0] += 1
}
return frequencies
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public enum HiveRuntimeError: Error, Sendable {
case checkpointCorrupt(field: String, errorDescription: String)
case interruptPending(interruptID: HiveInterruptID)
case noCheckpointToResume
case checkpointNotFound(id: HiveCheckpointID)
case noInterruptToResume
case resumeInterruptMismatch(expected: HiveInterruptID, found: HiveInterruptID)

Expand Down
6 changes: 5 additions & 1 deletion Sources/Hive/Sources/HiveCore/Graph/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@

<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->

*No recent activity*
### Feb 19, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #7531 | 3:32 AM | 🔵 | Hive Framework: Complete Codebase Inventory | ~919 |
</claude-mem-context>
Loading
Loading