holistic 0.6.0 → 0.6.2
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/CHANGELOG.md +11 -0
- package/README.md +53 -45
- package/dist/__tests__/privacy-artifacts.test.d.ts +5 -0
- package/dist/__tests__/privacy-artifacts.test.d.ts.map +1 -0
- package/dist/__tests__/privacy-artifacts.test.js +75 -0
- package/dist/__tests__/privacy-artifacts.test.js.map +1 -0
- package/dist/__tests__/redact.test.d.ts +5 -0
- package/dist/__tests__/redact.test.d.ts.map +1 -0
- package/dist/__tests__/redact.test.js +91 -0
- package/dist/__tests__/redact.test.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +48 -27
- package/dist/cli.js.map +1 -1
- package/dist/core/git-hooks.d.ts +1 -0
- package/dist/core/git-hooks.d.ts.map +1 -1
- package/dist/core/git-hooks.js +2 -0
- package/dist/core/git-hooks.js.map +1 -1
- package/dist/core/redact.d.ts.map +1 -1
- package/dist/core/redact.js +6 -2
- package/dist/core/redact.js.map +1 -1
- package/dist/core/setup.d.ts +33 -2
- package/dist/core/setup.d.ts.map +1 -1
- package/dist/core/setup.js +124 -4
- package/dist/core/setup.js.map +1 -1
- package/dist/mcp-server.d.ts +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +18 -10
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1 - 2026-04-12
|
|
4
|
+
|
|
5
|
+
Trust & Privacy Hardening (M006). This release implements a "Consent-First" read-only architecture, strengthens privacy boundaries for portable state, and introduces configurable MCP logging and enhanced secret redaction.
|
|
6
|
+
|
|
7
|
+
- Implemented **Read-Only Command Policy**: Routine commands (`status`, `resume`, `diff`, `search`) are now strictly non-mutating. They will surface health warnings for outdated hooks but will never fix them silently.
|
|
8
|
+
- Hardened **Privacy Mode Enforcement**: When `portableState` is disabled (Privacy Mode), generated shell scripts and Git hooks now exit early to prevent any accidental remote state synchronization.
|
|
9
|
+
- Added **MCP Logging Privacy**: Introduced `mcpLogging` configuration (`off` | `minimal` | `default`). Defaults to `minimal` to prevent session objectives and titles from leaking into system logs.
|
|
10
|
+
- Expanded **Secret Redaction**: significantly strengthened the redaction engine to identify and scrub JWT tokens, Bearer tokens, AWS keys, and PEM private key blocks.
|
|
11
|
+
- Added **Redaction Quality Tests**: Integrated 8 new unit tests to verify that sensitive patterns are correctly scrubbed while preserving normal text.
|
|
12
|
+
- Added **SECURITY.md**: Published a comprehensive technical disclosure of Holistic's trust model, data residency guarantees, and safety architecture.
|
|
13
|
+
|
|
3
14
|
## 0.6.0 - 2026-04-11
|
|
4
15
|
|
|
5
16
|
Comprehensive Reliability & UX Refinement (M005). This release finalizes the security hardening milestone, introduces granular bootstrap controls, and adds support for explicit portable-state management.
|
package/README.md
CHANGED
|
@@ -17,35 +17,35 @@ Shared memory for AI agents, built into your repo.
|
|
|
17
17
|
[](./LICENSE)
|
|
18
18
|
[](./package.json)
|
|
19
19
|
|
|
20
|
-
### One command. Every agent. Zero re-explaining. ✨
|
|
20
|
+
### One command. Every agent. Zero re-explaining. ✨
|
|
21
|
+
No context loss. No fragile handoffs.
|
|
21
22
|
|
|
22
23
|
Holistic gives your AI agents shared memory inside the repo itself. When you switch from Claude to Codex to Gemini, the next agent can see what happened last time, what not to break, and what should happen next.
|
|
23
24
|
|
|
24
|
-
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
+
## Why trust Holistic? 🔒
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
Holistic is designed to be **safe to install, inspectable, and predictable**.
|
|
29
30
|
|
|
30
|
-
- 🔐
|
|
31
|
-
-
|
|
31
|
+
- 🔐 **Security-first design** — local-first, no telemetry, no external services
|
|
32
|
+
- 🧭 **Consent-first model** — system changes only happen via `bootstrap` or `repair`
|
|
33
|
+
- 👀 **Read-only by default** — routine commands never silently modify your repo or machine
|
|
34
|
+
- 🔍 **Fully transparent** — readable scripts, visible hooks, no hidden behavior
|
|
35
|
+
- 🛡️ **Privacy mode by default** — no remote sync unless explicitly enabled
|
|
36
|
+
- 🧪 60+ automated tests covering core flows
|
|
32
37
|
- 🛠️ Actively maintained (frequent releases)
|
|
33
|
-
- 🔍 Transparent local setup (see `SECURITY.md`)
|
|
34
|
-
|
|
35
|
-
> Early beta, but built to be safe, inspectable, and predictable.
|
|
36
38
|
|
|
39
|
+
> See [SECURITY.md](./SECURITY.md) for full technical details.
|
|
37
40
|
|
|
38
41
|
---
|
|
39
42
|
|
|
40
|
-
|
|
41
43
|
## Get started in 30 seconds ⚡
|
|
42
44
|
|
|
43
45
|
Open your project repo in PowerShell, Terminal, Command Prompt, or whatever shell you normally use.
|
|
44
46
|
|
|
45
47
|
Requires Node.js 24+.
|
|
46
48
|
|
|
47
|
-
Run these two commands:
|
|
48
|
-
|
|
49
49
|
```bash
|
|
50
50
|
npm install -g holistic
|
|
51
51
|
holistic bootstrap --yes
|
|
@@ -61,26 +61,22 @@ That is enough to get the basic Holistic workflow working.
|
|
|
61
61
|
|
|
62
62
|
If you want the fuller install and setup details, jump to [Quick start](#quick-start-).
|
|
63
63
|
|
|
64
|
-
|
|
65
64
|
---
|
|
66
65
|
|
|
67
|
-
|
|
68
66
|
## The problem 😵
|
|
69
67
|
|
|
70
68
|
If you use more than one AI coding assistant, the workflow usually falls apart:
|
|
71
69
|
|
|
72
|
-
- 🔁 You re-explain the project every session
|
|
73
|
-
- 🐞 Bugs come back because the next agent does not know what was already fixed
|
|
74
|
-
- 🧠 Progress gets lost when context windows end
|
|
75
|
-
- 💥 Agents undo each other because there is no durable handoff
|
|
76
|
-
- 🌫️ It is hard to tell what is actually done
|
|
70
|
+
- 🔁 You re-explain the project every session
|
|
71
|
+
- 🐞 Bugs come back because the next agent does not know what was already fixed
|
|
72
|
+
- 🧠 Progress gets lost when context windows end
|
|
73
|
+
- 💥 Agents undo each other because there is no durable handoff
|
|
74
|
+
- 🌫️ It is hard to tell what is actually done
|
|
77
75
|
|
|
78
76
|
Holistic fixes that by making the repo the source of truth.
|
|
79
77
|
|
|
80
|
-
|
|
81
78
|
---
|
|
82
79
|
|
|
83
|
-
|
|
84
80
|
## What it feels like with HOLISTIC 🌿
|
|
85
81
|
|
|
86
82
|
Run one setup command on a machine:
|
|
@@ -91,19 +87,15 @@ holistic bootstrap
|
|
|
91
87
|
|
|
92
88
|
Then daily use is mostly:
|
|
93
89
|
|
|
94
|
-
1. Open the repo in Codex, Claude, or another supported app
|
|
95
|
-
2. Start a fresh session
|
|
96
|
-
3. Ask the agent to read `AGENTS.md` and `HOLISTIC.md
|
|
97
|
-
4. Let Holistic carry continuity through checkpoints, handoffs, and repo memory
|
|
90
|
+
1. Open the repo in Codex, Claude, or another supported app
|
|
91
|
+
2. Start a fresh session
|
|
92
|
+
3. Ask the agent to read `AGENTS.md` and `HOLISTIC.md`
|
|
93
|
+
4. Let Holistic carry continuity through checkpoints, handoffs, and repo memory
|
|
98
94
|
|
|
99
95
|
Most days, you do not need to keep a terminal process open or manually re-brief the agent.
|
|
100
96
|
|
|
101
|
-
`holistic bootstrap` is a machine setup command, not just a repo setup command. By default it can install local startup helpers and configure Claude Desktop MCP on that machine.
|
|
102
|
-
|
|
103
|
-
|
|
104
97
|
---
|
|
105
98
|
|
|
106
|
-
|
|
107
99
|
## How it works 🧭
|
|
108
100
|
|
|
109
101
|
```text
|
|
@@ -122,9 +114,24 @@ Holistic checkpoints and handoffs keep repo memory current
|
|
|
122
114
|
The next agent picks up without a long re-explanation
|
|
123
115
|
```
|
|
124
116
|
|
|
125
|
-
|
|
126
117
|
---
|
|
127
118
|
|
|
119
|
+
## 🔒 Security & Trust Model
|
|
120
|
+
|
|
121
|
+
Holistic is designed to be **transparent, audit-safe, and consent-first**.
|
|
122
|
+
|
|
123
|
+
- **Read-Only by Default** — routine commands warn instead of mutating
|
|
124
|
+
- **Explicit Consent** — system changes require `bootstrap` or `repair`
|
|
125
|
+
- **Granular Control** — apply only what you want (`--yes-*` flags)
|
|
126
|
+
- **Privacy Mode** — sync disabled by default with enforced guards
|
|
127
|
+
- **MCP Logging Controls** — configurable visibility
|
|
128
|
+
- **Redaction** — JWTs, tokens, AWS keys, and PEM blocks scrubbed
|
|
129
|
+
- **Traceable Activity** — logs written locally
|
|
130
|
+
- **Git-native behavior** — respects `.gitignore`
|
|
131
|
+
|
|
132
|
+
For full details, see [SECURITY.md](./SECURITY.md).
|
|
133
|
+
|
|
134
|
+
---
|
|
128
135
|
|
|
129
136
|
## Quick start 🚀
|
|
130
137
|
|
|
@@ -309,19 +316,21 @@ The portable repo memory (config, state, context, sessions) is meant to be commi
|
|
|
309
316
|
|
|
310
317
|
## Commands
|
|
311
318
|
|
|
319
|
+
| Command | Description |
|
|
320
|
+
| :--- | :--- |
|
|
312
321
|
| `holistic init` | Base repo setup and scaffolding |
|
|
313
322
|
| `holistic bootstrap` | One-step machine setup. Required `--yes` for Core Setup, or granular `--yes-*` flags. |
|
|
314
|
-
| `holistic doctor` |
|
|
315
|
-
| `holistic repair` | Regenerates `.holistic/system/` helpers |
|
|
316
|
-
| `holistic resume / start
|
|
323
|
+
| `holistic doctor` | Health checks and configuration diagnostics **(Read-only)** |
|
|
324
|
+
| `holistic repair` | Regenerates `.holistic/system/` local helpers |
|
|
325
|
+
| `holistic resume / start` | Loads project recap and prints state **(Read-only)** |
|
|
317
326
|
| `holistic start-new` | Starts a fresh session |
|
|
318
|
-
| `holistic checkpoint
|
|
319
|
-
| `holistic handoff` | Ends a session with a handoff message |
|
|
320
|
-
| `holistic status` | Shows current state and
|
|
321
|
-
| `holistic diff
|
|
322
|
-
| `holistic search
|
|
323
|
-
| `holistic serve` | Runs the thin MCP server |
|
|
324
|
-
| `holistic watch` | Foreground daemon
|
|
327
|
+
| `holistic checkpoint` | Saves progress and context **(Stateful)** |
|
|
328
|
+
| `holistic handoff` | Ends a session with a handoff message **(Stateful)** |
|
|
329
|
+
| `holistic status` | Shows current state and sync activity **(Read-only)** |
|
|
330
|
+
| `holistic diff` | Compares two session IDs **(Read-only)** |
|
|
331
|
+
| `holistic search` | Finds and retrieves session state **(Read-only)** |
|
|
332
|
+
| `holistic serve` | Runs the thin MCP server **(Read-only)** |
|
|
333
|
+
| `holistic watch` | Foreground daemon; automatically creates checkpoints **(Mutating)** |
|
|
325
334
|
|
|
326
335
|
### Slash command helper text (agent-facing)
|
|
327
336
|
|
|
@@ -413,18 +422,17 @@ For support and troubleshooting, see [SUPPORT.md](./SUPPORT.md).
|
|
|
413
422
|
---
|
|
414
423
|
|
|
415
424
|
|
|
416
|
-
## Security, Privacy, and Trust 🔒
|
|
417
|
-
|
|
418
425
|
Holistic is designed to be **transparent, audit-safe, and consent-first**. It is a shared memory layer that stays in your repo, not a cloud service that watches your screen.
|
|
419
426
|
|
|
420
427
|
### Trust Architecture:
|
|
421
|
-
- **
|
|
428
|
+
- **Read-Only by Default**: Routine commands (`status`, `resume`, `diff`) are strictly non-mutating. They will only **warn** you if git hooks are missing or outdated, rather than fixing them silently.
|
|
429
|
+
- **Granular Consent**: `holistic bootstrap` uses a "Consent-First" model. It displays a summary of system-modifying actions and requires an explicit `--yes` for the Core Setup (hooks, daemon, MCP, attributes).
|
|
422
430
|
- **Surgical Control**: Use granular flags (`--yes-hooks`, `--yes-daemon`, `--yes-mcp`, `--yes-attr`, `--yes-claude`) to apply only the specific integrations you want.
|
|
423
|
-
- **Privacy Mode
|
|
424
|
-
- **
|
|
431
|
+
- **Privacy Mode Enforcement**: When `portableState` is disabled (the default), all generated sync scripts and git hooks contain early-exit guards to prevent any accidental remote traffic.
|
|
432
|
+
- **MCP Logging Privacy**: Control what Holistic reports to your agent UI. Set `mcpLogging` to `"off"`, `"minimal"` (default), or `"default"` in `.holistic/config.json`.
|
|
433
|
+
- **Advanced Redaction**: Holistic automatically scrubs JWTs, Bearer tokens, AWS keys, and PEM blocks from all generated session metadata to prevent context leakage.
|
|
425
434
|
- **Traceable Activity**: Background sync operations (PowerShell/Bash) are visible and logged with timestamps to `.holistic/system/sync.log`.
|
|
426
435
|
- **Git-Native Snapshotting**: The repo snapshot logic uses native `git ls-files`, ensuring that your `.gitignore` rules are perfectly respected and performance stays $O(\text{repo size})$.
|
|
427
|
-
- **Zero Shell Injection**: Internal commit logic has been stripped of shell wrappers to eliminate command injection risks.
|
|
428
436
|
|
|
429
437
|
### What it does:
|
|
430
438
|
- Writes session state into `.holistic/` inside your repo (committed files you control)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy-artifacts.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/privacy-artifacts.test.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,KAAK;;;GA2EjB,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { initializeHolistic } from '../core/setup.js';
|
|
7
|
+
import { getRuntimePaths } from '../core/state.js';
|
|
8
|
+
function makeTempRepo() {
|
|
9
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "holistic-privacy-test-"));
|
|
10
|
+
execFileSync("git", ["init"], { cwd: root });
|
|
11
|
+
execFileSync("git", ["config", "user.name", "Test User"], { cwd: root });
|
|
12
|
+
execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: root });
|
|
13
|
+
return root;
|
|
14
|
+
}
|
|
15
|
+
export const tests = [
|
|
16
|
+
{
|
|
17
|
+
name: "generated artifacts contain privacy guards when portableState is off",
|
|
18
|
+
run: () => {
|
|
19
|
+
const rootDir = makeTempRepo();
|
|
20
|
+
try {
|
|
21
|
+
initializeHolistic(rootDir, {
|
|
22
|
+
portableState: false,
|
|
23
|
+
installGitHooks: true,
|
|
24
|
+
platform: "win32"
|
|
25
|
+
});
|
|
26
|
+
const paths = getRuntimePaths(rootDir);
|
|
27
|
+
const prePush = fs.readFileSync(path.join(rootDir, ".git", "hooks", "pre-push"), "utf8");
|
|
28
|
+
assert.match(prePush, /exit 0 # Portable state disabled \(Privacy Mode\)/);
|
|
29
|
+
const syncPs1 = fs.readFileSync(path.join(rootDir, ".holistic", "system", "sync-state.ps1"), "utf8");
|
|
30
|
+
assert.match(syncPs1, /Write-Host 'Holistic sync skipped: portableState is disabled \(Privacy Mode\).'/);
|
|
31
|
+
assert.match(syncPs1, /exit 0/);
|
|
32
|
+
const restorePs1 = fs.readFileSync(path.join(rootDir, ".holistic", "system", "restore-state.ps1"), "utf8");
|
|
33
|
+
assert.match(restorePs1, /Write-Host 'Holistic restore skipped: portableState is disabled \(Privacy Mode\).'/);
|
|
34
|
+
assert.match(restorePs1, /exit 0/);
|
|
35
|
+
initializeHolistic(rootDir, {
|
|
36
|
+
portableState: false,
|
|
37
|
+
installGitHooks: true,
|
|
38
|
+
platform: "linux"
|
|
39
|
+
});
|
|
40
|
+
const syncSh = fs.readFileSync(path.join(rootDir, ".holistic", "system", "sync-state.sh"), "utf8");
|
|
41
|
+
assert.match(syncSh, /echo 'Holistic sync skipped: portableState is disabled \(Privacy Mode\).'/);
|
|
42
|
+
assert.match(syncSh, /exit 0/);
|
|
43
|
+
const restoreSh = fs.readFileSync(path.join(rootDir, ".holistic", "system", "restore-state.sh"), "utf8");
|
|
44
|
+
assert.match(restoreSh, /echo 'Holistic restore skipped: portableState is disabled \(Privacy Mode\).'/);
|
|
45
|
+
assert.match(restoreSh, /exit 0/);
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "generated artifacts do NOT contain privacy guards when portableState is on",
|
|
54
|
+
run: () => {
|
|
55
|
+
const rootDir = makeTempRepo();
|
|
56
|
+
try {
|
|
57
|
+
initializeHolistic(rootDir, {
|
|
58
|
+
portableState: true,
|
|
59
|
+
installGitHooks: true,
|
|
60
|
+
platform: "linux"
|
|
61
|
+
});
|
|
62
|
+
const prePush = fs.readFileSync(path.join(rootDir, ".git", "hooks", "pre-push"), "utf8");
|
|
63
|
+
assert.doesNotMatch(prePush, /Privacy Mode/);
|
|
64
|
+
const syncSh = fs.readFileSync(path.join(rootDir, ".holistic", "system", "sync-state.sh"), "utf8");
|
|
65
|
+
assert.doesNotMatch(syncSh, /Privacy Mode/);
|
|
66
|
+
const restoreSh = fs.readFileSync(path.join(rootDir, ".holistic", "system", "restore-state.sh"), "utf8");
|
|
67
|
+
assert.doesNotMatch(restoreSh, /Privacy Mode/);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
];
|
|
75
|
+
//# sourceMappingURL=privacy-artifacts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy-artifacts.test.js","sourceRoot":"","sources":["../../src/__tests__/privacy-artifacts.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/redact.test.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,KAAK;;;GAuFjB,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { sanitizeText } from '../core/redact.js';
|
|
3
|
+
export const tests = [
|
|
4
|
+
{
|
|
5
|
+
name: "sanitizeText redacts OpenAI-style sk- keys",
|
|
6
|
+
run: () => {
|
|
7
|
+
const input = "Use key sk-Abc123Abc123Abc123 for access";
|
|
8
|
+
const output = sanitizeText(input);
|
|
9
|
+
assert.strictEqual(output, "Use key [REDACTED_SECRET] for access");
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: "sanitizeText redacts GitHub PATs",
|
|
14
|
+
run: () => {
|
|
15
|
+
const input = "My token is ghp_foobarbazqux123";
|
|
16
|
+
const output = sanitizeText(input);
|
|
17
|
+
assert.strictEqual(output, "My token is [REDACTED_SECRET]");
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "sanitizeText redacts JWT tokens",
|
|
22
|
+
run: () => {
|
|
23
|
+
const input = "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoyNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
24
|
+
const output = sanitizeText(input);
|
|
25
|
+
assert.strictEqual(output, "Authorization: [REDACTED_JWT]");
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "sanitizeText redacts Bearer tokens",
|
|
30
|
+
run: () => {
|
|
31
|
+
const input = "Header: Bearer abc.123.def-456";
|
|
32
|
+
const output = sanitizeText(input);
|
|
33
|
+
assert.strictEqual(output, "Header: Bearer [REDACTED]");
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "sanitizeText redacts AWS Access Key IDs",
|
|
38
|
+
run: () => {
|
|
39
|
+
const input = "AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE";
|
|
40
|
+
const output = sanitizeText(input);
|
|
41
|
+
assert.strictEqual(output, "AWS_ACCESS_KEY_ID=[REDACTED_AWS_KEY]");
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "sanitizeText redacts PEM Private Key blocks",
|
|
46
|
+
run: () => {
|
|
47
|
+
const input = `Here is the key:
|
|
48
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
49
|
+
MIIEpQIBAAKCAQEA75v...
|
|
50
|
+
...more...
|
|
51
|
+
-----END RSA PRIVATE KEY-----
|
|
52
|
+
Keep it safe.`;
|
|
53
|
+
const output = sanitizeText(input);
|
|
54
|
+
assert.ok(output.includes("[REDACTED_PEM_PRIVATE_KEY]"));
|
|
55
|
+
assert.ok(!output.includes("MIIEpQIBAAKCAQEA75v"));
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "sanitizeText redacts assignment-style secrets",
|
|
60
|
+
run: () => {
|
|
61
|
+
const input = "password: mypassword123; secret=shhh";
|
|
62
|
+
const output = sanitizeText(input);
|
|
63
|
+
assert.strictEqual(output, "password: [REDACTED]; secret= [REDACTED]");
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "sanitizeText redacts Azure keys",
|
|
68
|
+
run: () => {
|
|
69
|
+
const input = "azure_key = abc123def456ghi789jkl012mno345pqr";
|
|
70
|
+
const output = sanitizeText(input);
|
|
71
|
+
assert.strictEqual(output, "azure_key = [REDACTED]");
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "sanitizeText redacts Stripe keys",
|
|
76
|
+
run: () => {
|
|
77
|
+
const input = "stripe_key: sk_test_51...abc";
|
|
78
|
+
const output = sanitizeText(input);
|
|
79
|
+
assert.strictEqual(output, "stripe_key: [REDACTED]");
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "sanitizeText preserves normal text",
|
|
84
|
+
run: () => {
|
|
85
|
+
const input = "The quick brown fox jumps over the lazy dog.";
|
|
86
|
+
const output = sanitizeText(input);
|
|
87
|
+
assert.strictEqual(output, input);
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
//# sourceMappingURL=redact.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.test.js","sourceRoot":"","sources":["../../src/__tests__/redact.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAA4C,YAAY,EAAE,aAAa,EAAgB,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA2EvJ,wBAAgB,cAAc,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAA4C,YAAY,EAAE,aAAa,EAAgB,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA2EvJ,wBAAgB,cAAc,IAAI,MAAM,CA2BvC;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAoFD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,GAAG,YAAY,CAcnG;AAMD,wBAAgB,UAAU,CAAC,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAoE1G;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM,CA8D1E"}
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
6
6
|
import { renderRepoLocalCliCommands } from './core/cli-fallback.js';
|
|
7
7
|
import { captureRepoSnapshot, clearPendingCommit, commitPendingChanges, writePendingCommit } from './core/git.js';
|
|
8
8
|
import { writeDerivedDocs } from './core/docs.js';
|
|
9
|
-
import { bootstrapHolistic, getSetupStatus, initializeHolistic, refreshHolisticHooks, repairHolistic } from './core/setup.js';
|
|
9
|
+
import { bootstrapHolistic, checkHolisticHooksStatus, getSetupStatus, initializeHolistic, refreshHolisticHooks, repairHolistic, validateRuntimeConfig } from './core/setup.js';
|
|
10
10
|
import { printSplash, printSplashError, renderSplash } from './core/splash.js';
|
|
11
11
|
import { requestAutoSync } from './core/sync.js';
|
|
12
12
|
import { runDaemonTick } from './daemon.js';
|
|
@@ -71,30 +71,29 @@ function getVersion() {
|
|
|
71
71
|
export function renderHelpText() {
|
|
72
72
|
return `Holistic CLI v${getVersion()}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
Setup Commands:
|
|
75
75
|
holistic init [--install-daemon] [--install-hooks] [--platform win32|darwin|linux] [--interval 30] [--remote origin] [--state-ref refs/holistic/state] [--state-branch holistic/state]
|
|
76
76
|
holistic bootstrap [--platform win32|darwin|linux] [--interval 30] [--yes]
|
|
77
|
-
holistic doctor
|
|
78
77
|
holistic repair [--platform win32|darwin|linux] [--refresh-hooks false] [--install-daemon true] [--configure-mcp true]
|
|
79
|
-
holistic start [--agent codex|claude|antigravity|gemini|copilot|cursor|goose|gsd|gsd2] [--continue] [--json]
|
|
80
|
-
holistic resume [--agent codex|claude|antigravity|gemini|copilot|cursor|goose|gsd|gsd2] [--continue] [--json]
|
|
81
|
-
holistic checkpoint --reason "<reason>" [--status "<status>"] [--plan "<step>"]... [--completion-kind natural-breakpoint|task-complete|slice-complete|milestone-complete] [--completion-source agent|system] [--completion-recorded-at <iso8601>]
|
|
82
|
-
holistic checkpoint --fixed "<bug>" [--fix-files "<file>"] [--fix-risk "<what reintroduces it>"]
|
|
83
|
-
holistic handoff [--draft] [--summary "<summary>"] [--next "<step>"]... [--commit]
|
|
84
|
-
holistic start-new [--goal "<goal>"] [--title "<title>"] [--plan "<step>"]...
|
|
85
|
-
holistic status
|
|
86
|
-
holistic diff --from "<session-id>" --to "<session-id>" [--format text|json]
|
|
87
|
-
holistic search --id "<session-id>" [--format text|json]
|
|
88
|
-
holistic serve
|
|
89
|
-
holistic watch [--agent codex|claude|antigravity|gemini|copilot|cursor|goose|gsd|gsd2] [--interval 60]
|
|
90
78
|
|
|
91
|
-
|
|
92
|
-
holistic
|
|
93
|
-
holistic
|
|
79
|
+
Read-Only & Diagnostic Commands:
|
|
80
|
+
holistic status | View current repository health and sync status.
|
|
81
|
+
holistic doctor | Run deep diagnostics and configuration validation.
|
|
82
|
+
holistic diff | Compare session state between two points in time.
|
|
83
|
+
holistic search | Find and retrieve session state by ID.
|
|
84
|
+
holistic resume | Load the latest project recap and continue work.
|
|
85
|
+
|
|
86
|
+
Stateful & Mutating Commands:
|
|
87
|
+
holistic checkpoint | Capture current work progress (reason required).
|
|
88
|
+
holistic handoff | Prepare a narrative handoff for the next session.
|
|
89
|
+
holistic start-new | Initialize a new session with a fresh goal.
|
|
90
|
+
holistic watch | Foreground daemon mode; may create checkpoints automatically.
|
|
94
91
|
|
|
95
|
-
|
|
92
|
+
Checkpoint examples:
|
|
93
|
+
holistic checkpoint --reason "tests passed" --status "Focused verification is green"
|
|
94
|
+
holistic checkpoint --reason "feature complete" --status "Ready for handoff"
|
|
96
95
|
|
|
97
|
-
|
|
96
|
+
Use checkpoint at natural breakpoints like tests passed, bug fixed, feature complete, or focus change. Use handoff as the final safety valve when the session is ending.
|
|
98
97
|
`;
|
|
99
98
|
}
|
|
100
99
|
function printHelp() {
|
|
@@ -108,6 +107,13 @@ function reportHookWarnings(warnings) {
|
|
|
108
107
|
process.stderr.write(`${warning}\n`);
|
|
109
108
|
}
|
|
110
109
|
}
|
|
110
|
+
function warnIfHooksOutdated(rootDir) {
|
|
111
|
+
const result = checkHolisticHooksStatus(rootDir);
|
|
112
|
+
if (result.refreshed.length > 0) {
|
|
113
|
+
process.stderr.write(`\u26A0 Holistic hooks are outdated. Run 'holistic repair' to refresh them.\n`);
|
|
114
|
+
process.stderr.write(` Hint: Keeping hooks current ensures session continuity and privacy guards are active.\n`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
111
117
|
function refreshHooksBeforeCommand(rootDir) {
|
|
112
118
|
const hookResult = refreshHolisticHooks(rootDir);
|
|
113
119
|
reportHookWarnings(hookResult.warnings);
|
|
@@ -427,13 +433,33 @@ async function handleDoctor(rootDir, parsed) {
|
|
|
427
433
|
printSplash({
|
|
428
434
|
message: "running holistic health check...",
|
|
429
435
|
});
|
|
436
|
+
const paths = getRuntimePaths(rootDir);
|
|
430
437
|
const status = getSetupStatus(rootDir);
|
|
431
438
|
process.stdout.write("\nSystem Configuration:\n\n");
|
|
432
439
|
printSetupStatusTable(status);
|
|
440
|
+
const findings = validateRuntimeConfig(paths);
|
|
441
|
+
if (findings.length > 0) {
|
|
442
|
+
process.stdout.write("\nConfiguration Diagnostics:\n\n");
|
|
443
|
+
for (const f of findings) {
|
|
444
|
+
const icon = f.level === "error" ? "✖" : f.level === "warn" ? "⚠" : "ℹ";
|
|
445
|
+
process.stdout.write(`${icon} ${f.field.padEnd(20)} | ${f.level.padEnd(10)} | ${f.message}\n`);
|
|
446
|
+
}
|
|
447
|
+
const errors = findings.filter(f => f.level === "error").length;
|
|
448
|
+
const warns = findings.filter(f => f.level === "warn").length;
|
|
449
|
+
if (errors > 0)
|
|
450
|
+
process.stdout.write("\n✖ config invalid\n");
|
|
451
|
+
else if (warns > 0)
|
|
452
|
+
process.stdout.write("\n⚠ config loaded with safe fallbacks\n");
|
|
453
|
+
else
|
|
454
|
+
process.stdout.write("\n✓ config valid\n");
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
process.stdout.write("\n✓ config valid\n");
|
|
458
|
+
}
|
|
433
459
|
process.stdout.write("\nSync Diagnostics:\n\n");
|
|
434
460
|
process.stdout.write(renderSyncStatus(rootDir));
|
|
435
461
|
const healthy = status.every(s => s.status === "ok");
|
|
436
|
-
if (healthy) {
|
|
462
|
+
if (healthy && findings.every(f => f.level !== "error")) {
|
|
437
463
|
process.stdout.write("\n\u2713 Holistic is healthy and correctly configured on this machine.\n");
|
|
438
464
|
}
|
|
439
465
|
else {
|
|
@@ -489,7 +515,7 @@ Repo-local CLI: ${repairFallback}
|
|
|
489
515
|
return 0;
|
|
490
516
|
}
|
|
491
517
|
async function handleResume(rootDir, parsed) {
|
|
492
|
-
|
|
518
|
+
warnIfHooksOutdated(rootDir);
|
|
493
519
|
const agent = asAgent(firstFlag(parsed.flags, "agent", "unknown"));
|
|
494
520
|
if (firstFlag(parsed.flags, "continue") === "true") {
|
|
495
521
|
const mutateResult = mutateState(rootDir, (state) => continueFromLatest(rootDir, state, agent));
|
|
@@ -526,7 +552,6 @@ async function handleResume(rootDir, parsed) {
|
|
|
526
552
|
return 0;
|
|
527
553
|
}
|
|
528
554
|
async function handleCheckpoint(rootDir, parsed) {
|
|
529
|
-
refreshHooksBeforeCommand(rootDir);
|
|
530
555
|
const mutateResult = mutateState(rootDir, (state, paths) => {
|
|
531
556
|
const regressions = listFlag(parsed.flags, "regression");
|
|
532
557
|
const fixed = firstFlag(parsed.flags, "fixed");
|
|
@@ -577,7 +602,6 @@ async function handleCheckpoint(rootDir, parsed) {
|
|
|
577
602
|
return 0;
|
|
578
603
|
}
|
|
579
604
|
async function handleStartNew(rootDir, parsed) {
|
|
580
|
-
refreshHooksBeforeCommand(rootDir);
|
|
581
605
|
const agent = asAgent(firstFlag(parsed.flags, "agent", "unknown"));
|
|
582
606
|
let goal = firstFlag(parsed.flags, "goal");
|
|
583
607
|
const title = firstFlag(parsed.flags, "title");
|
|
@@ -598,7 +622,6 @@ async function handleStartNew(rootDir, parsed) {
|
|
|
598
622
|
return 0;
|
|
599
623
|
}
|
|
600
624
|
async function handleHandoff(rootDir, parsed) {
|
|
601
|
-
refreshHooksBeforeCommand(rootDir);
|
|
602
625
|
const { state, paths } = loadState(rootDir);
|
|
603
626
|
if (!state.activeSession) {
|
|
604
627
|
process.stderr.write("No active session to hand off.\n");
|
|
@@ -759,7 +782,7 @@ function renderSyncStatus(rootDir) {
|
|
|
759
782
|
return lines.join("\n") + "\n";
|
|
760
783
|
}
|
|
761
784
|
async function handleStatus(rootDir) {
|
|
762
|
-
|
|
785
|
+
warnIfHooksOutdated(rootDir);
|
|
763
786
|
const { state } = loadState(rootDir);
|
|
764
787
|
process.stdout.write(renderStatus(rootDir, state));
|
|
765
788
|
return 0;
|
|
@@ -831,7 +854,6 @@ async function handleSearch(rootDir, parsed) {
|
|
|
831
854
|
return 0;
|
|
832
855
|
}
|
|
833
856
|
async function handleServe(rootDir) {
|
|
834
|
-
refreshHooksBeforeCommand(rootDir);
|
|
835
857
|
printSplashError({
|
|
836
858
|
message: "starting MCP server on stdio...",
|
|
837
859
|
});
|
|
@@ -863,7 +885,6 @@ async function handleMarkCommit(rootDir, parsed) {
|
|
|
863
885
|
return 0;
|
|
864
886
|
}
|
|
865
887
|
async function handleWatch(rootDir, parsed) {
|
|
866
|
-
refreshHooksBeforeCommand(rootDir);
|
|
867
888
|
const intervalSeconds = Number.parseInt(firstFlag(parsed.flags, "interval", "60"), 10);
|
|
868
889
|
const agent = asAgent(firstFlag(parsed.flags, "agent", "unknown"));
|
|
869
890
|
process.stdout.write(`Watching repo every ${intervalSeconds}s for checkpoint-worthy changes.\n`);
|