@esoteric-logic/praxis-harness 1.1.0 → 1.2.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 CHANGED
@@ -84,7 +84,7 @@ For technical research: `/discover` (structured options evaluation before decisi
84
84
 
85
85
  Key additions in this version:
86
86
  - **context-management** — context brackets (FRESH/MODERATE/DEPLETED/CRITICAL) adapt behavior to session stage
87
- - **vault** — multi-backend vault integration (obsidian, logseq, plain, custom)
87
+ - **vault** — Obsidian vault integration
88
88
 
89
89
  ## Ralph
90
90
 
@@ -111,7 +111,7 @@ Ralph is not a replacement for GSD — it runs GSD internally per story. Use GSD
111
111
  │ GSD → Superpowers → Ralph │
112
112
  ├────────────────────────────────────────┤
113
113
  │ Vault layer │
114
- obsidian | logseq | plain | custom
114
+ Obsidian
115
115
  ├────────────────────────────────────────┤
116
116
  │ Claude Code │
117
117
  │ ~/.claude/ + plugins + subagents │
@@ -131,16 +131,9 @@ More kits coming. See `docs/creating-a-kit.md` to build your own.
131
131
 
132
132
  ## Vault integration
133
133
 
134
- Praxis integrates with a persistent vault for project state, session learnings, and architecture decisions. Four backends are supported:
134
+ Praxis integrates with an Obsidian vault for project state, session learnings, and architecture decisions.
135
135
 
136
- | Backend | Description | Search tool |
137
- |---------|-------------|-------------|
138
- | `obsidian` | Obsidian vault (default) | [Obsidian CLI](https://obsidian.md) |
139
- | `logseq` | Logseq graph | ripgrep |
140
- | `plain` | Plain markdown directory (`~/.praxis-vault`) | ripgrep |
141
- | `custom` | Any directory you choose | ripgrep |
142
-
143
- The backend and vault path are configured per machine during install:
136
+ The vault path is configured per machine during install:
144
137
 
145
138
  ```json
146
139
  {
@@ -151,8 +144,7 @@ The backend and vault path are configured per machine during install:
151
144
  }
152
145
  ```
153
146
 
154
- - **obsidian**: requires [Obsidian CLI](https://obsidian.md) (enable in Obsidian Settings > General > Command line interface). Obsidian must be running for vault search.
155
- - **logseq/plain/custom**: uses ripgrep for vault search — no extra dependencies
147
+ Requires [Obsidian CLI](https://obsidian.md) (enable in Obsidian Settings > General > Command line interface). Obsidian must be running for vault search.
156
148
 
157
149
  ## Updating
158
150
 
@@ -185,7 +177,7 @@ The git-clone + `install.sh` path uses symlinks instead of copies, so edits in t
185
177
  - macOS or Linux
186
178
  - Claude Code CLI
187
179
  - Node.js 18+
188
- - Obsidian, Logseq, or a plain markdown directory (optional, for vault integration)
180
+ - Obsidian with CLI enabled (for vault integration)
189
181
 
190
182
  ## License
191
183
 
package/base/CLAUDE.md CHANGED
@@ -57,7 +57,6 @@ Vault path and backend are machine-specific. Read from `~/.claude/praxis.config.
57
57
  ```json
58
58
  { "vault_path": "/path/to/vault", "vault_backend": "obsidian" }
59
59
  ```
60
- Supported backends: `obsidian` (default), `logseq`, `plain`, `custom`.
61
60
  If config file is missing: tell the user to run `praxis/install.sh`.
62
61
  All `{vault_path}` references in rules and skills resolve from this config.
63
62
 
@@ -75,7 +74,7 @@ Context is volatile. Files are permanent. Act accordingly.
75
74
  ## Vault Protocol
76
75
  - ALWAYS run a vault search before reading vault files (see vault.md backend table).
77
76
  - Obsidian indexes in real-time — no manual update command needed.
78
- - Link format: obsidian → `[[wikilinks]]`; logseq/plain/custom standard markdown links.
77
+ - Link format: `[[wikilinks]]` for all internal vault references.
79
78
  - Detect project from CWD matching `local_path` in `_index.md`.
80
79
 
81
80
  ## MCP Servers
@@ -84,9 +83,10 @@ Registered via `claude mcp add`. Persist globally across sessions.
84
83
  | Server | Purpose | API Key |
85
84
  |--------|---------|---------|
86
85
  | context7 | Live library/API docs | None |
87
- | perplexity | AI web search | `PERPLEXITY_API_KEY` |
88
86
  | github | Repo operations, PRs, issues | `GITHUB_PERSONAL_ACCESS_TOKEN` |
89
87
 
88
+ Optional: `perplexity` (AI web search). Run `bash scripts/onboard-mcp.sh perplexity` to add.
89
+
90
90
  Check: `claude mcp list` | Manage: `bash scripts/onboard-mcp.sh [server|all]`
91
91
  Missing servers are non-blocking — features degrade gracefully.
92
92
 
@@ -135,7 +135,7 @@ Kit manifests live in `~/.claude/kits/<name>/KIT.md`.
135
135
  | `~/.claude/rules/git-workflow.md` | Commits, branches, identity verification |
136
136
  | `~/.claude/rules/security.md` | Secrets, credentials, auth patterns |
137
137
  | `~/.claude/rules/communication.md` | Client writing, no AI attribution |
138
- | `~/.claude/rules/vault.md` | Second brain integration — obsidian, logseq, or plain markdown |
138
+ | `~/.claude/rules/vault.md` | Second brain integration — Obsidian vault |
139
139
  | `~/.claude/rules/architecture.md` | ADR format, What/So What/Now What, risk docs |
140
140
  | `~/.claude/rules/context-management.md` | GSD/Ralph anti-rot, context reset protocol |
141
141
 
@@ -15,7 +15,7 @@ You are preparing a clean context reset for the current session.
15
15
  - Identify: current milestone, last 3 decisions, any STOP conditions or blockers
16
16
 
17
17
  **Step 3 — Write context checkpoint**
18
- Write a standalone checkpoint file at `{vault_path}/plans/current-phase-summary.md`:
18
+ Write a standalone checkpoint file at `{vault_path}/plans/{YYYY-MM-DD}-context-checkpoint.md`:
19
19
 
20
20
  ```markdown
21
21
  ---
@@ -58,7 +58,7 @@ Write a session snapshot to `claude-progress.json`:
58
58
  Print:
59
59
  ```
60
60
  Context checkpoint saved to:
61
- Checkpoint: {vault_path}/plans/current-phase-summary.md
61
+ Checkpoint: {vault_path}/plans/{YYYY-MM-DD}-context-checkpoint.md
62
62
  Status: {vault_path}/status.md
63
63
  Progress: {vault_path}/claude-progress.json
64
64
 
@@ -67,6 +67,6 @@ Run /clear to reset context, then paste this bootstrap prompt:
67
67
  Context reset. Bootstrap:
68
68
  1. Read project CLAUDE.md
69
69
  2. Read {vault_path}/plans/{current-plan}
70
- 3. Read {vault_path}/plans/current-phase-summary.md
70
+ 3. Read {vault_path}/plans/{YYYY-MM-DD}-context-checkpoint.md
71
71
  4. Resume from milestone: {milestone-name}
72
72
  ```
@@ -32,7 +32,7 @@ If no commands are defined: warn and ask user for the correct commands.
32
32
 
33
33
  **Step 3b — UNIFY (mandatory after all milestones verified)**
34
34
  After self-review passes, write phase summary:
35
- - Write `{vault_path}/plans/current-phase-summary.md`:
35
+ - Write `{vault_path}/plans/{YYYY-MM-DD}_{project-slug}-phase-summary.md`:
36
36
  ```markdown
37
37
  ---
38
38
  tags: [unify, {project-slug}]
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ # Stop hook — runs project lint command as an advisory check.
3
+ # Always exits 0 (never blocks session end).
4
+ set -uo pipefail
5
+
6
+ CONFIG_FILE="$HOME/.claude/praxis.config.json"
7
+
8
+ # Find project CLAUDE.md by walking up from CWD
9
+ find_project_claude_md() {
10
+ local dir="$PWD"
11
+ while [[ "$dir" != "/" ]]; do
12
+ if [[ -f "$dir/CLAUDE.md" ]]; then
13
+ echo "$dir/CLAUDE.md"
14
+ return 0
15
+ fi
16
+ dir=$(dirname "$dir")
17
+ done
18
+ return 1
19
+ }
20
+
21
+ CLAUDE_MD=$(find_project_claude_md 2>/dev/null) || exit 0
22
+
23
+ # Extract lint command from ## Commands section
24
+ LINT_CMD=$(awk '/^## Commands/,/^## /{
25
+ if (/^lint:/) { sub(/^lint:[[:space:]]*/, ""); print; exit }
26
+ }' "$CLAUDE_MD" 2>/dev/null)
27
+
28
+ if [[ -z "$LINT_CMD" || "$LINT_CMD" == "#"* ]]; then
29
+ exit 0
30
+ fi
31
+
32
+ echo "Running lint: $LINT_CMD" >&2
33
+ if eval "$LINT_CMD" 2>&1 | tail -20 >&2; then
34
+ echo "Lint: PASS" >&2
35
+ else
36
+ echo "Lint: warnings found (advisory only)" >&2
37
+ fi
38
+
39
+ exit 0
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ # PreToolUse hook — blocks Write/Edit if file contains secret patterns.
3
+ # Exit 0 = allow, Exit 2 = block with message.
4
+ set -euo pipefail
5
+
6
+ # Claude Code passes tool input as JSON via stdin for PreToolUse hooks
7
+ INPUT=$(cat)
8
+
9
+ # Extract file_path from the JSON input (works for Write, Edit tools)
10
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
11
+
12
+ if [[ -z "$FILE_PATH" || ! -f "$FILE_PATH" ]]; then
13
+ exit 0
14
+ fi
15
+
16
+ # Scan the file for secret patterns
17
+ SECRET_PATTERN='(sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36,}|pplx-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16}|Bearer [A-Za-z0-9+/]{20,})'
18
+
19
+ if rg -q "$SECRET_PATTERN" "$FILE_PATH" 2>/dev/null; then
20
+ MATCHES=$(rg -n "$SECRET_PATTERN" "$FILE_PATH" 2>/dev/null | head -5)
21
+ echo "BLOCKED: Potential secret detected in $FILE_PATH" >&2
22
+ echo "$MATCHES" >&2
23
+ echo "Remove the secret before proceeding." >&2
24
+ exit 2
25
+ fi
26
+
27
+ exit 0
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ # PreCompact hook — writes minimal checkpoint to vault before context compaction.
3
+ # Always exits 0 (advisory, never blocks compaction).
4
+ set -uo pipefail
5
+
6
+ CONFIG_FILE="$HOME/.claude/praxis.config.json"
7
+
8
+ if [[ ! -f "$CONFIG_FILE" ]]; then
9
+ exit 0
10
+ fi
11
+
12
+ VAULT_PATH=$(jq -r '.vault_path // empty' "$CONFIG_FILE" 2>/dev/null)
13
+ if [[ -z "$VAULT_PATH" || ! -d "$VAULT_PATH" ]]; then
14
+ exit 0
15
+ fi
16
+
17
+ PLANS_DIR="$VAULT_PATH/plans"
18
+ mkdir -p "$PLANS_DIR"
19
+
20
+ DATE=$(date +%Y-%m-%d)
21
+ TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
22
+ CHECKPOINT_FILE="$PLANS_DIR/$DATE-compact-checkpoint.md"
23
+
24
+ BRANCH=$(git --no-pager rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
25
+ LAST_COMMIT=$(git --no-pager log --oneline -1 2>/dev/null || echo "no commits")
26
+ PROJECT_DIR=$(basename "$PWD")
27
+
28
+ cat > "$CHECKPOINT_FILE" <<EOF
29
+ ---
30
+ tags: [checkpoint, compact]
31
+ date: $DATE
32
+ source: agent
33
+ ---
34
+ # Compact Checkpoint — $TIMESTAMP
35
+
36
+ ## Working Directory
37
+ $PWD
38
+
39
+ ## Git State
40
+ - Branch: $BRANCH
41
+ - Last commit: $LAST_COMMIT
42
+
43
+ ## Note
44
+ This checkpoint was auto-written by the PreCompact hook.
45
+ Read this file after compaction to restore context.
46
+ EOF
47
+
48
+ echo "Vault checkpoint written: $CHECKPOINT_FILE" >&2
49
+ exit 0
@@ -3,20 +3,13 @@
3
3
 
4
4
  ---
5
5
 
6
- ## Backend Detection
7
- Read `vault_backend` from `~/.claude/praxis.config.json`. Defaults to `"obsidian"` if absent.
6
+ ## Vault Backend
8
7
 
9
- | Backend | Search command | Update after write | Link format |
10
- |---------|---------------|-------------------|-------------|
11
- | `obsidian` | `obsidian search query="{query}" limit=5` | no-op (real-time indexing) | `[[wikilinks]]` |
12
- | `logseq` | `rg -l "{query}" {vault_path} --glob "*.md" \| head -5` | no-op | standard markdown links |
13
- | `plain` | `rg -l "{query}" {vault_path} --glob "*.md" \| head -5` | no-op | standard markdown links |
14
- | `custom` | `rg -l "{query}" {vault_path} --glob "*.md" \| head -5` | no-op | standard markdown links |
15
-
16
- **Note:** The Obsidian CLI requires Obsidian to be running. If Obsidian is not running, vault search will fail.
8
+ Vault backend is Obsidian. Search: `obsidian search query="{query}" limit=5`
17
9
  Scope searches with `path=` filter: `obsidian search query="{query}" path="01_Projects" limit=5`
18
10
 
19
- All commands below use the backend table above. When a step says "vault search" or "vault update", substitute the correct command for the active backend.
11
+ **Note:** The Obsidian CLI requires Obsidian to be running. If Obsidian is not running, vault search will fail.
12
+ Use `[[wikilinks]]` for all internal vault references.
20
13
 
21
14
  ## Vault Location
22
15
  Read vault_path from `~/.claude/praxis.config.json`.
@@ -51,7 +44,6 @@ Never ask "should I save this?" for the above categories — just save it.
51
44
 
52
45
  ### No manual index update needed
53
46
  - Obsidian indexes vault changes in real-time — no update command required.
54
- - For all other backends: files are read directly, no indexing needed.
55
47
 
56
48
  ---
57
49
 
@@ -83,9 +75,7 @@ source: agent | human | meeting
83
75
  ---
84
76
  ```
85
77
 
86
- Link format depends on backend:
87
- - `obsidian` → use `[[wikilinks]]` for all internal vault references
88
- - `logseq`, `plain`, `custom` → use standard markdown links
78
+ Use `[[wikilinks]]` for all internal vault references.
89
79
 
90
80
  ### Bootstrap templates
91
81
  If `status.md`, `tasks.md`, or `_index.md` are missing from a project vault directory,
@@ -140,10 +140,8 @@ Vault indexing is automatic.
140
140
 
141
141
  ## Phase 7 — Confirm & Open Vault
142
142
 
143
- Open the vault in the user's app (backend-conditional):
144
- - `obsidian` → `open "obsidian://open?vault=Obsidian"`
145
- - `logseq` → `open "logseq://graph/{vault_name}"`
146
- - `plain` / `custom` → print vault path for user to open manually
143
+ Open the vault in Obsidian:
144
+ - `open "obsidian://open?vault=Obsidian"`
147
145
 
148
146
  Print summary table with all created files and bootstrap sequence.
149
147
 
@@ -14,7 +14,6 @@ agent-memory.jsonl
14
14
  .obsidian-local/
15
15
  .obsidian/workspace
16
16
  .obsidian/workspace.json
17
- .logseq/
18
17
 
19
18
  # ── OS and Editor Noise ───────────────────────────────────────────────────
20
19
  .DS_Store
package/bin/praxis.js CHANGED
@@ -107,43 +107,7 @@ async function install() {
107
107
  const readline = require('readline/promises');
108
108
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
109
109
 
110
- console.log('');
111
- console.log(' Choose a vault backend:');
112
- console.log(' [1] Obsidian (default)');
113
- console.log(' [2] Logseq');
114
- console.log(' [3] Plain markdown (~/.praxis-vault)');
115
- console.log(' [4] Custom path');
116
- console.log('');
117
- const backendChoice = await rl.question(' Choice [1]: ');
118
- const choice = (backendChoice || '1').trim();
119
-
120
- let vaultBackend = 'obsidian';
121
- let vaultPath = '';
122
-
123
- switch (choice) {
124
- case '1':
125
- vaultBackend = 'obsidian';
126
- vaultPath = await rl.question(' Obsidian vault path: ');
127
- break;
128
- case '2':
129
- vaultBackend = 'logseq';
130
- vaultPath = await rl.question(' Logseq vault path: ');
131
- break;
132
- case '3':
133
- vaultBackend = 'plain';
134
- vaultPath = path.join(os.homedir(), '.praxis-vault');
135
- fs.mkdirSync(vaultPath, { recursive: true });
136
- ok('Created ' + vaultPath);
137
- break;
138
- case '4':
139
- vaultBackend = 'custom';
140
- vaultPath = await rl.question(' Vault path: ');
141
- break;
142
- default:
143
- vaultBackend = 'obsidian';
144
- vaultPath = await rl.question(' Vault path: ');
145
- break;
146
- }
110
+ let vaultPath = await rl.question(' Obsidian vault path: ');
147
111
  rl.close();
148
112
 
149
113
  if (vaultPath) {
@@ -153,7 +117,7 @@ async function install() {
153
117
  const config = {
154
118
  version: '1.1.0',
155
119
  vault_path: vaultPath || '',
156
- vault_backend: vaultBackend,
120
+ vault_backend: 'obsidian',
157
121
  repo_path: PKG_DIR
158
122
  };
159
123
  fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n');
@@ -162,22 +126,9 @@ async function install() {
162
126
  dim('praxis.config.json already exists — skipping');
163
127
  }
164
128
 
165
- // Tool checks (conditional on backend)
129
+ // Tool checks
166
130
  header('Tool check');
167
- let vaultBackendForCheck = 'obsidian';
168
- if (fs.existsSync(CONFIG_FILE)) {
169
- try {
170
- const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
171
- vaultBackendForCheck = cfg.vault_backend || 'obsidian';
172
- } catch {}
173
- }
174
-
175
- const baseTools = ['node', 'claude', 'jq'];
176
- if (vaultBackendForCheck === 'obsidian') {
177
- baseTools.push('obsidian');
178
- } else {
179
- baseTools.push('rg');
180
- }
131
+ const baseTools = ['node', 'claude', 'jq', 'obsidian'];
181
132
  for (const tool of baseTools) {
182
133
  if (toolExists(tool)) {
183
134
  ok(tool + ' available');
@@ -262,21 +213,9 @@ function health() {
262
213
  } catch { total++; fail('praxis.config.json is invalid JSON'); }
263
214
  }
264
215
 
265
- // Tools (conditional on backend)
216
+ // Tools
266
217
  console.log('\nTools:');
267
- let healthBackend = 'obsidian';
268
- if (fs.existsSync(CONFIG_FILE)) {
269
- try {
270
- const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
271
- healthBackend = cfg.vault_backend || 'obsidian';
272
- } catch {}
273
- }
274
- const healthTools = ['node', 'claude', 'jq'];
275
- if (healthBackend === 'obsidian') {
276
- healthTools.push('obsidian');
277
- } else {
278
- healthTools.push('rg');
279
- }
218
+ const healthTools = ['node', 'claude', 'jq', 'obsidian'];
280
219
  for (const tool of healthTools) {
281
220
  check(toolExists(tool), tool + ' available');
282
221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esoteric-logic/praxis-harness",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Layered Claude Code harness — workflow discipline, AI-Kits, persistent vault integration",
5
5
  "bin": {
6
6
  "praxis-harness": "./bin/praxis.js"
@@ -19,7 +19,14 @@
19
19
  "type": "git",
20
20
  "url": "https://github.com/arcanesme/praxis.git"
21
21
  },
22
- "keywords": ["claude-code", "ai-harness", "vault", "obsidian", "logseq", "workflow", "gsd"],
22
+ "keywords": [
23
+ "claude-code",
24
+ "ai-harness",
25
+ "vault",
26
+ "obsidian",
27
+ "workflow",
28
+ "gsd"
29
+ ],
23
30
  "author": "arcanesme",
24
31
  "license": "MIT",
25
32
  "engines": {
@@ -88,18 +88,10 @@ if [[ -f "$CONFIG_FILE" ]]; then
88
88
  fi
89
89
  fi
90
90
 
91
- # ─── Required tools (conditional on backend) ───
91
+ # ─── Required tools ───
92
92
  echo ""
93
93
  echo "Tools:"
94
- VAULT_BACKEND=""
95
- if [[ -f "$CONFIG_FILE" ]]; then
96
- VAULT_BACKEND=$(jq -r '.vault_backend // "obsidian"' "$CONFIG_FILE" 2>/dev/null)
97
- fi
98
- if [[ "$VAULT_BACKEND" == "obsidian" ]]; then
99
- check "command -v obsidian" "Obsidian CLI available"
100
- else
101
- check "command -v rg" "ripgrep available"
102
- fi
94
+ check "command -v obsidian" "Obsidian CLI available"
103
95
  check "command -v node" "node available"
104
96
  check "command -v claude" "claude available"
105
97
  check "command -v jq" "jq available"
@@ -109,7 +101,7 @@ echo ""
109
101
  echo "MCP Servers:"
110
102
  if command -v claude &>/dev/null; then
111
103
  MCP_LIST=$(claude mcp list 2>/dev/null || true)
112
- for server in context7 perplexity github; do
104
+ for server in context7 github; do
113
105
  TOTAL=$((TOTAL + 1))
114
106
  if echo "$MCP_LIST" | grep -q "$server"; then
115
107
  echo " ✓ $server registered"
package/scripts/update.sh CHANGED
@@ -57,18 +57,10 @@ for kit_dir in "$REPO_PATH"/kits/*/; do
57
57
  fi
58
58
  done
59
59
 
60
- # ─── Verify key tools (conditional on backend) ───
60
+ # ─── Verify key tools ───
61
61
  echo ""
62
62
  echo "Verifying tools..."
63
- VAULT_BACKEND=""
64
- if [[ -f "$CONFIG_FILE" ]]; then
65
- VAULT_BACKEND=$(jq -r '.vault_backend // "obsidian"' "$CONFIG_FILE" 2>/dev/null)
66
- fi
67
- if [[ "$VAULT_BACKEND" == "obsidian" ]]; then
68
- command -v obsidian &>/dev/null && echo " ✓ Obsidian CLI available" || echo " ✗ Obsidian CLI not found"
69
- else
70
- command -v rg &>/dev/null && echo " ✓ ripgrep available" || echo " ✗ ripgrep not found"
71
- fi
63
+ command -v obsidian &>/dev/null && echo " ✓ Obsidian CLI available" || echo " ✗ Obsidian CLI not found"
72
64
  command -v claude &>/dev/null && echo " ✓ claude available" || echo " ✗ claude not found"
73
65
  command -v node &>/dev/null && echo " ✓ node available" || echo " ✗ node not found"
74
66