feat: Obsidian integration via cli scripts

This commit is contained in:
2026-04-17 08:05:23 -06:00
parent 902e11c7d4
commit a2b667f497
35 changed files with 715 additions and 34 deletions

View File

@@ -13,10 +13,14 @@ Behavior rules:
- Treat `core/` as the reusable project-independent operating model.
- Treat `profiles/fidelity/profile.md` as the active Fidelity project profile.
- Treat `vault/` as the canonical clean knowledge base for humans and AI.
- Treat `scripts/memory/` as the project-agnostic access layer for note creation, vault search, Base queries, and health checks.
- Treat `scripts/obsidian/` as the current Obsidian adapter. Do not couple durable memory rules to Obsidian-specific behavior.
- Treat `ai/inbox/` and generated connector files as raw evidence only, not promoted memory.
- Keep Obsidian Bases clean: do not let templates in `vault/09-templates/` appear as real daily notes, work items, people, decisions, systems, or workstreams.
- Role mapping notes such as `vault/04-people/manager.md` are `type: role-map`; actual people profiles are `type: person`.
- When editing canonical vault notes, update useful metadata at the same time: `updated`, `systems`, `workstreams`, `people`, `related`, `focus`, `work-items`, and `blockers` when applicable.
- When creating a new typed note, prefer `bash scripts/memory/memory.sh create <type> <slug> [title]`, then inspect and refine the generated Markdown.
- When checking vault quality, use `bash scripts/memory/memory.sh health` and direct file inspection.
- Work item notes should preserve Jira ID/title and explicit relationships so standups, Bases, and graph navigation stay useful.
- Daily notes should include `focus`, `work-items`, and `blockers` when those values are clear.
- Before answering a prompt that depends on current state, verify the latest relevant files instead of relying only on conversation history.
@@ -44,6 +48,7 @@ Behavior rules:
- Update preexisting memory when a new prompt clarifies or corrects something already stored.
- Do not wait for a dedicated sync command if the correct memory update is already obvious.
- Do not leave behavior-only corrections only in daily logs. If a correction should affect future output, update the tool or instruction that produces that output.
- If the memory interface or Obsidian adapter fails, continue with direct Markdown operations when safe and do not promote the failure as project memory.
- Do not over-promote uncertain information. Keep uncertain items in the daily log.
- When drafting communication, preserve technical meaning and improve clarity in natural US English.
- When answering Swift/iOS programming questions, use the project-local iOS skills and `vault/03-context/ios/`.

View File

@@ -13,12 +13,15 @@ Behavior rules:
- Load `core/` first for project-independent operating rules.
- Load the active profile from `AIW_PROJECT_PROFILE` when available; otherwise use the configured project files in this workspace.
- Treat `vault/` as the canonical clean knowledge base.
- Treat `scripts/memory/` as the stable memory access layer.
- Treat tool-specific integrations such as Obsidian as replaceable adapters.
- Treat profile files as configuration and `ai/inbox/` plus generated connector files as raw evidence.
- Keep Obsidian Bases clean by excluding templates and typing role maps separately from people.
- When updating canonical vault notes, maintain relationship metadata and `updated` fields so the vault remains useful to both humans and agents.
- Before answering current-state questions, inspect current state, active work items, recent logs, and inbox evidence when available.
- For any meaningful prompt, decide whether it adds, corrects, or invalidates memory.
- Update the smallest correct canonical file when memory should change.
- Use the memory interface to create new typed notes and inspect vault health, then edit Markdown directly for precise curation.
- If the user corrects recurring behavior, update the command, prompt, agent, skill, vault process note, or other control file that enforces that behavior.
- Keep imported evidence separate from promoted memory.
- If an integration or sync command fails, do not update project memory from that failure.

View File

@@ -9,6 +9,7 @@ Use these files as the baseline context:
@README.md
@core/README.md
@core/memory/operational-memory.md
@core/integrations/memory-vault-model.md
@core/integrations/communication-model.md
@profiles/fidelity/profile.md
@vault/00-start/start-here.md

View File

@@ -7,6 +7,8 @@ Slash commands available in this workspace.
## Generic Commands
- [Workspace Context](workspace-context.md)
- [Memory Health](memory-health.md)
- [Memory Create](memory-create.md)
- [Communication Sync](communication-sync.md)
- [Archive Import](archive-import.md)
- [AI Prompt](ai-prompt.md)

View File

@@ -0,0 +1,43 @@
---
description: Create a canonical memory note using the project-agnostic memory interface
---
Create a new canonical memory note from a known type.
Supported types:
- `daily`
- `work-item`
- `person`
- `decision`
- `system`
- `workstream`
- `meeting-note`
User request:
$ARGUMENTS
Read:
@core/integrations/memory-vault-model.md
@vault/00-start/workspace-architecture.md
@vault/09-templates/work-item.md
@vault/09-templates/person.md
@vault/09-templates/decision.md
@vault/09-templates/system.md
@vault/09-templates/workstream.md
Instructions:
- Parse the requested type, slug, and title from `$ARGUMENTS`.
- Use `bash scripts/memory/memory.sh create <type> <slug> [title]` when type and slug are clear.
- If type or slug is not clear, do not guess. Explain the expected format.
- After creation, inspect the generated file and update metadata/content if the user provided enough context.
- Keep the note in the canonical folder selected by the memory interface.
Return:
1. Created file
2. Any metadata/content refined
3. Any missing details the user may want to add

View File

@@ -0,0 +1,27 @@
---
description: Check canonical memory health and Obsidian adapter status
---
Run a lightweight health check for the canonical memory vault.
Read:
@core/integrations/memory-vault-model.md
@core/integrations/obsidian-model.md
@vault/00-start/workspace-architecture.md
Run:
!`bash scripts/memory/memory.sh health`
Structured views:
!`for base in work-items people decisions daily systems workstreams; do echo "\n## $base"; bash scripts/memory/memory.sh base-query "$base" paths 2>/dev/null || bash scripts/memory/memory.sh base-query "$base" 2>/dev/null || true; done`
Respond with:
1. Health status
2. Any issues that should be fixed now
3. Any optional Obsidian-only improvements
Do not modify project memory unless the user explicitly asks for cleanup.

View File

@@ -8,6 +8,7 @@ Read core:
@core/README.md
@core/memory/operational-memory.md
@core/integrations/memory-vault-model.md
@core/integrations/communication-model.md
@core/profiles/create-project-profile.md

View File

@@ -27,6 +27,7 @@ The detailed operating rules live in:
- `vault/03-context/process/ai-to-ai-prompting.md`
- `vault/03-context/process/workspace-model.md`
- `vault/03-context/process/memory-promotion-rules.md`
- `core/integrations/memory-vault-model.md`
- `vault/04-people/manager.md`
- `vault/04-people/index.md`
- `vault/02-work-items/index.md`
@@ -37,6 +38,8 @@ These are also loaded through `opencode.json`.
- Assume the workspace may contain stale context until checked.
- Treat `vault/` as the canonical clean memory for humans and AI. Treat `ai/inbox/` as raw evidence only.
- Treat `scripts/memory/` as the project-agnostic interface for creating notes, searching memory, querying Bases, and running vault health checks.
- Treat `scripts/obsidian/` as the current Obsidian adapter, not as the core memory abstraction.
- Keep Obsidian Bases clean: templates in `vault/09-templates/` must not be treated as real notes, and role mapping files such as `vault/04-people/manager.md` must not be typed as people.
- Maintain useful vault properties when editing canonical notes, especially work-item relationships (`systems`, `workstreams`, `people`, `related`) and daily note fields (`focus`, `work-items`, `blockers`).
- Before answering questions that depend on current work state, inspect `vault/01-current/current-work.md` and the latest relevant daily note under `vault/06-daily/`.
@@ -58,6 +61,8 @@ These are also loaded through `opencode.json`.
- If a new prompt corrects prior understanding, update the canonical file directly instead of keeping both versions alive.
- Do not ask what should be saved when the correct destination is already clear.
- If the user provides durable new facts, update the appropriate context files instead of leaving the new information only in chat history.
- When creating a new canonical note from a known type, prefer `scripts/memory/memory.sh create <type> <slug> [title]` so type-to-folder routing stays centralized.
- If the Obsidian CLI adapter fails, fall back to direct Markdown operations and treat the failure as tooling status, not project context.
- If a previous context file is now stale or inaccurate, update that file directly.
- Prefer correcting canonical context over appending contradictory notes.
- Keep changes concise and auditable.

View File

@@ -119,7 +119,12 @@ Repeatable working guides for:
### /scripts
Helpers for future automation around context generation and communication drafting.
Helpers for automation around memory access, context generation, communication drafting, and imports.
- `scripts/memory/` -> project-agnostic interface for canonical memory
- `scripts/obsidian/` -> current Obsidian adapter and URI helpers
- `scripts/mattermost/` -> live communication connector
- `scripts/slack/` -> historical archive importer
### /vault/.obsidian
@@ -129,6 +134,8 @@ Open `vault/` as the Obsidian vault. Do not open the repository root as the vaul
Portable vault configuration can be versioned, while local layout and plugin runtime files are ignored.
Obsidian is the current visual interface over canonical memory. The reusable memory access layer is `scripts/memory/`, so the workspace can later swap Obsidian for another Markdown knowledge tool without changing the memory model.
---
## Daily Usage
@@ -218,6 +225,8 @@ This keeps AI output tied to the latest workspace state instead of relying on ch
## Generic Commands
- `/workspace-context` -> load core plus active profile
- `/memory-health` -> check canonical memory and adapter health
- `/memory-create` -> create a typed canonical note through the memory interface
- `/communication-sync` -> sync live communication evidence and promote high-confidence memory
- `/archive-import` -> import historical archive evidence
- `/ai-prompt` -> generate a self-contained prompt for another AI
@@ -293,8 +302,16 @@ Runtime/evidence stays outside the vault:
Ignored Obsidian runtime files include workspace layout, plugin cache, snippets, and local plugin installs.
Obsidian URI helpers live under `scripts/obsidian/`:
Project-agnostic memory helpers live under `scripts/memory/`:
- `scripts/memory/memory.sh create <type> <slug> [title]`
- `scripts/memory/memory.sh search <query> [folder]`
- `scripts/memory/memory.sh base-query <base-name> [format]`
- `scripts/memory/memory.sh health`
Obsidian adapter helpers live under `scripts/obsidian/`:
- `scripts/obsidian/cli.sh <obsidian-cli-command>`
- `scripts/obsidian/open.sh <vault-relative-path>`
- `scripts/obsidian/daily.sh`
- `scripts/obsidian/search.sh <query>`

View File

@@ -64,3 +64,5 @@ See `core/integrations/communication-model.md` for the reusable connector contra
Optional navigation layers such as Obsidian should read the same Markdown files instead of copying memory into a second store.
See `core/integrations/obsidian-model.md` for the recommended vault model.
The project-agnostic memory interface lives in `scripts/memory/` and is documented in `core/integrations/memory-vault-model.md`. Agents should use that interface for note creation, vault search, Base queries, and health checks, while still editing Markdown directly for precise curation.

View File

@@ -0,0 +1,92 @@
# Memory Vault Integration Model
## Purpose
Define a project-agnostic interface for reading, creating, searching, and validating the canonical Markdown memory.
Obsidian is the current human-facing implementation, but the workspace should not depend on Obsidian-specific behavior for core memory maintenance.
---
## Layers
### Canonical Memory
The source of truth is plain Markdown under `vault/`.
Agents should edit canonical notes directly when precision matters because direct edits produce auditable diffs.
### Memory Interface
Use `scripts/memory/memory.sh` for vault-level operations:
- `root` prints the configured memory root
- `read <path>` reads a vault-relative note
- `search <query> [folder]` searches memory
- `create <type> <slug> [title]` creates a note in the canonical folder from the matching template
- `base-query <base-name> [format]` queries a structured view when supported
- `health` checks required files and optional navigation health
### Tool Adapter
The current adapter is Obsidian CLI through `scripts/obsidian/cli.sh`.
If Obsidian CLI is unavailable or fails, `scripts/memory/memory.sh` falls back to direct Markdown operations unless `AIW_MEMORY_BACKEND=obsidian` is explicitly set.
---
## Configuration
- `AIW_MEMORY_VAULT_DIR`: canonical memory directory. Defaults to `<workspace-root>/vault`.
- `AIW_MEMORY_BACKEND`: `auto`, `files`, or `obsidian`. Defaults to `auto`.
- `AIW_OBSIDIAN_VAULT_DIR`: Obsidian-specific vault override, still supported by the adapter.
- `AIW_OBSIDIAN_VAULT_NAME`: Obsidian URI vault name override for URI wrappers.
Backend meanings:
- `auto`: use Obsidian CLI when useful and available; otherwise use direct Markdown.
- `files`: force direct Markdown behavior.
- `obsidian`: require Obsidian CLI for supported operations and fail if unavailable.
---
## Note Type Routing
The memory interface owns type-to-folder routing:
- `daily` -> `vault/06-daily/`
- `work-item` -> `vault/02-work-items/`
- `person` -> `vault/04-people/`
- `decision` -> `vault/05-decisions/`
- `system` -> `vault/03-context/systems/`
- `workstream` -> `vault/03-context/workstreams/`
- `meeting-note` -> `vault/06-daily/`
Templates live in `vault/09-templates/`.
This gives agents an automatic destination for new notes without coupling the rule to Obsidian.
---
## Agent Rules
- Use the memory interface when creating new canonical notes from a known type.
- Use the memory interface for broad search, Base queries, and vault health checks.
- Edit Markdown directly for precise memory curation, corrections, and content updates.
- Do not treat Obsidian CLI failure as project context.
- Do not require Obsidian to be running for core workspace operation.
- Keep raw evidence outside `vault/`; promote only curated memory.
---
## Useful Obsidian CLI Operations
When available, Obsidian CLI improves human-facing memory workflows:
- `search:context` gives Obsidian-aware search context.
- `create path=... template=...` creates notes from templates.
- `base:query` reads Bases as structured views.
- `properties` and `property:*` inspect or update metadata.
- `unresolved`, `orphans`, `links`, `backlinks`, and `tags` support vault health checks.
These are optional enhancements. The canonical memory remains Markdown.

View File

@@ -126,11 +126,23 @@ Do not use Bases for raw inboxes, generated evidence, scripts, or runtime logs.
## CLI Wrappers
Use `scripts/obsidian/` for non-interactive Obsidian URI helpers:
Use `scripts/memory/` as the project-agnostic memory interface.
Use `scripts/obsidian/` only as the current Obsidian adapter:
- `cli.sh` runs the official Obsidian CLI from the configured vault directory.
- `uri.sh` generates encoded Obsidian URIs.
- `open.sh <vault-relative-path>` opens a note.
- `daily.sh` opens the configured daily note.
- `search.sh <query>` opens Obsidian search.
- `open.sh <vault-relative-path>` opens a note through Obsidian URI.
- `daily.sh` opens the configured daily note through Obsidian URI.
- `search.sh <query>` opens Obsidian search through Obsidian URI.
The wrappers default to `vault/` and can be overridden with `AIW_OBSIDIAN_VAULT_DIR` and `AIW_OBSIDIAN_VAULT_NAME`.
The memory interface can use Obsidian CLI when available and fall back to direct Markdown operations when it is not.
The agent should depend on `scripts/memory/memory.sh` for portable operations:
- `create <type> <slug> [title]`
- `search <query> [folder]`
- `base-query <base-name> [format]`
- `health`
This keeps Obsidian replaceable while still making the current vault easier to use.

View File

@@ -58,6 +58,8 @@ Use generic variables first:
```text
AIW_PROJECT_PROFILE=<project>
AIW_MEMORY_VAULT_DIR=<optional custom vault path>
AIW_MEMORY_BACKEND=auto
AIW_CHANNEL_PREFIX=<project-or-team-prefix>
AIW_MATTERMOST_SYNC_CMD=<optional custom command>
AIW_SLACK_EXPORT_PATH=<optional archive path>
@@ -72,6 +74,8 @@ Connector secrets belong in ignored `.env` files, not in profile files.
Start with generic commands:
- `/workspace-context`
- `/memory-health`
- `/memory-create`
- `/communication-sync`
- `/archive-import`
- `/standup`
@@ -92,6 +96,7 @@ Before using the workspace for real work:
- run a communication sync with test channels or a dry sample
- generate one standup from sample context
- verify that imported evidence and promoted memory stay separate
- run `bash scripts/memory/memory.sh health`
---
@@ -99,6 +104,8 @@ Before using the workspace for real work:
Open `vault/` as the Obsidian vault.
Use `scripts/memory/` as the project-agnostic memory interface. Obsidian is the default visual and CLI-backed adapter, but profile logic should not depend on Obsidian.
Recommended rules:
- keep `vault/` as the clean canonical human/AI memory

View File

@@ -43,6 +43,7 @@
"./README.md",
"./core/README.md",
"./core/memory/operational-memory.md",
"./core/integrations/memory-vault-model.md",
"./core/integrations/communication-model.md",
"./core/integrations/obsidian-model.md",
"./core/profiles/create-project-profile.md",

View File

@@ -70,6 +70,8 @@ Core Fidelity context remains in:
Raw communication evidence remains outside the vault under `ai/inbox/` and generated connector folders.
Memory access should go through the project-agnostic `scripts/memory/` interface for note creation, search, Base queries, and health checks. Obsidian is the current visual and CLI adapter, not a Fidelity-specific dependency.
Important domain themes:
- Fid4 consumer validation
@@ -87,6 +89,8 @@ Important domain themes:
Generic commands:
- `/workspace-context`
- `/memory-health`
- `/memory-create`
- `/communication-sync`
- `/archive-import`
- `/ai-prompt`

View File

@@ -3,11 +3,26 @@
This directory contains helpers that automate:
- context aggregation
- canonical memory access
- standup generation
- manager update drafting
- Mattermost-ready message formatting
- historical Slack import
The project-agnostic memory interface lives in:
- `scripts/memory/`
Recommended commands:
```bash
bash scripts/memory/memory.sh health
bash scripts/memory/memory.sh search "PDIAP-15765"
bash scripts/memory/memory.sh create work-item pdiap-15999 "Example title"
```
This interface defaults to Markdown files under `vault/`, uses Obsidian CLI when useful and available, and falls back to direct file operations.
The default workspace Mattermost extractor now lives in:
- `scripts/mattermost/`

42
scripts/memory/README.md Normal file
View File

@@ -0,0 +1,42 @@
# Memory Scripts
This directory exposes a project-agnostic interface for the canonical memory vault.
The current implementation uses Markdown files under `vault/` and can optionally delegate to the Obsidian CLI when it is available. The agent should depend on this memory interface, not on Obsidian-specific behavior, so the backing tool can be replaced later.
## Backend Model
- `AIW_MEMORY_VAULT_DIR` points to the canonical Markdown memory directory.
- `AIW_MEMORY_BACKEND` defaults to `auto`.
- `auto` uses Obsidian CLI when it is useful and available, then falls back to direct Markdown operations.
- `files` forces direct Markdown operations.
- `obsidian` requires the Obsidian CLI for supported operations.
## Commands
```bash
bash scripts/memory/memory.sh root
bash scripts/memory/memory.sh read 01-current/current-work.md
bash scripts/memory/memory.sh search "PDIAP-15765"
bash scripts/memory/memory.sh create work-item pdiap-15999 "Example title"
bash scripts/memory/memory.sh base-query work-items
bash scripts/memory/memory.sh health
```
## Note Creation
`create` maps note types to canonical folders and templates:
- `daily` -> `06-daily/`
- `work-item` -> `02-work-items/`
- `person` -> `04-people/`
- `decision` -> `05-decisions/`
- `system` -> `03-context/systems/`
- `workstream` -> `03-context/workstreams/`
- `meeting-note` -> `06-daily/`
The template is resolved from `09-templates/<type>.md`. When Obsidian CLI is available, the script uses `obsidian create path=... template=...`. Otherwise it creates the file directly from the template and resolves the basic `{{title}}`, `{{date}}`, and `{{time}}` variables.
## Agent Rule
Use these scripts for vault-level operations such as creating notes, querying Bases, validating navigation health, and searching memory. For precise content edits, agents should still edit Markdown files directly so diffs remain auditable.

335
scripts/memory/memory.sh Executable file
View File

@@ -0,0 +1,335 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
VAULT_DIR="${AIW_MEMORY_VAULT_DIR:-${AIW_OBSIDIAN_VAULT_DIR:-$WORKSPACE_ROOT/vault}}"
BACKEND="${AIW_MEMORY_BACKEND:-auto}"
OBSIDIAN_CLI="$WORKSPACE_ROOT/scripts/obsidian/cli.sh"
usage() {
cat >&2 <<'EOF'
usage: memory.sh <command> [args]
commands:
root
read <vault-relative-path>
search <query> [folder]
create <type> <slug> [title]
base-query <base-name> [format]
health
EOF
}
die() {
echo "$*" >&2
exit 1
}
ensure_vault() {
[[ -d "$VAULT_DIR" ]] || die "vault directory not found: $VAULT_DIR"
}
has_obsidian() {
[[ "$BACKEND" != "files" ]] && command -v obsidian >/dev/null 2>&1 && [[ -x "$OBSIDIAN_CLI" ]]
}
require_obsidian_if_forced() {
if [[ "$BACKEND" == "obsidian" ]] && ! has_obsidian; then
die "AIW_MEMORY_BACKEND=obsidian but Obsidian CLI is unavailable"
fi
}
safe_path() {
local rel="$1"
[[ "$rel" != /* ]] || die "path must be vault-relative: $rel"
[[ "$rel" != *".."* ]] || die "path must not contain '..': $rel"
printf '%s/%s\n' "$VAULT_DIR" "$rel"
}
template_name_for_type() {
case "$1" in
daily) echo "daily" ;;
work-item) echo "work-item" ;;
person) echo "person" ;;
decision) echo "decision" ;;
system) echo "system" ;;
workstream) echo "workstream" ;;
meeting-note) echo "meeting-note" ;;
*) die "unsupported note type: $1" ;;
esac
}
folder_for_type() {
case "$1" in
daily) echo "06-daily" ;;
work-item) echo "02-work-items" ;;
person) echo "04-people" ;;
decision) echo "05-decisions" ;;
system) echo "03-context/systems" ;;
workstream) echo "03-context/workstreams" ;;
meeting-note) echo "06-daily" ;;
*) die "unsupported note type: $1" ;;
esac
}
note_path_for_type() {
local type="$1"
local slug="$2"
local folder
folder="$(folder_for_type "$type")"
case "$slug" in
*.md) echo "$folder/$slug" ;;
*) echo "$folder/$slug.md" ;;
esac
}
render_template() {
local template="$1"
local title="$2"
local slug="$3"
python3 - "$template" "$title" "$slug" <<'PY'
import datetime
import pathlib
import re
import sys
template = pathlib.Path(sys.argv[1])
title = sys.argv[2]
slug = sys.argv[3]
now = datetime.datetime.now()
ticket = slug.removesuffix(".md").upper()
ticket = re.sub(r"^([A-Z]+)-?(\d+)$", r"\1-\2", ticket)
content = template.read_text()
content = content.replace("{{title}}", title)
content = content.replace("{{slug}}", slug.removesuffix(".md"))
content = content.replace("{{ticket}}", ticket)
content = content.replace("{{date}}", now.strftime("%Y-%m-%d"))
content = content.replace("{{date:YYYY-MM-DD}}", now.strftime("%Y-%m-%d"))
content = content.replace("{{time}}", now.strftime("%H:%M"))
print(content, end="")
PY
}
postprocess_note() {
local rel="$1"
local title="$2"
local slug="$3"
local target
target="$(safe_path "$rel")"
[[ -f "$target" ]] || return 0
python3 - "$target" "$title" "$slug" <<'PY'
import datetime
import pathlib
import re
import sys
path = pathlib.Path(sys.argv[1])
title = sys.argv[2]
slug = sys.argv[3].removesuffix(".md")
ticket = re.sub(r"^([A-Z]+)-?(\d+)$", r"\1-\2", slug.upper())
today = datetime.datetime.now().strftime("%Y-%m-%d")
now = datetime.datetime.now().strftime("%H:%M")
content = path.read_text()
content = content.replace("{{title}}", title)
content = content.replace("{{slug}}", slug)
content = content.replace("{{ticket}}", ticket)
content = content.replace("{{date}}", today)
content = content.replace("{{date:YYYY-MM-DD}}", today)
content = content.replace("{{time}}", now)
lines = content.splitlines()
if lines[:1] == ["---"]:
try:
end = lines.index("---", 1)
except ValueError:
end = -1
if end > 0:
frontmatter = lines[1:end]
body = lines[end + 1 :]
for i, line in enumerate(frontmatter):
if line.startswith("title:"):
frontmatter[i] = f"title: {title}"
elif line.startswith("ticket:"):
frontmatter[i] = f"ticket: {ticket}"
elif line.startswith("date:"):
frontmatter[i] = f"date: {today}"
elif line.startswith("updated:"):
frontmatter[i] = f"updated: {today}"
lines = ["---", *frontmatter, "---", *body]
content = "\n".join(lines) + "\n"
path.write_text(content)
PY
}
create_direct() {
local type="$1"
local slug="$2"
local title="${3:-$slug}"
local rel target template_name template
rel="$(note_path_for_type "$type" "$slug")"
target="$(safe_path "$rel")"
template_name="$(template_name_for_type "$type")"
template="$VAULT_DIR/09-templates/$template_name.md"
[[ -e "$target" ]] && die "note already exists: $rel"
[[ -f "$template" ]] || die "template not found: 09-templates/$template_name.md"
mkdir -p "$(dirname "$target")"
render_template "$template" "$title" "$slug" > "$target"
postprocess_note "$rel" "$title" "$slug"
echo "$rel"
}
create_note() {
local type="$1"
local slug="$2"
local title="${3:-$slug}"
local rel template_name
rel="$(note_path_for_type "$type" "$slug")"
template_name="$(template_name_for_type "$type")"
require_obsidian_if_forced
if has_obsidian; then
if "$OBSIDIAN_CLI" create "path=$rel" "template=$template_name" >/dev/null 2>&1; then
postprocess_note "$rel" "$title" "$slug"
echo "$rel"
return 0
fi
[[ "$BACKEND" == "obsidian" ]] && die "obsidian create failed for $rel"
fi
create_direct "$type" "$slug" "$title"
}
search_memory() {
local query="$1"
local folder="${2:-}"
require_obsidian_if_forced
if has_obsidian; then
if [[ -n "$folder" ]]; then
if "$OBSIDIAN_CLI" search:context "query=$query" "path=$folder" "limit=50"; then
return 0
fi
else
if "$OBSIDIAN_CLI" search:context "query=$query" "limit=50"; then
return 0
fi
fi
[[ "$BACKEND" == "obsidian" ]] && die "obsidian search failed"
fi
if [[ -n "$folder" ]]; then
rg -n -- "$query" "$VAULT_DIR/$folder"
else
rg -n -- "$query" "$VAULT_DIR"
fi
}
base_query() {
local base="$1"
local format="${2:-md}"
local base_path="08-bases/${base%.base}.base"
local type_name="${base%.base}"
require_obsidian_if_forced
if has_obsidian; then
if "$OBSIDIAN_CLI" base:query "path=$base_path" "format=$format"; then
return 0
fi
[[ "$BACKEND" == "obsidian" ]] && die "obsidian base query failed for $base_path"
fi
case "$type_name" in
work-items) rg -l '^type: work-item$' "$VAULT_DIR/02-work-items" ;;
people) rg -l '^type: person$' "$VAULT_DIR/04-people" ;;
decisions) rg -l '^type: decision$' "$VAULT_DIR/05-decisions" ;;
daily) rg -l '^type: daily$' "$VAULT_DIR/06-daily" ;;
systems) rg -l '^type: system$' "$VAULT_DIR/03-context/systems" ;;
workstreams) rg -l '^type: workstream$' "$VAULT_DIR/03-context/workstreams" ;;
*) die "unsupported base fallback: $base" ;;
esac
}
health_check() {
local failed=0
local required=(
"00-start/start-here.md"
"01-current/current-work.md"
"01-current/work-items.md"
"02-work-items/index.md"
"03-context/project.md"
"04-people/index.md"
"07-maps/index.md"
"08-bases/work-items.base"
"09-templates/work-item.md"
)
ensure_vault
for rel in "${required[@]}"; do
if [[ ! -e "$VAULT_DIR/$rel" ]]; then
echo "missing: $rel"
failed=1
fi
done
if [[ -d "$WORKSPACE_ROOT/.obsidian" ]]; then
echo "unexpected root Obsidian config: .obsidian"
failed=1
fi
if has_obsidian; then
"$OBSIDIAN_CLI" unresolved total 2>/dev/null | sed 's/^/unresolved-links: /' || true
"$OBSIDIAN_CLI" orphans total 2>/dev/null | sed 's/^/orphans: /' || true
"$OBSIDIAN_CLI" tags total 2>/dev/null | sed 's/^/tags: /' || true
else
echo "obsidian-cli: unavailable; file checks only"
fi
if [[ "$failed" -eq 0 ]]; then
echo "memory health ok"
fi
exit "$failed"
}
main() {
ensure_vault
local command="${1:-}"
[[ -n "$command" ]] || { usage; exit 1; }
shift || true
case "$command" in
root)
echo "$VAULT_DIR"
;;
read)
[[ $# -eq 1 ]] || die "usage: memory.sh read <vault-relative-path>"
cat "$(safe_path "$1")"
;;
search)
[[ $# -ge 1 ]] || die "usage: memory.sh search <query> [folder]"
search_memory "$1" "${2:-}"
;;
create)
[[ $# -ge 2 ]] || die "usage: memory.sh create <type> <slug> [title]"
create_note "$1" "$2" "${3:-$2}"
;;
base-query)
[[ $# -ge 1 ]] || die "usage: memory.sh base-query <base-name> [format]"
base_query "$1" "${2:-md}"
;;
health)
health_check
;;
*)
usage
exit 1
;;
esac
}
main "$@"

20
scripts/obsidian/cli.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
VAULT_DIR="${AIW_MEMORY_VAULT_DIR:-${AIW_OBSIDIAN_VAULT_DIR:-$WORKSPACE_ROOT/vault}}"
if ! command -v obsidian >/dev/null 2>&1; then
echo "obsidian CLI is not installed or not in PATH" >&2
exit 127
fi
if [[ ! -d "$VAULT_DIR" ]]; then
echo "vault directory not found: $VAULT_DIR" >&2
exit 1
fi
cd "$VAULT_DIR"
exec obsidian "$@"

View File

@@ -4,7 +4,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
VAULT_DIR="${AIW_OBSIDIAN_VAULT_DIR:-$WORKSPACE_ROOT/vault}"
VAULT_DIR="${AIW_OBSIDIAN_VAULT_DIR:-${AIW_MEMORY_VAULT_DIR:-$WORKSPACE_ROOT/vault}}"
VAULT_NAME="${AIW_OBSIDIAN_VAULT_NAME:-$(basename "$VAULT_DIR")}"
URI="$("$SCRIPT_DIR/uri.sh" daily "vault=$VAULT_NAME")"

View File

@@ -4,7 +4,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
VAULT_DIR="${AIW_OBSIDIAN_VAULT_DIR:-$WORKSPACE_ROOT/vault}"
VAULT_DIR="${AIW_OBSIDIAN_VAULT_DIR:-${AIW_MEMORY_VAULT_DIR:-$WORKSPACE_ROOT/vault}}"
VAULT_NAME="${AIW_OBSIDIAN_VAULT_NAME:-$(basename "$VAULT_DIR")}"
if [[ $# -lt 1 ]]; then

View File

@@ -4,7 +4,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
VAULT_DIR="${AIW_OBSIDIAN_VAULT_DIR:-$WORKSPACE_ROOT/vault}"
VAULT_DIR="${AIW_OBSIDIAN_VAULT_DIR:-${AIW_MEMORY_VAULT_DIR:-$WORKSPACE_ROOT/vault}}"
VAULT_NAME="${AIW_OBSIDIAN_VAULT_NAME:-$(basename "$VAULT_DIR")}"
if [[ $# -lt 1 ]]; then

View File

@@ -60,6 +60,6 @@
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"scale": 1.0087453683557874,
"scale": 1.3297103454416062,
"close": true
}
}

View File

@@ -24,6 +24,7 @@ Common terms used in this workspace.
- `inbox`: raw or lightly processed communication evidence under `ai/inbox/`.
- `promotion`: moving high-confidence evidence into canonical memory.
- `tooling behavior`: reusable behavior encoded in commands, prompts, skills, agents, or knowledge rules.
- `memory interface`: project-agnostic scripts under `scripts/memory/` that create, search, query, and validate canonical memory without coupling the workspace to Obsidian.
---

View File

@@ -10,6 +10,8 @@ tags:
Use Obsidian to navigate and review workspace memory. Do not use it as a separate memory database.
Obsidian is the current interface over canonical Markdown memory. The workspace-level abstraction is `scripts/memory/`, so agents should use Obsidian-specific commands only through that adapter when possible.
---
## Recommended Start
@@ -126,3 +128,26 @@ For normal onboarding, keep them hidden and navigate through the named maps.
- Templates may contain the final note `type` so newly created notes are useful immediately, but Bases must exclude the template folder so template files do not appear as data rows.
- Role mapping files such as `04-people/manager.md` should use `type: role-map`, not `type: person`.
- Bookmarks should keep `00-start/start-here.md`, `00-start/onboarding.md`, current work, work items, people, and high-value Bases easy to open.
---
## CLI And Templates
The official Obsidian CLI can create notes from templates, query Bases, inspect properties, and search the vault.
In this workspace, prefer the platform-agnostic memory wrapper:
```bash
bash scripts/memory/memory.sh create work-item pdiap-15999 "Example title"
bash scripts/memory/memory.sh search "PDIAP-15765"
bash scripts/memory/memory.sh base-query work-items
bash scripts/memory/memory.sh health
```
The wrapper owns note type routing, so a `work-item` goes to `02-work-items/`, a `person` goes to `04-people/`, a `decision` goes to `05-decisions/`, and so on.
Use Obsidian CLI directly only when debugging Obsidian-specific behavior:
```bash
bash scripts/obsidian/cli.sh help
```

View File

@@ -75,6 +75,8 @@ If you are new to this project, read:
- [Communication Rules](../03-context/process/communication-rules.md)
- [Context Maintenance](../03-context/process/context-maintenance.md)
- [Workspace Architecture](workspace-architecture.md)
- Memory interface: `scripts/memory/`
- Obsidian adapter: `scripts/obsidian/`
---

View File

@@ -50,6 +50,19 @@ Runtime and generated files can be used as evidence, but durable project facts s
---
## Memory Access Layer
Use `scripts/memory/` as the platform-agnostic interface to canonical memory.
Obsidian is the current visual and CLI-backed adapter, but the source of truth remains Markdown under `vault/`.
- Agents use `scripts/memory/memory.sh create` when a new typed note is needed.
- Agents use `scripts/memory/memory.sh search` or direct Markdown reads for context lookup.
- Agents use `scripts/memory/memory.sh base-query` and `health` for structured review.
- Precise memory edits should still be made directly to Markdown so changes stay auditable.
---
## Memory Rule
Promoted memory lives in `vault/`.

View File

@@ -48,6 +48,8 @@ When the user corrects a recurring behavior, the workspace should update the fil
- `core/` for reusable project-independent behavior
- `profiles/<project>/` for project-specific assumptions
- `vault/.obsidian/` only for portable vault configuration, not project memory
- `scripts/memory/` for project-agnostic memory access, creation, search, Base queries, and health checks
- `scripts/obsidian/` for the current Obsidian adapter, not for core memory semantics
- `.opencode/commands/` for slash commands
- `prompts/` for reusable drafting templates
- `.opencode/agents/` and `AGENTS.md` for default agent behavior

View File

@@ -24,6 +24,8 @@ Commands, prompts, skills, workflows, and automation surfaces that make the work
- Commands index: `.opencode/commands/index.md`
- Workspace context: `.opencode/commands/workspace-context.md`
- Memory health: `.opencode/commands/memory-health.md`
- Memory create: `.opencode/commands/memory-create.md`
- Communication sync: `.opencode/commands/communication-sync.md`
- Archive import: `.opencode/commands/archive-import.md`
- AI prompt: `.opencode/commands/ai-prompt.md`
@@ -51,3 +53,11 @@ Commands, prompts, skills, workflows, and automation surfaces that make the work
- Story draft prompt: `prompts/story-draft.md`
- OpenCode entry workflow: `workflows/opencode-entry.md`
- Daily context sync: `workflows/daily-context-sync.md`
---
## Memory Interface
- Project-agnostic memory scripts: `scripts/memory/`
- Current Obsidian adapter: `scripts/obsidian/`
- Canonical vault: `vault/`

View File

@@ -3,7 +3,7 @@ type: decision
project: fidelity
status: proposed
date:
title:
title: "{{title}}"
systems: []
workstreams: []
people: []
@@ -11,10 +11,10 @@ related: []
tags:
- decision
- fidelity
updated:
updated: "{{date:YYYY-MM-DD}}"
---
# Decision Title
# {{title}}
## Decision
@@ -37,4 +37,3 @@ updated:
## Follow-up
-

View File

@@ -1,17 +1,17 @@
---
type: meeting-note
project: fidelity
date:
date: "{{date:YYYY-MM-DD}}"
people: []
work-items: []
systems: []
tags:
- meeting
- fidelity
updated:
updated: "{{date:YYYY-MM-DD}}"
---
# Meeting Note
# {{title}}
## Context
@@ -34,4 +34,3 @@ updated:
## Memory Updates
-

View File

@@ -9,10 +9,10 @@ related: []
tags:
- person
- fidelity
updated:
updated: "{{date:YYYY-MM-DD}}"
---
# Name
# {{title}}
## Role
@@ -35,4 +35,3 @@ updated:
## Related Context
-

View File

@@ -9,10 +9,10 @@ related: []
tags:
- system
- fidelity
updated:
updated: "{{date:YYYY-MM-DD}}"
---
# System Name
# {{title}}
## Role
@@ -35,4 +35,3 @@ updated:
## Related Context
-

View File

@@ -2,8 +2,8 @@
type: work-item
project: fidelity
status: active
ticket:
title:
ticket: "{{ticket}}"
title: "{{title}}"
systems: []
workstreams: []
people: []
@@ -11,10 +11,10 @@ related: []
tags:
- work-item
- fidelity
updated:
updated: "{{date:YYYY-MM-DD}}"
---
# Ticket - Title
# {{ticket}} - {{title}}
## Status
@@ -43,4 +43,3 @@ updated:
## Next Step
-

View File

@@ -9,10 +9,10 @@ related: []
tags:
- workstream
- fidelity
updated:
updated: "{{date:YYYY-MM-DD}}"
---
# Workstream Name
# {{title}}
## Goal
@@ -35,4 +35,3 @@ updated:
## Related Work
-