pipulate 2.64__tar.gz → 2.65__tar.gz
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.
- {pipulate-2.64/pipulate.egg-info → pipulate-2.65}/PKG-INFO +1 -1
- {pipulate-2.64 → pipulate-2.65}/__init__.py +2 -2
- {pipulate-2.64 → pipulate-2.65}/apps/030_roles.py +3 -3
- {pipulate-2.64 → pipulate-2.65}/imports/ascii_displays.py +97 -17
- {pipulate-2.64 → pipulate-2.65}/pipulate/__init__.py +3 -3
- {pipulate-2.64 → pipulate-2.65}/pipulate/core.py +5 -5
- {pipulate-2.64 → pipulate-2.65/pipulate.egg-info}/PKG-INFO +1 -1
- {pipulate-2.64 → pipulate-2.65}/pyproject.toml +1 -1
- {pipulate-2.64 → pipulate-2.65}/server.py +2 -2
- {pipulate-2.64 → pipulate-2.65}/LICENSE +0 -0
- {pipulate-2.64 → pipulate-2.65}/MANIFEST.in +0 -0
- {pipulate-2.64 → pipulate-2.65}/README.md +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/010_introduction.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/015_config.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/020_profiles.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/025_aspect.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/040_hello_workflow.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/050_documentation.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/060_tasks.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/070_history.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/110_parameter_buster.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/120_link_graph.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/130_gap_analysis.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/200_workflow_genesis.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/210_widget_examples.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/220_roadmap.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/230_dev_assistant.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/240_simon_mcp.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/300_blank_placeholder.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/400_botify_trifecta.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/440_browser_automation.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/450_stream_simulator.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/510_text_field.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/520_text_area.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/530_dropdown.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/540_checkboxes.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/550_radios.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/560_range.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/570_switch.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/580_upload.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/610_markdown.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/620_mermaid.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/630_prism.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/640_javascript.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/710_pandas.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/720_rich.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/730_matplotlib.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/810_webbrowser.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/820_selenium.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/apps/830_pico_slider.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/cli.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/config.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/__init__.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/ai_dictdb.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/ai_tool_discovery_simple_parser.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/append_only_conversation.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/botify/__init__.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/botify/code_generators.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/botify/true_schema_discoverer.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/botify_code_generation.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/crud.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/database_safety_wrapper.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/dom_processing/__init__.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/dom_processing/ai_dom_beautifier.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/dom_processing/enhanced_dom_processor.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/durable_backup_system.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/mcp_orchestrator.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/server_logging.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/stream_orchestrator.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/imports/voice_synthesis.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/pipulate.egg-info/SOURCES.txt +0 -0
- {pipulate-2.64 → pipulate-2.65}/pipulate.egg-info/dependency_links.txt +0 -0
- {pipulate-2.64 → pipulate-2.65}/pipulate.egg-info/entry_points.txt +0 -0
- {pipulate-2.64 → pipulate-2.65}/pipulate.egg-info/requires.txt +0 -0
- {pipulate-2.64 → pipulate-2.65}/pipulate.egg-info/top_level.txt +0 -0
- {pipulate-2.64 → pipulate-2.65}/setup.cfg +0 -0
- {pipulate-2.64 → pipulate-2.65}/tests/test_apply_airlock.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tests/test_mck_rep2.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/__init__.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/advanced_automation_tools.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/botify_tools.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/connector_tools.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/conversation_tools.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/dom_tools.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/keychain_tools.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/llm_optics.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/mcp_tools.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/scraper_tools.py +0 -0
- {pipulate-2.64 → pipulate-2.65}/tools/system_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipulate
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.65
|
|
4
4
|
Summary: The context compiler that helps you prompt well — local-first, Nix-reproducible, replayable. The successor to AI SEO software.
|
|
5
5
|
Author-email: Mike Levin <pipulate@gmail.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -12,7 +12,7 @@ Usage:
|
|
|
12
12
|
pipulate
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
__version__ = "2.
|
|
15
|
+
__version__ = "2.65"
|
|
16
16
|
# APOSTROPHES RESTORED (2026-08-04). They were stripped as a workaround for
|
|
17
17
|
# flake.nix's descMatch regex, whose character class excluded ' from the
|
|
18
18
|
# CAPTURE and truncated the banner to "(So)". That regex was fixed in the same
|
|
@@ -23,7 +23,7 @@ __version__ = "2.64"
|
|
|
23
23
|
# is not a property of the system; the regex is. Blast radius is one banner:
|
|
24
24
|
# nothing but flake.nix reads this name -- version_sync.py syncs __version__
|
|
25
25
|
# and __description__, never this. So'wI' chu' -- "engage the cloaking device."
|
|
26
|
-
__version_description__ = "
|
|
26
|
+
__version_description__ = "About on the Menu"
|
|
27
27
|
# SPDX expression, single source of truth, synced into pyproject.toml by
|
|
28
28
|
# scripts/release/version_sync.py. "-or-later" (not bare AGPL-3.0, which is
|
|
29
29
|
# deprecated SPDX) because the header below grants "any later version".
|
|
@@ -44,15 +44,15 @@ class PluginIdentityManager:
|
|
|
44
44
|
|
|
45
45
|
@property
|
|
46
46
|
def TRAINING_PROMPT(self):
|
|
47
|
-
"""Load the roles skill from
|
|
48
|
-
skill_path = os.path.join(os.path.dirname(__file__), '..', '
|
|
47
|
+
"""Load the roles skill from .agents/skills/roles/SKILL.md at the repo root (graceful fallback)."""
|
|
48
|
+
skill_path = os.path.join(os.path.dirname(__file__), '..', '.agents', 'skills', 'roles', 'SKILL.md')
|
|
49
49
|
try:
|
|
50
50
|
with open(skill_path, 'r', encoding='utf-8') as f:
|
|
51
51
|
return f.read()
|
|
52
52
|
except Exception:
|
|
53
53
|
return (
|
|
54
54
|
'# Pipulate Roles System\n\n'
|
|
55
|
-
'SKILL.md not found at
|
|
55
|
+
'SKILL.md not found at .agents/skills/roles/SKILL.md. '
|
|
56
56
|
'The Roles app is the homepage and APP-menu control center: checkboxes toggle roles, '
|
|
57
57
|
'drag reorders the APP menu, Core is always on. Secret word: ORCHESTRATOR.'
|
|
58
58
|
)
|
|
@@ -124,6 +124,88 @@ def figurate(name: str, context: Optional[str] = None) -> FigurateResult:
|
|
|
124
124
|
return FigurateResult(name=name, human=human_out, ai=ai_out, drift=drift)
|
|
125
125
|
|
|
126
126
|
|
|
127
|
+
def seal_readings():
|
|
128
|
+
"""One (name, sealed, computed) per registered art: the FIGURATE_LEDGER
|
|
129
|
+
value (None when the art has no seal) beside the CRC32 of the ai string
|
|
130
|
+
rendered right now, computed exactly as figurate() computes drift."""
|
|
131
|
+
rows = []
|
|
132
|
+
for name, entry in FIGURATE_REGISTRY.items():
|
|
133
|
+
render_fn = entry.get("render")
|
|
134
|
+
if render_fn is None:
|
|
135
|
+
continue
|
|
136
|
+
_, ai_out = render_fn()
|
|
137
|
+
rows.append((name, FIGURATE_LEDGER.get(name), binascii.crc32(ai_out.encode('utf-8'))))
|
|
138
|
+
return rows
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def reseal_ledger(write=False):
|
|
142
|
+
"""THE RESEAL (2026-09-28, the operator's ruling: pragmatism over paranoia
|
|
143
|
+
while the art is redrawn). Rewrite the drifted FIGURATE_LEDGER lines of THIS
|
|
144
|
+
file in place, each line's trailing comment kept, and give an unsealed art a
|
|
145
|
+
line above the EXTRUDE_BOTTOM marker. write=False prints the same readings
|
|
146
|
+
and touches nothing. Returns (changed, missing). The seal still detects:
|
|
147
|
+
figurate() and --check read drift exactly as before, and only a hand typing
|
|
148
|
+
--reseal moves a number. THE THREE-REGION ART EDIT keeps its three regions;
|
|
149
|
+
its ordering becomes author, --reseal, figurate reads Drift: 0.
|
|
150
|
+
"""
|
|
151
|
+
import re
|
|
152
|
+
from pathlib import Path
|
|
153
|
+
path = Path(__file__).resolve()
|
|
154
|
+
text = path.read_text(encoding='utf-8')
|
|
155
|
+
head = "FIGURATE_LEDGER: dict = {"
|
|
156
|
+
start = text.index(head) + len(head)
|
|
157
|
+
stop = text.index("# === FIGURATE_LEDGER_EXTRUDE_BOTTOM ===", start)
|
|
158
|
+
line_re = re.compile(r'^(?P<head>\s*"(?P<name>[^"]+)":\s*)(?P<crc>\d+)(?P<tail>,.*)$')
|
|
159
|
+
computed = {name: crc for name, _, crc in seal_readings()}
|
|
160
|
+
lines = text[start:stop].split('\n')
|
|
161
|
+
changed, seen = [], set()
|
|
162
|
+
for i, line in enumerate(lines):
|
|
163
|
+
m = line_re.match(line)
|
|
164
|
+
if not m or m.group('name') not in computed:
|
|
165
|
+
continue
|
|
166
|
+
name = m.group('name')
|
|
167
|
+
seen.add(name)
|
|
168
|
+
old, new = int(m.group('crc')), computed[name]
|
|
169
|
+
if old != new:
|
|
170
|
+
changed.append((name, old, new))
|
|
171
|
+
lines[i] = f"{m.group('head')}{new}{m.group('tail')}"
|
|
172
|
+
missing = [name for name in computed if name not in seen]
|
|
173
|
+
for name in missing:
|
|
174
|
+
lines.insert(len(lines) - 1, f' "{name}": {computed[name]}, # sealed by --reseal')
|
|
175
|
+
verb = "RESEALED" if write else "DRIFT"
|
|
176
|
+
for name, old, new in changed:
|
|
177
|
+
print(f"{name:22s} {old:>11d} -> {new:<11d} {verb}")
|
|
178
|
+
for name in missing:
|
|
179
|
+
print(f"{name:22s} {'(none)':>11s} -> {computed[name]:<11d} {'SEALED' if write else 'UNSEALED'}")
|
|
180
|
+
if write and (changed or missing):
|
|
181
|
+
path.write_text(text[:start] + '\n'.join(lines) + text[stop:], encoding='utf-8')
|
|
182
|
+
return changed, missing
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _main(argv=None):
|
|
186
|
+
"""python imports/ascii_displays.py --check | --reseal
|
|
187
|
+
--check prints every art's seal beside its live CRC32 and exits 1 on any
|
|
188
|
+
drift or unsealed art; --reseal prints the same table, then rewrites the
|
|
189
|
+
ledger and exits 0. Plain print on purpose: the readings land in a `!`
|
|
190
|
+
receipt and on Honeybot, where this file is rsynced alone."""
|
|
191
|
+
import sys
|
|
192
|
+
argv = sys.argv[1:] if argv is None else argv
|
|
193
|
+
if argv not in (["--check"], ["--reseal"]):
|
|
194
|
+
print("usage: python imports/ascii_displays.py --check | --reseal", file=sys.stderr)
|
|
195
|
+
return 2
|
|
196
|
+
rows = seal_readings()
|
|
197
|
+
bad = [name for name, sealed, computed in rows if sealed != computed]
|
|
198
|
+
for name, sealed, computed in rows:
|
|
199
|
+
state = "ok" if sealed == computed else ("UNSEALED" if sealed is None else "DRIFT")
|
|
200
|
+
print(f"{name:22s} sealed={str(sealed if sealed is not None else '-'):<11} computed={computed:<11d} {state}")
|
|
201
|
+
print(f"{len(rows)} art(s), {len(bad)} to reseal")
|
|
202
|
+
if argv == ["--reseal"] and bad:
|
|
203
|
+
changed, missing = reseal_ledger(write=True)
|
|
204
|
+
print(f"resealed {len(changed)} line(s), sealed {len(missing)} new line(s); figurate reads Drift: 0 from here")
|
|
205
|
+
return 0
|
|
206
|
+
return 1 if bad else 0
|
|
207
|
+
|
|
208
|
+
|
|
127
209
|
def _x11_screen_geometry(env):
|
|
128
210
|
"""Return (width, height) in pixels for the active X display, or None.
|
|
129
211
|
|
|
@@ -791,7 +873,7 @@ def _figurate_mechanical_man():
|
|
|
791
873
|
|
|
792
874
|
|
|
793
875
|
def _figurate_workspace_tree():
|
|
794
|
-
"""Render the Workshop as it
|
|
876
|
+
"""Render the Workshop as it sits on disk: three tiers and nothing else.
|
|
795
877
|
THE NESTED VERSION WAS THE ONLY AUTHORITY THAT SHIPPED, AND IT WAS WRONG.
|
|
796
878
|
update_agents_md_in_place() splices this art into AGENTS.md on every
|
|
797
879
|
compile, so it is what every external agent tool reads -- while flake.nix
|
|
@@ -799,9 +881,11 @@ def _figurate_workspace_tree():
|
|
|
799
881
|
and no Corporate/, Personal/, or Workshop/ at all. Three authorities, one
|
|
800
882
|
winner, and the winner disagreed with the disk. Fixed at the GENERATOR so
|
|
801
883
|
the output cannot drift back.
|
|
802
|
-
Three tiers
|
|
803
|
-
|
|
804
|
-
|
|
884
|
+
Three tiers and nothing else (2026-09-28): corporate/ is the org's,
|
|
885
|
+
personal/ is the person's (the starter notebooks land in
|
|
886
|
+
personal/Notebooks/), shared/ is the single deliberate outbound surface
|
|
887
|
+
with one folder per person; flake.nix's THE THREE TIERS block rules and
|
|
888
|
+
this drawing follows it. Authored with no .strip() so
|
|
805
889
|
the leading newline is preserved (the honeybot_pipeline convention), which
|
|
806
890
|
the FIGURATE_LEDGER seal must match. No color-bit or angle-tag markers, so
|
|
807
891
|
_expand_color_bits_ai leaves it untouched and ai_art == art.
|
|
@@ -810,23 +894,15 @@ def _figurate_workspace_tree():
|
|
|
810
894
|
Workshop/ — the JupyterLab root (NOT Pipulate's own root)
|
|
811
895
|
│ FLAT siblings. Nothing nests. Nothing to get wrong.
|
|
812
896
|
│
|
|
813
|
-
├── Advanced_Notebooks/ canon · flake-delivered, copy-if-absent
|
|
814
|
-
├── Educational_Notebooks/ canon · your edits survive, updates do not arrive
|
|
815
|
-
├── imports/ canon · the code-behind "sauce" modules
|
|
816
|
-
│
|
|
817
|
-
├── personal/ personal · gitignored · your own git repo goes here
|
|
818
|
-
├── Client_Work/ personal · gitignored · never leaves this machine
|
|
819
|
-
├── Deliverables/ personal · gitignored
|
|
820
|
-
│
|
|
821
897
|
├── corporate/ the org's canon · gitignored · its own private repo
|
|
822
|
-
|
|
823
|
-
└──
|
|
898
|
+
├── personal/ personal · gitignored · your own git repo goes here
|
|
899
|
+
└── shared/ the ONE folder for handing work to a teammate
|
|
824
900
|
├── alice/ one folder per person; you write ONLY your own
|
|
825
901
|
└── bob/ single-writer partitions = zero merge conflicts
|
|
826
902
|
"""
|
|
827
903
|
ai_art = _expand_color_bits_ai(art)
|
|
828
904
|
human_art = _expand_color_bits_human(art)
|
|
829
|
-
human = Panel(human_art, title="🗂️ Workshop —
|
|
905
|
+
human = Panel(human_art, title="🗂️ Workshop — corporate · personal · shared", border_style="white")
|
|
830
906
|
return human, ai_art
|
|
831
907
|
|
|
832
908
|
|
|
@@ -1539,6 +1615,10 @@ Rich console blocked on Mac (Error: {e}), using fallback display.
|
|
|
1539
1615
|
return fallback_summary.strip()
|
|
1540
1616
|
|
|
1541
1617
|
|
|
1618
|
+
if __name__ == "__main__":
|
|
1619
|
+
raise SystemExit(_main())
|
|
1620
|
+
|
|
1621
|
+
|
|
1542
1622
|
# _ ____ ____ ___ ___ _ ____ _____ ____ _ _
|
|
1543
1623
|
# / \ / ___| / ___|_ _|_ _| / \ | _ \_ _| | _ \| | __ _ _ _ __ _ _ __ ___ _ _ _ __ __| |
|
|
1544
1624
|
# / _ \ \___ \| | | | | | / _ \ | |_) || | | |_) | |/ _` | | | |/ _` | '__/ _ \| | | | '_ \ / _` |
|
|
@@ -1862,7 +1942,7 @@ Rich console blocked on Mac (Error: {e}), using fallback display.
|
|
|
1862
1942
|
# └── assets/ # optional templates and files
|
|
1863
1943
|
# ```
|
|
1864
1944
|
#
|
|
1865
|
-
# You already have this.
|
|
1945
|
+
# You already have this. `.agents/skills/hello_workflow/SKILL.md`, `gsc_readonly`, `roles` — it's in your manifest.
|
|
1866
1946
|
#
|
|
1867
1947
|
# **3. OKF** — what the v0.1 specification actually fixes is a folder layout, markdown files, YAML frontmatter, reserved filenames, and a single required field: `type`. A bundle is a directory of markdown files, each carrying a short YAML block — type, title, description — linking to its neighbors; add an index.md that lists the files so an agent can see what's there before opening everything, and that's the format. The spec fits on a single page.
|
|
1868
1948
|
#
|
|
@@ -1888,7 +1968,7 @@ Rich console blocked on Mac (Error: {e}), using fallback display.
|
|
|
1888
1968
|
# ├── cli.py # = the `allowed-tools` surface: mcp-discover / call
|
|
1889
1969
|
# ├── apply.py # = "PR instructions" made executable (SEARCH/REPLACE actuator)
|
|
1890
1970
|
# ├── flake.nix # = "Dev environment setup" made executable (nix develop)
|
|
1891
|
-
# ├──
|
|
1971
|
+
# ├── .agents/skills/ # = Agent Skills, already standard-shaped
|
|
1892
1972
|
# │ ├── hello_workflow/SKILL.md
|
|
1893
1973
|
# │ ├── gsc_readonly/SKILL.md
|
|
1894
1974
|
# │ └── roles/SKILL.md
|
|
@@ -28,13 +28,13 @@ project_root = find_project_root(os.getcwd()) or Path.cwd()
|
|
|
28
28
|
|
|
29
29
|
# ... after project_root is defined ...
|
|
30
30
|
if project_root:
|
|
31
|
-
notebooks_path = str(project_root / "Workshop")
|
|
31
|
+
notebooks_path = str(project_root / "Workshop" / "personal" / "Notebooks")
|
|
32
32
|
if notebooks_path not in sys.path:
|
|
33
33
|
sys.path.append(notebooks_path)
|
|
34
34
|
|
|
35
35
|
# 🪄 THE FUSION DANCE: Merge core imports with user imports
|
|
36
36
|
import imports
|
|
37
|
-
notebook_imports_path = project_root / "Workshop" / "imports"
|
|
37
|
+
notebook_imports_path = project_root / "Workshop" / "personal" / "Notebooks" / "imports"
|
|
38
38
|
if notebook_imports_path.exists() and str(notebook_imports_path) not in imports.__path__:
|
|
39
39
|
imports.__path__.append(str(notebook_imports_path))
|
|
40
40
|
|
|
@@ -44,7 +44,7 @@ logger.remove()
|
|
|
44
44
|
logger.add(sys.stderr, level="WARNING", colorize=True, format="<level>{level: <8}</level> | <cyan>{name}:{function}:{line}</cyan> - <level>{message}</level>")
|
|
45
45
|
|
|
46
46
|
# 2. Instantiate the wand so we can use its Topological Manifold for paths
|
|
47
|
-
DB_PATH = project_root / "Workshop" / "data" / "pipeline.sqlite"
|
|
47
|
+
DB_PATH = project_root / "Workshop" / "personal" / "Notebooks" / "data" / "pipeline.sqlite"
|
|
48
48
|
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
49
49
|
wand = Pipulate(db_path=str(DB_PATH))
|
|
50
50
|
|
|
@@ -225,7 +225,7 @@ class Pipulate:
|
|
|
225
225
|
class WorkspaceManifold:
|
|
226
226
|
def __init__(self, root: Path, is_notebook: bool):
|
|
227
227
|
self.root = root
|
|
228
|
-
self.base = root / "Workshop" if is_notebook else root
|
|
228
|
+
self.base = root / "Workshop" / "personal" / "Notebooks" if is_notebook else root
|
|
229
229
|
self.data = self.base / "data"
|
|
230
230
|
self.logs = self.data / "logs"
|
|
231
231
|
self.temp = self.data / "temp"
|
|
@@ -2536,7 +2536,7 @@ class Pipulate:
|
|
|
2536
2536
|
|
|
2537
2537
|
# --- Notebook Sync Logic ---
|
|
2538
2538
|
print(f"🔄 Syncing notebook '{notebook_filename}'...")
|
|
2539
|
-
notebook_source_path = project_root / "Workshop" / notebook_filename
|
|
2539
|
+
notebook_source_path = project_root / "Workshop" / "personal" / "Notebooks" / notebook_filename
|
|
2540
2540
|
notebook_dest_path = project_root / "assets" / "nbs" / notebook_filename
|
|
2541
2541
|
|
|
2542
2542
|
if not notebook_source_path.exists():
|
|
@@ -2622,7 +2622,7 @@ class Pipulate:
|
|
|
2622
2622
|
if isinstance(modules, str): modules = (modules,)
|
|
2623
2623
|
for module_name in modules:
|
|
2624
2624
|
module_filename = f"{module_name}.py"
|
|
2625
|
-
module_source_path = project_root / "Workshop" / "imports" / module_filename # Look inside imports/
|
|
2625
|
+
module_source_path = project_root / "Workshop" / "personal" / "Notebooks" / "imports" / module_filename # Look inside imports/
|
|
2626
2626
|
module_dest_path = project_root / "assets" / "nbs" / "imports" / module_filename # Sync back to imports/
|
|
2627
2627
|
if module_source_path.exists():
|
|
2628
2628
|
try:
|
|
@@ -3224,8 +3224,8 @@ class Pipulate:
|
|
|
3224
3224
|
import json
|
|
3225
3225
|
|
|
3226
3226
|
# 1. Explicitly define the path to the Jupyter chrysalis (ignoring current context)
|
|
3227
|
-
jupyter_db_path = self.paths.root / "Workshop" / "data" / "pipeline.sqlite"
|
|
3228
|
-
sentinel_path = self.paths.root / "Workshop" / "data" / ".onboarded"
|
|
3227
|
+
jupyter_db_path = self.paths.root / "Workshop" / "personal" / "Notebooks" / "data" / "pipeline.sqlite"
|
|
3228
|
+
sentinel_path = self.paths.root / "Workshop" / "personal" / "Notebooks" / "data" / ".onboarded"
|
|
3229
3229
|
|
|
3230
3230
|
# 2. Safety Check: Only proceed if the chrysalis exists
|
|
3231
3231
|
if not jupyter_db_path.exists():
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipulate
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.65
|
|
4
4
|
Summary: The context compiler that helps you prompt well — local-first, Nix-reproducible, replayable. The successor to AI SEO software.
|
|
5
5
|
Author-email: Mike Levin <pipulate@gmail.com>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pipulate"
|
|
7
7
|
# VERSION NOTE: Synced from pipulate/__init__.py.__version__ via python version_sync.py
|
|
8
|
-
version = "2.
|
|
8
|
+
version = "2.65"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Mike Levin", email="pipulate@gmail.com" },
|
|
11
11
|
]
|
|
@@ -2095,7 +2095,7 @@ async def home(request):
|
|
|
2095
2095
|
logger.debug(f'Selected explore item: {menux}')
|
|
2096
2096
|
|
|
2097
2097
|
# ---> THE NEW AIRLOCK TRIGGER <---
|
|
2098
|
-
sentinel_path = Path("Workshop/data/.onboarded")
|
|
2098
|
+
sentinel_path = Path("Workshop/personal/Notebooks/data/.onboarded")
|
|
2099
2099
|
# Add the database flag check here:
|
|
2100
2100
|
if sentinel_path.exists() and pipulate.db.get('airlock_sealed') != 'true':
|
|
2101
2101
|
logger.info("🚨 Sentinel detected and airlock unsealed. Triggering Airlock.")
|
|
@@ -3723,7 +3723,7 @@ async def reset_config_onboarding(request):
|
|
|
3723
3723
|
# 1. Wipe the File System Sentinel
|
|
3724
3724
|
try:
|
|
3725
3725
|
project_root = Path(os.getcwd())
|
|
3726
|
-
sentinel_path = project_root / "Workshop" / "data" / ".onboarded"
|
|
3726
|
+
sentinel_path = project_root / "Workshop" / "personal" / "Notebooks" / "data" / ".onboarded"
|
|
3727
3727
|
if sentinel_path.exists():
|
|
3728
3728
|
sentinel_path.unlink()
|
|
3729
3729
|
logger.info(f"🗑️ Deleted sentinel file: {sentinel_path}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|