holistic 0.5.5 → 0.6.1
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 +23 -0
- package/README.md +51 -40
- 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 +75 -0
- package/dist/__tests__/redact.test.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +43 -17
- package/dist/cli.js.map +1 -1
- package/dist/core/git-hooks.d.ts +2 -0
- package/dist/core/git-hooks.d.ts.map +1 -1
- package/dist/core/git-hooks.js +54 -0
- package/dist/core/git-hooks.js.map +1 -1
- package/dist/core/redact.d.ts.map +1 -1
- package/dist/core/redact.js +5 -1
- package/dist/core/redact.js.map +1 -1
- package/dist/core/setup.d.ts +20 -1
- package/dist/core/setup.d.ts.map +1 -1
- package/dist/core/setup.js +66 -14
- package/dist/core/setup.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +16 -6
- package/dist/mcp-server.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
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
|
+
|
|
14
|
+
## 0.6.0 - 2026-04-11
|
|
15
|
+
|
|
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.
|
|
17
|
+
|
|
18
|
+
- Added **Granular Bootstrap Flags**: Users can now surgically enable setup items with `--yes-hooks`, `--yes-daemon`, `--yes-mcp`, `--yes-attr`, and `--yes-claude`.
|
|
19
|
+
- Added `--portable` flag to `init` and `bootstrap` to explicitly toggle **Portable State (Privacy Mode)** during setup.
|
|
20
|
+
- Refined **Bootstrap Pre-flight UX**: The pre-flight check now clearly differentiates between "Core Configuration" (covered by `--yes`) and "Optional/Explicit" items.
|
|
21
|
+
- Fixed **Runtime Script Resolution**: Resolved a critical production bug where the CLI incorrectly searched for `.ts` files in built environments; now correctly resolves `.js` files when TypeScript stripping is unavailable.
|
|
22
|
+
- Hardened **Read-Only Diagnostics**: Refactored `holistic doctor` and `getSetupStatus` to be strictly read-only, ensuring health checks never inadvertently modify Git hooks or repository state.
|
|
23
|
+
- Improved **MCP Server Transparency**: Sanitized startup logging to prevent context leakage in system logs while maintaining full context availability via the `holistic_resume` tool.
|
|
24
|
+
- Aligned **Claude Code Hook Detection**: Fixed a bug where `holistic doctor` misreported Claude hook status by incorrectly checking the filesystem instead of `settings.json`.
|
|
25
|
+
|
|
3
26
|
## 0.5.5 - 2026-04-10
|
|
4
27
|
|
|
5
28
|
Major Security & Trust Hardening (M005) to eliminate silent automation and improve auditability.
|
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
|
|
|
@@ -178,12 +185,14 @@ Advanced overrides:
|
|
|
178
185
|
```bash
|
|
179
186
|
holistic bootstrap --state-ref refs/holistic/state
|
|
180
187
|
holistic bootstrap --state-branch holistic/state
|
|
188
|
+
holistic bootstrap --portable
|
|
181
189
|
```
|
|
182
190
|
|
|
183
|
-
If you want repo scaffolding without changing local desktop integrations or daemon startup on the current machine, use:
|
|
191
|
+
If you want repo scaffolding without changing local desktop integrations or daemon startup on the current machine, you can use granular flags to be surgical:
|
|
184
192
|
|
|
185
193
|
```bash
|
|
186
|
-
|
|
194
|
+
# Only install Git hooks and managed attributes
|
|
195
|
+
holistic bootstrap --yes-hooks --yes-attr
|
|
187
196
|
```
|
|
188
197
|
|
|
189
198
|
**What to commit:**
|
|
@@ -208,7 +217,7 @@ One-time machine setup:
|
|
|
208
217
|
|
|
209
218
|
- Run `holistic bootstrap`.
|
|
210
219
|
- By default it scaffolds repo files, installs hooks, sets up daemon startup, and configures supported integrations such as Claude Desktop MCP on the current machine.
|
|
211
|
-
-
|
|
220
|
+
- To be surgical about what is applied, use granular flags like `--yes-hooks`, `--yes-daemon`, or `--yes-mcp`.
|
|
212
221
|
|
|
213
222
|
Normal use:
|
|
214
223
|
|
|
@@ -307,8 +316,10 @@ The portable repo memory (config, state, context, sessions) is meant to be commi
|
|
|
307
316
|
|
|
308
317
|
## Commands
|
|
309
318
|
|
|
319
|
+
| Command | Description |
|
|
320
|
+
| :--- | :--- |
|
|
310
321
|
| `holistic init` | Base repo setup and scaffolding |
|
|
311
|
-
| `holistic bootstrap` | One-step machine setup. Required `--yes`
|
|
322
|
+
| `holistic bootstrap` | One-step machine setup. Required `--yes` for Core Setup, or granular `--yes-*` flags. |
|
|
312
323
|
| `holistic doctor` | Runs health checks on machine setup and sync logs |
|
|
313
324
|
| `holistic repair` | Regenerates `.holistic/system/` helpers |
|
|
314
325
|
| `holistic resume / start --agent <name>` | Loads project recap and prints state |
|
|
@@ -411,17 +422,17 @@ For support and troubleshooting, see [SUPPORT.md](./SUPPORT.md).
|
|
|
411
422
|
---
|
|
412
423
|
|
|
413
424
|
|
|
414
|
-
## Security, Privacy, and Trust 🔒
|
|
415
|
-
|
|
416
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.
|
|
417
426
|
|
|
418
427
|
### Trust Architecture:
|
|
419
|
-
- **
|
|
420
|
-
- **
|
|
421
|
-
- **
|
|
422
|
-
- **
|
|
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).
|
|
430
|
+
- **Surgical Control**: Use granular flags (`--yes-hooks`, `--yes-daemon`, `--yes-mcp`, `--yes-attr`, `--yes-claude`) to apply only the specific integrations you want.
|
|
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.
|
|
434
|
+
- **Traceable Activity**: Background sync operations (PowerShell/Bash) are visible and logged with timestamps to `.holistic/system/sync.log`.
|
|
423
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})$.
|
|
424
|
-
- **Zero Shell Injection**: Internal commit logic has been stripped of shell wrappers to eliminate command injection risks.
|
|
425
436
|
|
|
426
437
|
### What it does:
|
|
427
438
|
- Writes session state into `.holistic/` inside your repo (committed files you control)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/redact.test.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,KAAK;;;GAuEjB,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
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 preserves normal text",
|
|
68
|
+
run: () => {
|
|
69
|
+
const input = "The quick brown fox jumps over the lazy dog.";
|
|
70
|
+
const output = sanitizeText(input);
|
|
71
|
+
assert.strictEqual(output, input);
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
//# sourceMappingURL=redact.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.test.js","sourceRoot":"","sources":["../../src/__tests__/redact.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB;QACE,IAAI,EAAE,4CAA4C;QAClD,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG,0CAA0C,CAAC;YACzD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,sCAAsC,CAAC,CAAC;QACrE,CAAC;KACF;IACD;QACE,IAAI,EAAE,kCAAkC;QACxC,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG,iCAAiC,CAAC;YAChD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,+BAA+B,CAAC,CAAC;QAC9D,CAAC;KACF;IACD;QACE,IAAI,EAAE,iCAAiC;QACvC,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG,4KAA4K,CAAC;YAC3L,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,+BAA+B,CAAC,CAAC;QAC9D,CAAC;KACF;IACD;QACE,IAAI,EAAE,oCAAoC;QAC1C,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG,gCAAgC,CAAC;YAC/C,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAAC;QAC1D,CAAC;KACF;IACD;QACE,IAAI,EAAE,yCAAyC;QAC/C,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG,wCAAwC,CAAC;YACvD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,sCAAsC,CAAC,CAAC;QACrE,CAAC;KACF;IACD;QACE,IAAI,EAAE,6CAA6C;QACnD,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG;;;;;cAKN,CAAC;YACT,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,CAAC;YACzD,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC;QACrD,CAAC;KACF;IACD;QACE,IAAI,EAAE,+CAA+C;QACrD,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG,sCAAsC,CAAC;YACrD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,0CAA0C,CAAC,CAAC;QACzE,CAAC;KACF;IACD;QACE,IAAI,EAAE,oCAAoC;QAC1C,GAAG,EAAE,GAAG,EAAE;YACR,MAAM,KAAK,GAAG,8CAA8C,CAAC;YAC7D,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;KACF;CACF,CAAC"}
|
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,CA4BvC;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;
|
|
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,CA4BvC;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAmFD,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 } 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';
|
|
@@ -108,6 +108,12 @@ function reportHookWarnings(warnings) {
|
|
|
108
108
|
process.stderr.write(`${warning}\n`);
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
function warnIfHooksOutdated(rootDir) {
|
|
112
|
+
const result = checkHolisticHooksStatus(rootDir);
|
|
113
|
+
if (result.refreshed.length > 0) {
|
|
114
|
+
process.stderr.write(`\u26A0 Holistic hooks are outdated. Run 'holistic repair' to refresh them.\n`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
111
117
|
function refreshHooksBeforeCommand(rootDir) {
|
|
112
118
|
const hookResult = refreshHolisticHooks(rootDir);
|
|
113
119
|
reportHookWarnings(hookResult.warnings);
|
|
@@ -135,7 +141,7 @@ function runtimeScript(name) {
|
|
|
135
141
|
const runtimeDir = path.dirname(currentFile);
|
|
136
142
|
const useStripTypes = extension === ".ts";
|
|
137
143
|
return {
|
|
138
|
-
scriptPath: path.resolve(runtimeDir, `${name}${useStripTypes ? ".ts" : ".
|
|
144
|
+
scriptPath: path.resolve(runtimeDir, `${name}${useStripTypes ? ".ts" : ".js"}`),
|
|
139
145
|
useStripTypes,
|
|
140
146
|
};
|
|
141
147
|
}
|
|
@@ -307,14 +313,17 @@ async function handleInit(rootDir, parsed) {
|
|
|
307
313
|
const platformFlag = firstFlag(parsed.flags, "platform", process.platform);
|
|
308
314
|
const platform = platformFlag === "windows" ? "win32" : platformFlag === "macos" ? "darwin" : platformFlag === "linux" ? "linux" : platformFlag;
|
|
309
315
|
const intervalSeconds = Number.parseInt(firstFlag(parsed.flags, "interval", "30"), 10);
|
|
316
|
+
const portableState = firstFlag(parsed.flags, "portable") === "true" ? true : firstFlag(parsed.flags, "portable") === "false" ? false : undefined;
|
|
310
317
|
const result = initializeHolistic(rootDir, {
|
|
311
|
-
installDaemon: firstFlag(parsed.flags, "
|
|
312
|
-
installGitHooks: firstFlag(parsed.flags, "
|
|
318
|
+
installDaemon: firstFlag(parsed.flags, "yes-daemon") === "true",
|
|
319
|
+
installGitHooks: firstFlag(parsed.flags, "yes-hooks") === "true",
|
|
320
|
+
installGitAttributes: firstFlag(parsed.flags, "yes-attr", "true") !== "false",
|
|
313
321
|
platform: platform,
|
|
314
322
|
intervalSeconds,
|
|
315
323
|
remote: firstFlag(parsed.flags, "remote", "origin"),
|
|
316
324
|
stateRef: firstFlag(parsed.flags, "state-ref"),
|
|
317
325
|
stateBranch: firstFlag(parsed.flags, "state-branch"),
|
|
326
|
+
portableState,
|
|
318
327
|
});
|
|
319
328
|
reportHookWarnings(result.gitHookWarnings);
|
|
320
329
|
const statusItems = [];
|
|
@@ -357,18 +366,33 @@ async function handleBootstrap(rootDir, parsed) {
|
|
|
357
366
|
printSplash({
|
|
358
367
|
message: "bootstrap pre-flight: pending actions",
|
|
359
368
|
});
|
|
360
|
-
process.stdout.write("\nHolistic needs to make the following changes to your system:\n\n");
|
|
369
|
+
process.stdout.write("\nHolistic needs to make the following changes to your system (some outside this repo):\n\n");
|
|
361
370
|
printSetupStatusTable(status);
|
|
362
|
-
process.stdout.write("\nRun with --yes to apply
|
|
371
|
+
process.stdout.write("\nRun with --yes to apply the Core Configuration:\n");
|
|
372
|
+
process.stdout.write(" ✓ Git hooks, Background daemon, MCP config, Git attributes\n");
|
|
373
|
+
process.stdout.write("\nOptional / Explicit flags:\n");
|
|
374
|
+
process.stdout.write(" --yes-claude Install Claude Code SessionStart hooks\n");
|
|
375
|
+
process.stdout.write(" --portable Enable Portable State (remote-sync via git ref)\n");
|
|
376
|
+
process.stdout.write("\nGranular overrides:\n");
|
|
377
|
+
process.stdout.write(" --yes-hooks, --yes-daemon, --yes-mcp, --yes-attr\n");
|
|
363
378
|
return 1;
|
|
364
379
|
}
|
|
365
380
|
printSplash({
|
|
366
381
|
message: "bootstrapping holistic on this machine...",
|
|
367
382
|
});
|
|
383
|
+
const installDaemon = firstFlag(parsed.flags, "yes-daemon") === "true" || (confirmed && firstFlag(parsed.flags, "install-daemon", "true") !== "false");
|
|
384
|
+
const installGitHooks = firstFlag(parsed.flags, "yes-hooks") === "true" || (confirmed && firstFlag(parsed.flags, "install-hooks", "true") !== "false");
|
|
385
|
+
const configureMcp = firstFlag(parsed.flags, "yes-mcp") === "true" || (confirmed && firstFlag(parsed.flags, "configure-mcp", "true") !== "false");
|
|
386
|
+
const installGitAttributes = firstFlag(parsed.flags, "yes-attr") === "true" || confirmed;
|
|
387
|
+
const installClaudeHooks = firstFlag(parsed.flags, "yes-claude") === "true";
|
|
388
|
+
const portableState = firstFlag(parsed.flags, "portable") === "true" ? true : firstFlag(parsed.flags, "portable") === "false" ? false : undefined;
|
|
368
389
|
const result = bootstrapHolistic(rootDir, {
|
|
369
|
-
installDaemon
|
|
370
|
-
installGitHooks
|
|
371
|
-
|
|
390
|
+
installDaemon,
|
|
391
|
+
installGitHooks,
|
|
392
|
+
installGitAttributes,
|
|
393
|
+
installClaudeHooks,
|
|
394
|
+
configureMcp,
|
|
395
|
+
portableState,
|
|
372
396
|
platform: platform,
|
|
373
397
|
intervalSeconds,
|
|
374
398
|
remote: firstFlag(parsed.flags, "remote", "origin"),
|
|
@@ -471,7 +495,7 @@ Repo-local CLI: ${repairFallback}
|
|
|
471
495
|
return 0;
|
|
472
496
|
}
|
|
473
497
|
async function handleResume(rootDir, parsed) {
|
|
474
|
-
|
|
498
|
+
warnIfHooksOutdated(rootDir);
|
|
475
499
|
const agent = asAgent(firstFlag(parsed.flags, "agent", "unknown"));
|
|
476
500
|
if (firstFlag(parsed.flags, "continue") === "true") {
|
|
477
501
|
const mutateResult = mutateState(rootDir, (state) => continueFromLatest(rootDir, state, agent));
|
|
@@ -508,7 +532,6 @@ async function handleResume(rootDir, parsed) {
|
|
|
508
532
|
return 0;
|
|
509
533
|
}
|
|
510
534
|
async function handleCheckpoint(rootDir, parsed) {
|
|
511
|
-
refreshHooksBeforeCommand(rootDir);
|
|
512
535
|
const mutateResult = mutateState(rootDir, (state, paths) => {
|
|
513
536
|
const regressions = listFlag(parsed.flags, "regression");
|
|
514
537
|
const fixed = firstFlag(parsed.flags, "fixed");
|
|
@@ -559,11 +582,17 @@ async function handleCheckpoint(rootDir, parsed) {
|
|
|
559
582
|
return 0;
|
|
560
583
|
}
|
|
561
584
|
async function handleStartNew(rootDir, parsed) {
|
|
562
|
-
refreshHooksBeforeCommand(rootDir);
|
|
563
585
|
const agent = asAgent(firstFlag(parsed.flags, "agent", "unknown"));
|
|
564
|
-
|
|
586
|
+
let goal = firstFlag(parsed.flags, "goal");
|
|
565
587
|
const title = firstFlag(parsed.flags, "title");
|
|
566
588
|
const plan = listFlag(parsed.flags, "plan");
|
|
589
|
+
if (!goal) {
|
|
590
|
+
goal = await ask("Current objective / goal");
|
|
591
|
+
}
|
|
592
|
+
if (!goal) {
|
|
593
|
+
process.stderr.write("Error: A goal is required to start a new session.\n");
|
|
594
|
+
return 1;
|
|
595
|
+
}
|
|
567
596
|
const mutateResult = mutateState(rootDir, (state) => startNewSession(rootDir, state, agent, goal, plan, title));
|
|
568
597
|
if (!mutateResult.success || !mutateResult.state) {
|
|
569
598
|
process.stderr.write(`Error: Failed to start session: ${mutateResult.error}\n`);
|
|
@@ -573,7 +602,6 @@ async function handleStartNew(rootDir, parsed) {
|
|
|
573
602
|
return 0;
|
|
574
603
|
}
|
|
575
604
|
async function handleHandoff(rootDir, parsed) {
|
|
576
|
-
refreshHooksBeforeCommand(rootDir);
|
|
577
605
|
const { state, paths } = loadState(rootDir);
|
|
578
606
|
if (!state.activeSession) {
|
|
579
607
|
process.stderr.write("No active session to hand off.\n");
|
|
@@ -734,7 +762,7 @@ function renderSyncStatus(rootDir) {
|
|
|
734
762
|
return lines.join("\n") + "\n";
|
|
735
763
|
}
|
|
736
764
|
async function handleStatus(rootDir) {
|
|
737
|
-
|
|
765
|
+
warnIfHooksOutdated(rootDir);
|
|
738
766
|
const { state } = loadState(rootDir);
|
|
739
767
|
process.stdout.write(renderStatus(rootDir, state));
|
|
740
768
|
return 0;
|
|
@@ -806,7 +834,6 @@ async function handleSearch(rootDir, parsed) {
|
|
|
806
834
|
return 0;
|
|
807
835
|
}
|
|
808
836
|
async function handleServe(rootDir) {
|
|
809
|
-
refreshHooksBeforeCommand(rootDir);
|
|
810
837
|
printSplashError({
|
|
811
838
|
message: "starting MCP server on stdio...",
|
|
812
839
|
});
|
|
@@ -838,7 +865,6 @@ async function handleMarkCommit(rootDir, parsed) {
|
|
|
838
865
|
return 0;
|
|
839
866
|
}
|
|
840
867
|
async function handleWatch(rootDir, parsed) {
|
|
841
|
-
refreshHooksBeforeCommand(rootDir);
|
|
842
868
|
const intervalSeconds = Number.parseInt(firstFlag(parsed.flags, "interval", "60"), 10);
|
|
843
869
|
const agent = asAgent(firstFlag(parsed.flags, "agent", "unknown"));
|
|
844
870
|
process.stdout.write(`Watching repo every ${intervalSeconds}s for checkpoint-worthy changes.\n`);
|