MertCapkin-GraphStack 4.5.1__py3-none-any.whl
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.
- graphstack/__init__.py +12 -0
- graphstack/__main__.py +10 -0
- graphstack/assets/docs/CURSOR_PROMPTS.md +215 -0
- graphstack/assets/handoff/BOOTSTRAP.md +73 -0
- graphstack/assets/handoff/BRIEF.md +66 -0
- graphstack/assets/handoff/REVIEW.md +7 -0
- graphstack/assets/handoff/board/README.md +60 -0
- graphstack/assets/orchestrator/ORCHESTRATOR.md +416 -0
- graphstack/assets/orchestrator/TOKEN_OPTIMIZER.md +319 -0
- graphstack/assets/scripts/board.ps1 +37 -0
- graphstack/assets/scripts/board.sh +22 -0
- graphstack/assets/scripts/gate-hook.ps1 +41 -0
- graphstack/assets/scripts/gate-hook.sh +26 -0
- graphstack/assets/scripts/post-commit +20 -0
- graphstack/assets/scripts/post-commit.ps1 +44 -0
- graphstack/board.py +361 -0
- graphstack/bootstrap.py +50 -0
- graphstack/cli.py +99 -0
- graphstack/compact/__init__.py +9 -0
- graphstack/compact/__pycache__/__init__.cpython-311.pyc +0 -0
- graphstack/compact/__pycache__/base.cpython-311.pyc +0 -0
- graphstack/compact/__pycache__/generic.cpython-311.pyc +0 -0
- graphstack/compact/__pycache__/git.cpython-311.pyc +0 -0
- graphstack/compact/__pycache__/registry.cpython-311.pyc +0 -0
- graphstack/compact/base.py +115 -0
- graphstack/compact/generic.py +90 -0
- graphstack/compact/git.py +167 -0
- graphstack/compact/registry.py +47 -0
- graphstack/constants.py +38 -0
- graphstack/gate.py +429 -0
- graphstack/graph.py +143 -0
- graphstack/hook.py +144 -0
- graphstack/init_cmd.py +113 -0
- graphstack/installer.py +366 -0
- graphstack/platform_utils.py +127 -0
- graphstack/run.py +103 -0
- graphstack/state.py +117 -0
- graphstack/tests/__init__.py +0 -0
- graphstack/tests/conftest.py +30 -0
- graphstack/tests/test_assets.py +35 -0
- graphstack/tests/test_board.py +166 -0
- graphstack/tests/test_compact.py +93 -0
- graphstack/tests/test_gate.py +406 -0
- graphstack/tests/test_graph.py +60 -0
- graphstack/tests/test_hook.py +57 -0
- graphstack/tests/test_init.py +58 -0
- graphstack/tests/test_installer.py +73 -0
- graphstack/tests/test_platform_utils.py +69 -0
- graphstack/tests/test_state.py +56 -0
- graphstack/tests/test_validate.py +204 -0
- graphstack/validate.py +469 -0
- mertcapkin_graphstack-4.5.1.dist-info/METADATA +720 -0
- mertcapkin_graphstack-4.5.1.dist-info/RECORD +57 -0
- mertcapkin_graphstack-4.5.1.dist-info/WHEEL +5 -0
- mertcapkin_graphstack-4.5.1.dist-info/entry_points.txt +2 -0
- mertcapkin_graphstack-4.5.1.dist-info/licenses/LICENSE +21 -0
- mertcapkin_graphstack-4.5.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# Token Optimizer
|
|
2
|
+
|
|
3
|
+
GraphStack's token optimization layer. These rules are enforced across ALL roles.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The Core Principle
|
|
8
|
+
|
|
9
|
+
> The graph is a compression of your codebase.
|
|
10
|
+
> Reading the graph = reading the codebase, at 1/10th the cost.
|
|
11
|
+
> Every raw file read that the graph could have answered is waste.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Session Budget Tracker
|
|
16
|
+
|
|
17
|
+
At session start, initialize mentally:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
SESSION BUDGET
|
|
21
|
+
──────────────
|
|
22
|
+
Graph reads: 1 allowed (GRAPH_REPORT.md) — FREE
|
|
23
|
+
Brief reads: 1 allowed (BRIEF.md) — FREE
|
|
24
|
+
Raw file reads: track each one
|
|
25
|
+
Re-reads: 0 allowed
|
|
26
|
+
Speculative: 0 allowed
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Decision Tree (Run Before Every Tool Call)
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
Before reading a file:
|
|
35
|
+
├─ Is it GRAPH_REPORT.md?
|
|
36
|
+
│ ├─ Already read this session? → SKIP (use context)
|
|
37
|
+
│ └─ Not yet read? → READ (Tier 1, free)
|
|
38
|
+
│
|
|
39
|
+
├─ Is it in handoff/BRIEF.md's file list?
|
|
40
|
+
│ └─ YES → READ (Tier 2, justified)
|
|
41
|
+
│
|
|
42
|
+
├─ Can graph.json answer this question?
|
|
43
|
+
│ ├─ YES → QUERY GRAPH (Tier 1, free)
|
|
44
|
+
│ └─ NO → continue...
|
|
45
|
+
│
|
|
46
|
+
├─ Is this file already in my context window?
|
|
47
|
+
│ └─ YES → SKIP (use context — re-read is banned)
|
|
48
|
+
│
|
|
49
|
+
├─ Do I need the WHOLE file or just one function?
|
|
50
|
+
│ ├─ One function → READ TARGETED SECTION ONLY (Tier 2)
|
|
51
|
+
│ └─ Whole file → JUSTIFY FIRST
|
|
52
|
+
│ └─ Can I get away with the graph summary? → try graph first
|
|
53
|
+
│
|
|
54
|
+
└─ Is this a "just to be sure" read?
|
|
55
|
+
└─ YES → CANCEL (speculative reads banned)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Shell Output (Mandatory for Agents)
|
|
61
|
+
|
|
62
|
+
Every **Shell / terminal** tool call that runs git, tests, linters, or package managers MUST use GraphStack compaction — not raw commands.
|
|
63
|
+
|
|
64
|
+
**Default:**
|
|
65
|
+
```bash
|
|
66
|
+
python -m graphstack run -- git status
|
|
67
|
+
python -m graphstack run -- git diff
|
|
68
|
+
python -m graphstack run -- git log -n 20 --oneline
|
|
69
|
+
python -m graphstack run -- pytest -q
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Quality escape hatch** (debug only — wastes tokens):
|
|
73
|
+
```bash
|
|
74
|
+
python -m graphstack run --raw -- git diff
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### What compactors preserve (never sacrifice for size)
|
|
78
|
+
|
|
79
|
+
- File paths, branch names, commit hashes
|
|
80
|
+
- Diff hunk headers (`@@`) and changed lines (`+` / `-`)
|
|
81
|
+
- Test failures, tracebacks, `FAILED` / `ERROR` lines
|
|
82
|
+
- stderr (always appended verbatim)
|
|
83
|
+
|
|
84
|
+
If compaction would drop critical signal, the tool **falls back to raw stdout** automatically.
|
|
85
|
+
|
|
86
|
+
### Tier placement
|
|
87
|
+
|
|
88
|
+
| Action | Tier |
|
|
89
|
+
|--------|------|
|
|
90
|
+
| `graphstack run -- git status` | Tier 2 — cheap, preferred |
|
|
91
|
+
| Raw `git status` in shell | Tier 3 — avoid unless `run` unavailable |
|
|
92
|
+
| `graphstack run --raw` | Tier 3 — justified for deep debug |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Graph Query Patterns
|
|
97
|
+
|
|
98
|
+
**Default:** use the GraphStack wrapper (same graphify engine, consistent argv):
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
python -m graphstack graph query "who calls login"
|
|
102
|
+
python -m graphstack graph query "blast radius of crypto.ts" --budget 1500
|
|
103
|
+
python -m graphstack graph path src/auth/login.ts src/utils/crypto.ts
|
|
104
|
+
python -m graphstack graph explain "login()"
|
|
105
|
+
python -m graphstack graph update . # AST-only refresh after code changes
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Only read `GRAPH_REPORT.md` once per session for broad architecture overview.
|
|
109
|
+
For targeted questions, **always query first** — never grep or read raw files speculatively.
|
|
110
|
+
|
|
111
|
+
### Manual graph.json patterns (fallback only)
|
|
112
|
+
|
|
113
|
+
These answer common questions WITHOUT reading files when `graph query` is unavailable:
|
|
114
|
+
|
|
115
|
+
### "What does this module import?"
|
|
116
|
+
```
|
|
117
|
+
graph.json → node["src/auth/login.ts"].edges.filter(e => e.type === "imports")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### "Who calls this function?"
|
|
121
|
+
```
|
|
122
|
+
graph.json → nodes.filter(n => n.edges.some(e => e.target === "login" && e.type === "calls"))
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### "What modules are in this cluster?"
|
|
126
|
+
```
|
|
127
|
+
GRAPH_REPORT.md → cluster section → find cluster containing target
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### "What's the blast radius of changing X?"
|
|
131
|
+
```
|
|
132
|
+
graph.json → BFS from node X, depth 2, outgoing edges only
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### "Are there tests for this?"
|
|
136
|
+
```
|
|
137
|
+
graph.json → node["src/auth/login.ts"].edges.filter(e => e.type === "tested_by")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### "What pattern does the codebase use for Y?"
|
|
141
|
+
```
|
|
142
|
+
GRAPH_REPORT.md → patterns section
|
|
143
|
+
graph.json → find 3 nodes in same cluster as Y → read ONE as example
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Parallel Read Protocol
|
|
149
|
+
|
|
150
|
+
When 2+ files must be read, NEVER read sequentially.
|
|
151
|
+
|
|
152
|
+
**Wrong (2x cost):**
|
|
153
|
+
```
|
|
154
|
+
read(src/auth/login.ts)
|
|
155
|
+
read(src/auth/session.ts)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Right (1x cost):**
|
|
159
|
+
```
|
|
160
|
+
read([src/auth/login.ts, src/auth/session.ts]) // one tool call
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Rule: If you know you need N files, request all N in one call.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Output Compression Rules
|
|
168
|
+
|
|
169
|
+
### Never produce these:
|
|
170
|
+
- Restatement of user's request ("You want me to add a login feature...")
|
|
171
|
+
- Transition announcements over 2 lines ("Now I will switch to the Builder role and begin implementing...")
|
|
172
|
+
- Ellipsis thinking ("Let me think about this...")
|
|
173
|
+
- Excessive role headers (one short `[ROLE MODE]` tag is enough)
|
|
174
|
+
- Full file contents when a diff is sufficient
|
|
175
|
+
- Full function when a summary is sufficient
|
|
176
|
+
|
|
177
|
+
### Always prefer:
|
|
178
|
+
- Diffs over full files
|
|
179
|
+
- Summaries over full reads
|
|
180
|
+
- One-line status over paragraphs
|
|
181
|
+
- Tables over bullet lists for comparisons
|
|
182
|
+
- "✓" / "✗" over "passed" / "failed"
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Context Window Rules
|
|
187
|
+
|
|
188
|
+
### What stays in context (never re-read):
|
|
189
|
+
- GRAPH_REPORT.md content
|
|
190
|
+
- BRIEF.md content
|
|
191
|
+
- Any file read this session
|
|
192
|
+
|
|
193
|
+
### What gets summarized out (to free context):
|
|
194
|
+
- Intermediate reasoning
|
|
195
|
+
- Rejected alternatives
|
|
196
|
+
- Verbose error messages (summarize to one line)
|
|
197
|
+
|
|
198
|
+
### When context is getting full:
|
|
199
|
+
```
|
|
200
|
+
"Context at ~80% capacity. Summarizing intermediate state to handoff/STATE.md.
|
|
201
|
+
Continuing from current role."
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Estimated Token Savings by Pattern
|
|
207
|
+
|
|
208
|
+
| Old Pattern | New Pattern | Savings |
|
|
209
|
+
|-------------|-------------|---------|
|
|
210
|
+
| Read 10 files to understand architecture | Read GRAPH_REPORT.md | ~85% |
|
|
211
|
+
| Re-read file already in context | Use existing context | 100% |
|
|
212
|
+
| Sequential file reads | Parallel reads | ~50% |
|
|
213
|
+
| Full file read for one function | Targeted section read | ~70% |
|
|
214
|
+
| Speculative read "just in case" | Query graph first | ~90% |
|
|
215
|
+
| Chat history for state | STATE.md file | ~60% per new session |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Graph Update Strategy
|
|
220
|
+
|
|
221
|
+
### When to Update (Smart Triggers)
|
|
222
|
+
|
|
223
|
+
The post-commit hook enforces these rules automatically. Manually apply the same logic:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
TRIGGER 1 — Structural change (highest priority)
|
|
227
|
+
Any file added or deleted (outside graphify-out/ and handoff/)
|
|
228
|
+
→ Always update, immediately
|
|
229
|
+
→ Reason: new nodes or missing nodes break graph queries
|
|
230
|
+
|
|
231
|
+
TRIGGER 2 — Ship completed
|
|
232
|
+
A cycle or feature just shipped
|
|
233
|
+
→ Always update after Ship role completes
|
|
234
|
+
→ Reason: the next Architect/Bootstrapper reads this graph
|
|
235
|
+
|
|
236
|
+
TRIGGER 3 — Staleness
|
|
237
|
+
graph.json is >24 hours old
|
|
238
|
+
→ Update at start of next session
|
|
239
|
+
→ Reason: accumulated small changes may have shifted architecture
|
|
240
|
+
|
|
241
|
+
NOT A TRIGGER — Content edits only
|
|
242
|
+
Modifying existing functions, fixing bugs, updating tests
|
|
243
|
+
→ Do NOT update graph unless 24h threshold reached
|
|
244
|
+
→ Reason: graph topology unchanged; existing queries still valid
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Why Not Update Every Commit?
|
|
248
|
+
|
|
249
|
+
Graphify runs locally (tree-sitter, no API calls) so it has no token cost. But:
|
|
250
|
+
|
|
251
|
+
1. **Time cost** — update takes 5-30 seconds on large repos
|
|
252
|
+
2. **Noise cost** — frequent graph commits clutter git history
|
|
253
|
+
3. **Diminishing returns** — a bug fix in an existing function doesn't change who imports what
|
|
254
|
+
|
|
255
|
+
**The graph represents structure, not content.** Content changes don't require a new graph.
|
|
256
|
+
|
|
257
|
+
### Manual Update Commands
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Force update now (run in Cursor or terminal)
|
|
261
|
+
/graphify --update
|
|
262
|
+
|
|
263
|
+
# Check graph age
|
|
264
|
+
ls -la graphify-out/GRAPH_REPORT.md
|
|
265
|
+
|
|
266
|
+
# See what changed since last graph update
|
|
267
|
+
git diff $(git log --all --oneline -- graphify-out/graph.json | head -1 | cut -d' ' -f1) -- src/
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Bootstrap Mode Graph Schedule
|
|
271
|
+
|
|
272
|
+
During Bootstrap cycles, the Orchestrator enforces this manually:
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
Cycle 1 → Ship → STOP → "Run /graphify . now" → wait for confirmation → Cycle 2 brief
|
|
276
|
+
Cycle N → Ship → STOP → "Run /graphify --update now" → wait → Cycle N+1 brief
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Never skip this step in bootstrap mode. The next brief depends on the real graph.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Project Type Suitability
|
|
284
|
+
|
|
285
|
+
GraphStack token savings scale with **codebase complexity and query frequency**.
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
High value (>60% savings):
|
|
289
|
+
✅ REST/GraphQL APIs ✅ Data pipelines
|
|
290
|
+
✅ Monolithic web apps ✅ Libraries/SDKs
|
|
291
|
+
✅ Game backends ✅ CLI tools (medium+)
|
|
292
|
+
✅ Microservices ✅ Admin panels
|
|
293
|
+
|
|
294
|
+
Medium value (30-60% savings):
|
|
295
|
+
🟡 React/Vue SPAs 🟡 Mobile apps (React Native)
|
|
296
|
+
🟡 Unity games (C#) 🟡 TypeScript monorepos
|
|
297
|
+
🟡 Serverless functions 🟡 E-commerce backends
|
|
298
|
+
|
|
299
|
+
Low value (<30% savings):
|
|
300
|
+
🔴 Static sites 🔴 Single-file scripts
|
|
301
|
+
🔴 Jupyter notebooks 🔴 Rapid prototypes (<20 files)
|
|
302
|
+
🔴 Godot (GDScript) 🔴 Flutter (widget-heavy)
|
|
303
|
+
|
|
304
|
+
The threshold: GraphStack pays off when your codebase exceeds ~20 files
|
|
305
|
+
and queries regularly cross module boundaries.
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
❌ Reading GRAPH_REPORT.md more than once per session
|
|
310
|
+
❌ Reading a file to "explore" without a specific question
|
|
311
|
+
❌ Asking the user a question you could answer from the graph
|
|
312
|
+
❌ Reading all files in a directory to find the right one
|
|
313
|
+
(use graph.json node lookup instead)
|
|
314
|
+
❌ Producing a plan before reading the graph
|
|
315
|
+
❌ Re-reading a file because you forgot its contents
|
|
316
|
+
(summarize key facts to STATE.md before losing context)
|
|
317
|
+
❌ Writing code before checking graph dependencies
|
|
318
|
+
❌ Reviewing code without checking graph neighbors
|
|
319
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# GraphStack GNAP board — thin PowerShell shim that delegates to the Python core.
|
|
2
|
+
# Real logic lives in scripts/graphstack/board.py.
|
|
3
|
+
#
|
|
4
|
+
# Usage: .\scripts\board.ps1 <command> [args]
|
|
5
|
+
|
|
6
|
+
$ErrorActionPreference = 'Stop'
|
|
7
|
+
|
|
8
|
+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
9
|
+
$env:PYTHONPATH = if ($env:PYTHONPATH) {
|
|
10
|
+
"$scriptDir;$env:PYTHONPATH"
|
|
11
|
+
} else {
|
|
12
|
+
$scriptDir
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Resolve-Python {
|
|
16
|
+
# On Windows, prefer `py -3` because `python.exe` is often the Microsoft
|
|
17
|
+
# Store redirect stub which prints a localized error and exits 9009.
|
|
18
|
+
$py = Get-Command 'py' -ErrorAction SilentlyContinue
|
|
19
|
+
if ($py) { return [pscustomobject]@{ Exe = $py.Source; PreArgs = @('-3') } }
|
|
20
|
+
foreach ($name in @('python3', 'python')) {
|
|
21
|
+
$cmd = Get-Command $name -ErrorAction SilentlyContinue
|
|
22
|
+
if (-not $cmd) { continue }
|
|
23
|
+
# Skip the Windows Store stub (lives under WindowsApps).
|
|
24
|
+
if ($cmd.Source -match 'WindowsApps') { continue }
|
|
25
|
+
return [pscustomobject]@{ Exe = $cmd.Source; PreArgs = @() }
|
|
26
|
+
}
|
|
27
|
+
return $null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
$python = Resolve-Python
|
|
31
|
+
if (-not $python) {
|
|
32
|
+
Write-Error 'GraphStack: Python not found. Install Python 3.8+ and retry.'
|
|
33
|
+
exit 127
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
& $python.Exe @($python.PreArgs) -m graphstack board @args
|
|
37
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# GraphStack GNAP board — thin shim that delegates to the Python core.
|
|
3
|
+
# Real logic lives in scripts/graphstack/board.py.
|
|
4
|
+
#
|
|
5
|
+
# Usage: bash scripts/board.sh <command> [args]
|
|
6
|
+
# commands: status | new | claim | complete | log | help
|
|
7
|
+
|
|
8
|
+
set -uo pipefail
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
export PYTHONPATH="${SCRIPT_DIR}${PYTHONPATH:+:$PYTHONPATH}"
|
|
12
|
+
|
|
13
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
14
|
+
PY=python3
|
|
15
|
+
elif command -v python >/dev/null 2>&1; then
|
|
16
|
+
PY=python
|
|
17
|
+
else
|
|
18
|
+
echo "❌ GraphStack: Python not found. Install Python 3.8+ and retry." >&2
|
|
19
|
+
exit 127
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
exec "$PY" -m graphstack board "$@"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# GraphStack process gate — thin PowerShell shim for Cursor / Claude Code hooks.
|
|
2
|
+
# Real logic lives in scripts/graphstack/gate.py.
|
|
3
|
+
#
|
|
4
|
+
# Usage: .\scripts\gate-hook.ps1 <cursor|claude>
|
|
5
|
+
|
|
6
|
+
$ErrorActionPreference = 'Stop'
|
|
7
|
+
|
|
8
|
+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
9
|
+
$env:PYTHONPATH = if ($env:PYTHONPATH) {
|
|
10
|
+
"$scriptDir;$env:PYTHONPATH"
|
|
11
|
+
} else {
|
|
12
|
+
$scriptDir
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Resolve-Python {
|
|
16
|
+
$py = Get-Command 'py' -ErrorAction SilentlyContinue
|
|
17
|
+
if ($py) { return [pscustomobject]@{ Exe = $py.Source; PreArgs = @('-3') } }
|
|
18
|
+
foreach ($name in @('python3', 'python')) {
|
|
19
|
+
$cmd = Get-Command $name -ErrorAction SilentlyContinue
|
|
20
|
+
if (-not $cmd) { continue }
|
|
21
|
+
if ($cmd.Source -match 'WindowsApps') { continue }
|
|
22
|
+
return [pscustomobject]@{ Exe = $cmd.Source; PreArgs = @() }
|
|
23
|
+
}
|
|
24
|
+
return $null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
$platform = if ($args.Count -gt 0) { $args[0] } else { 'cursor' }
|
|
28
|
+
|
|
29
|
+
$python = Resolve-Python
|
|
30
|
+
if (-not $python) {
|
|
31
|
+
Write-Host 'graphstack gate-hook: Python not found — failing open' -ForegroundColor Yellow
|
|
32
|
+
if ($platform -eq 'cursor') {
|
|
33
|
+
Write-Output '{"continue": true, "permission": "allow"}'
|
|
34
|
+
} else {
|
|
35
|
+
Write-Output '{}'
|
|
36
|
+
}
|
|
37
|
+
exit 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
& $python.Exe @($python.PreArgs) -m graphstack gate hook @args
|
|
41
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# GraphStack process gate — thin shim for Cursor / Claude Code hooks.
|
|
3
|
+
# Real logic lives in scripts/graphstack/gate.py.
|
|
4
|
+
#
|
|
5
|
+
# Usage: bash scripts/gate-hook.sh <cursor|claude>
|
|
6
|
+
|
|
7
|
+
set -uo pipefail
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
export PYTHONPATH="${SCRIPT_DIR}${PYTHONPATH:+:$PYTHONPATH}"
|
|
11
|
+
|
|
12
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
13
|
+
exec python3 -m graphstack gate hook "$@"
|
|
14
|
+
elif command -v python >/dev/null 2>&1; then
|
|
15
|
+
exec python -m graphstack gate hook "$@"
|
|
16
|
+
elif command -v py >/dev/null 2>&1; then
|
|
17
|
+
exec py -3 -m graphstack gate hook "$@"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
echo "graphstack gate-hook: Python not found — failing open" >&2
|
|
21
|
+
if [ "${1:-}" = "cursor" ]; then
|
|
22
|
+
echo '{"continue": true, "permission": "allow"}'
|
|
23
|
+
else
|
|
24
|
+
echo '{}'
|
|
25
|
+
fi
|
|
26
|
+
exit 0
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# GraphStack — Smart Graph Update Hook (thin shim).
|
|
3
|
+
# Real logic lives in scripts/graphstack/hook.py.
|
|
4
|
+
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
|
|
7
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
8
|
+
export PYTHONPATH="${REPO_ROOT}/scripts${PYTHONPATH:+:$PYTHONPATH}"
|
|
9
|
+
|
|
10
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
11
|
+
PY=python3
|
|
12
|
+
elif command -v python >/dev/null 2>&1; then
|
|
13
|
+
PY=python
|
|
14
|
+
else
|
|
15
|
+
echo "⚠️ GraphStack: Python not found — skipping graph update."
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
cd "$REPO_ROOT" || exit 0
|
|
20
|
+
exec "$PY" -m graphstack hook
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# GraphStack — Smart Graph Update Hook (thin PowerShell shim).
|
|
2
|
+
# Real logic lives in scripts/graphstack/hook.py.
|
|
3
|
+
|
|
4
|
+
$ErrorActionPreference = 'Stop'
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
$repoRoot = (& git rev-parse --show-toplevel 2>$null).Trim()
|
|
8
|
+
if (-not $repoRoot) { $repoRoot = (Get-Location).Path }
|
|
9
|
+
} catch {
|
|
10
|
+
$repoRoot = (Get-Location).Path
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
$packageParent = Join-Path $repoRoot 'scripts'
|
|
14
|
+
$env:PYTHONPATH = if ($env:PYTHONPATH) {
|
|
15
|
+
"$packageParent;$env:PYTHONPATH"
|
|
16
|
+
} else {
|
|
17
|
+
$packageParent
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function Resolve-Python {
|
|
21
|
+
$py = Get-Command 'py' -ErrorAction SilentlyContinue
|
|
22
|
+
if ($py) { return [pscustomobject]@{ Exe = $py.Source; PreArgs = @('-3') } }
|
|
23
|
+
foreach ($name in @('python3', 'python')) {
|
|
24
|
+
$cmd = Get-Command $name -ErrorAction SilentlyContinue
|
|
25
|
+
if (-not $cmd) { continue }
|
|
26
|
+
if ($cmd.Source -match 'WindowsApps') { continue }
|
|
27
|
+
return [pscustomobject]@{ Exe = $cmd.Source; PreArgs = @() }
|
|
28
|
+
}
|
|
29
|
+
return $null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
$python = Resolve-Python
|
|
33
|
+
if (-not $python) {
|
|
34
|
+
Write-Host 'GraphStack: Python not found - skipping graph update.'
|
|
35
|
+
exit 0
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Push-Location $repoRoot
|
|
39
|
+
try {
|
|
40
|
+
& $python.Exe @($python.PreArgs) -m graphstack hook
|
|
41
|
+
exit $LASTEXITCODE
|
|
42
|
+
} finally {
|
|
43
|
+
Pop-Location
|
|
44
|
+
}
|