Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d210c92
[DOCS] spec for user-level memory layer
gradyzhuo May 19, 2026
4d966de
[DOCS] implementation plan for user-level memory layer
gradyzhuo May 19, 2026
83c7fca
[FEAT] OriginConfig.shareUserMemory field (default true)
gradyzhuo May 19, 2026
6c1de75
[FEAT] OrreryEnvironment.shareUserMemory field (default true)
gradyzhuo May 19, 2026
040c794
[FEAT] EnvironmentStore.userMemoryDir() returns ~/.orrery/user/memory
gradyzhuo May 19, 2026
4c50500
[FEAT] introduce MemoryStore value type for shared read/write/fragmen…
gradyzhuo May 19, 2026
f5eddea
[FEAT] MemoryStore.emit produces capped hook output with fragments block
gradyzhuo May 19, 2026
d71042b
[FEAT] orrery memory user subcommand skeleton (info/path/emit/export)
gradyzhuo May 19, 2026
00597d9
[BREAKING] regroup orrery memory subcommands under project/user names…
gradyzhuo May 19, 2026
fbf5348
[FEAT] L10n keys for memory restructure + user-memory subcommands
gradyzhuo May 19, 2026
e8dce80
[FIX] L10n bool variants use natural-language strings; document new keys
gradyzhuo May 19, 2026
a0bf72a
[REFACTOR] route MCPServer project-memory tools through MemoryStore
gradyzhuo May 19, 2026
f008445
[FEAT] register orrery_user_memory_read MCP tool
gradyzhuo May 19, 2026
ca524f6
[FEAT] register orrery_user_memory_write MCP tool
gradyzhuo May 19, 2026
614c03b
[FEAT] UserMemoryHookInstaller protocol + ClaudeHookInstaller
gradyzhuo May 19, 2026
5a1b60c
[FEAT] CodexHookInstaller (hooks.json) + GeminiHookInstaller (setting…
gradyzhuo May 19, 2026
e6915f5
[FEAT] EnvironmentStore.ensureUserMemoryHooks/removeUserMemoryHooks
gradyzhuo May 19, 2026
d3ec5cd
[FEAT] orrery memory user enable/disable wired through EnvironmentStore
gradyzhuo May 19, 2026
d20e1c7
[FEAT] _reconcile-user-memory-hooks command + shell use integration
gradyzhuo May 19, 2026
6e70145
[FEAT] env-create wizard + --no-user-memory flag, default enabled
gradyzhuo May 19, 2026
41d3702
[FEAT] origin setup wizard inherits user-memory toggle
gradyzhuo May 19, 2026
ccf2f52
[FEAT] addTool installs user-memory hook when env opted in
gradyzhuo May 19, 2026
a47a9f2
[RELEASE] v3.0.0 — user-level memory layer + memory CLI restructure
gradyzhuo May 19, 2026
45fb4ec
[DOCS] CHANGELOG entry for v3.0.0
gradyzhuo May 19, 2026
ef3e5b6
[DOCS] note orrery-sync watched-path addition for user memory
gradyzhuo May 19, 2026
970229e
[DOCS] align v3.0.0 heading with prior CHANGELOG convention
gradyzhuo May 19, 2026
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
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
# Changelog

## v3.0.0

### Added
- User-level memory layer at `~/.orrery/user/memory/`. Cross-project, cross-env
personal memory served via:
- SessionStart hooks installed automatically into each env's Claude / Codex /
Gemini config (controlled by per-env `shareUserMemory`, default enabled).
- MCP tools `orrery_user_memory_read` and `orrery_user_memory_write`.
- New CLI subcommands `orrery memory user info / path / emit / export /
enable / disable`.
- Wizard question on env creation: "Enable user memory?" (default: yes).
- `--no-user-memory` flag on `orrery create` to opt out from the CLI.

### Changed
- **BREAKING:** `orrery memory <info|export|isolate|share|storage>` renamed to
`orrery memory project <info|export|isolate|share|storage>`. No aliases.
Scripts must be updated.
- Interactive `orrery memory` now lists both project and user memory states and
routes into the relevant submenu.

### Internal
- Introduced `MemoryStore` value type to share read/write/fragment logic
between project- and user-level memory.
- New `UserMemoryHookInstaller` protocol with Claude / Codex / Gemini
implementations; internal `_reconcile-user-memory-hooks` command runs on
every `orrery use`.

### Notes
- Existing memories under `~/.orrery/shared/memory/{projectKey}/` are
untouched.
- A future `orrery memory user import` will help lift cross-project entries
out of the project layer; not in this release.
- If you use `orrery-sync`, add `~/.orrery/user/memory/fragments/` to your
watched-paths list to enable cross-machine sync of user memory. The fragment
format is identical to the project layer.

## v2.7.0 - 2026-04-29

### Architecture
Expand Down
49 changes: 44 additions & 5 deletions Sources/OrreryCore/Commands/CreateCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public struct CreateCommand: ParsableCommand {
@Flag(name: .long, help: ArgumentHelp(L10n.Create.isolateMemoryHelp))
public var isolateMemory: Bool = false

@Flag(name: .long, inversion: .prefixedNo, help: ArgumentHelp(L10n.Create.userMemoryDisableHelp))
public var userMemory: Bool = true

public init() {}

public func run() throws {
Expand All @@ -49,6 +52,7 @@ public struct CreateCommand: ParsableCommand {
// "yes"). Same per-step flag overrides apply.
var configs: [ToolSetupRunner.Config]
var installStatusline = false
let shareUserMemoryDefault: Bool
if let toolFlag = tool {
guard let t = Tool(rawValue: toolFlag) else {
throw ValidationError(L10n.Create.unknownTool(toolFlag))
Expand All @@ -61,8 +65,17 @@ public struct CreateCommand: ParsableCommand {
isolateSessionsOverride: isolateSessions,
isolateMemoryOverride: isolateMemory
)]
// Explicit-tool path skips the interactive wizard, so the user-memory
// preference comes from the (default-true) --user-memory/--no-user-memory flag.
shareUserMemoryDefault = userMemory
} else {
(configs, installStatusline) = Self.runWizard(store: store)
let wizardResult = Self.runWizard(store: store)
configs = wizardResult.0
installStatusline = wizardResult.1
// --no-user-memory takes precedence over the wizard answer: if the
// user explicitly passed --no-user-memory (userMemory == false),
// honor that; otherwise use whatever the wizard returned.
shareUserMemoryDefault = userMemory ? wizardResult.2 : false
}

// Create empty env — per-tool flags populated during apply()
Expand Down Expand Up @@ -105,13 +118,23 @@ public struct CreateCommand: ParsableCommand {
print("Could not install statusline: \(error.localizedDescription)")
}
}

// Persist the resolved shareUserMemory flag on the saved env and
// install user-memory hooks when enabled.
var saved = try store.load(named: name)
saved.shareUserMemory = shareUserMemoryDefault
try store.save(saved)
if shareUserMemoryDefault {
try? store.ensureUserMemoryHooks(for: name)
}
}

// MARK: - Wizard

/// Loop through all tools, asking setup/skip and running the per-tool wizard for each "setup".
/// Returns configs and whether the user chose to install statusline (asked after Claude setup).
static func runWizard(store: EnvironmentStore) -> ([ToolSetupRunner.Config], installStatusline: Bool) {
/// Returns configs, whether the user chose to install statusline (asked after Claude setup),
/// and whether to enable the cross-project user-memory layer.
static func runWizard(store: EnvironmentStore) -> ([ToolSetupRunner.Config], installStatusline: Bool, shareUserMemory: Bool) {
var configs: [ToolSetupRunner.Config] = []
var installStatusline = false
for tool in Tool.allCases {
Expand All @@ -121,7 +144,8 @@ public struct CreateCommand: ParsableCommand {
installStatusline = askInstallStatusline()
}
}
return (configs, installStatusline)
let shareUserMemory = askShareUserMemory()
return (configs, installStatusline, shareUserMemory)
}

static func askInstallStatusline() -> Bool {
Expand All @@ -133,6 +157,15 @@ public struct CreateCommand: ParsableCommand {
return selector.run() == 0
}

static func askShareUserMemory() -> Bool {
let selector = SingleSelect(
title: L10n.Create.askShareUserMemory,
options: [L10n.Create.shareUserMemoryYes, L10n.Create.shareUserMemoryNo],
selected: 0
)
return selector.run() == 0
}

static func askSetupTool(_ toolName: String, defaultYes: Bool) -> Bool {
let selector = SingleSelect(
title: L10n.Create.askSetupTool(toolName),
Expand All @@ -150,13 +183,15 @@ public struct CreateCommand: ParsableCommand {
tool: Tool,
isolateSessions: Bool = false,
isolateMemory: Bool = false,
shareUserMemory: Bool = true,
store: EnvironmentStore
) throws {
let env = OrreryEnvironment(
name: name,
description: description,
isolatedSessionTools: isolateSessions ? [tool] : [],
isolateMemory: isolateMemory
isolateMemory: isolateMemory,
shareUserMemory: shareUserMemory
)
try store.save(env)
try store.addTool(tool, to: name)
Expand All @@ -167,5 +202,9 @@ public struct CreateCommand: ParsableCommand {
let claudeConfigDir = store.toolConfigDir(tool: .claude, environment: name)
store.linkOrreryMemory(projectKey: projectKey, envName: name, claudeConfigDir: claudeConfigDir)
}

if shareUserMemory {
try store.ensureUserMemoryHooks(for: name)
}
}
}
Loading
Loading